Compare commits
201 Commits
d4a7eefc54
...
gopalss-Sm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcad3e6c50 | ||
|
|
2dfc37a475 | ||
|
|
9a6390cdee | ||
|
|
78c7c9011a | ||
|
|
1754669b07 | ||
|
|
1f7b4a49c0 | ||
|
|
15f47e2e35 | ||
|
|
12bf4165be | ||
|
|
f206a56362 | ||
|
|
13951d6459 | ||
|
|
5383e3400a | ||
|
|
ac3c36ffda | ||
|
|
3b681805aa | ||
|
|
ca49183639 | ||
|
|
b29d34f2e1 | ||
|
|
cb085d1fb6 | ||
|
|
b38e97b7ec | ||
|
|
b434c2497f | ||
|
|
94fc413466 | ||
|
|
1c6dbf9bd8 | ||
|
|
88ad575f62 | ||
|
|
5cd30b70e0 | ||
|
|
eb1f395101 | ||
|
|
27bdd547f0 | ||
|
|
75a4bd1d35 | ||
|
|
7c7335c907 | ||
|
|
3a9483ca6f | ||
|
|
1065d7cfb2 | ||
|
|
303043f9ec | ||
|
|
981f7c5f99 | ||
|
|
dcbc8d51aa | ||
|
|
785af4491d | ||
|
|
be46318a52 | ||
|
|
b5961cd7b1 | ||
|
|
76632f80b3 | ||
|
|
f59f93d7dc | ||
|
|
1dfd117ccd | ||
|
|
a86b78386b | ||
|
|
e3508c3ca3 | ||
|
|
8e7da82852 | ||
|
|
6cde18eb55 | ||
|
|
2409832b24 | ||
|
|
95fba2ab32 | ||
|
|
99518b6d3e | ||
|
|
d8f702c6f7 | ||
|
|
6eca09f2bf | ||
|
|
a1324f17fd | ||
|
|
56fa047ba9 | ||
|
|
0f4a955e01 | ||
|
|
fb80e04212 | ||
|
|
cdb5c2d375 | ||
|
|
44bff466e5 | ||
|
|
47989b591f | ||
|
|
41acdc95bc | ||
|
|
cc948e4c44 | ||
|
|
889b2187c1 | ||
|
|
3e146c376c | ||
|
|
e60d33385e | ||
|
|
744d2baa58 | ||
|
|
d5ca258817 | ||
|
|
b1ac3a6c7e | ||
|
|
69e56db124 | ||
|
|
13412b0898 | ||
|
|
9912bc5023 | ||
|
|
9d9e987c47 | ||
|
|
345560040b | ||
|
|
9134c1d395 | ||
|
|
cc9897316f | ||
|
|
e43580bd9a | ||
|
|
9613108cef | ||
|
|
4b29502c82 | ||
|
|
156b709556 | ||
|
|
00f51e3603 | ||
|
|
da5942d652 | ||
|
|
7c3206f919 | ||
|
|
4886eebbf4 | ||
|
|
91c727e204 | ||
|
|
26bdacedf6 | ||
|
|
7212ee47eb | ||
|
|
0fe1bb96c2 | ||
|
|
4bd75baaae | ||
|
|
bbcfe59c0f | ||
|
|
38c07d34c4 | ||
|
|
2019ace3b7 | ||
|
|
ad96ff0769 | ||
|
|
d7f83f29f0 | ||
|
|
98fad65028 | ||
|
|
e88016c63f | ||
|
|
edabf19461 | ||
|
|
8bed734a18 | ||
|
|
60ffb3ca9a | ||
|
|
6507e70236 | ||
|
|
2daec8be53 | ||
|
|
c2ff3f12fc | ||
|
|
a6a134a60e | ||
|
|
e0b39341fc | ||
|
|
947582b592 | ||
|
|
c207ff3561 | ||
|
|
41dd7c806e | ||
|
|
aa4dabe64f | ||
|
|
9868630d0e | ||
|
|
f2397573f3 | ||
|
|
7d5699118e | ||
|
|
0948ca0417 | ||
|
|
582eb2b818 | ||
|
|
c2474e27ab | ||
|
|
6c6390f034 | ||
|
|
4d6649cc6d | ||
|
|
d543b1447f | ||
|
|
d6eeb14216 | ||
|
|
aa670fe91e | ||
|
|
ebdbd8a231 | ||
|
|
fed4322d26 | ||
|
|
039d4a6c54 | ||
|
|
28ee1b42ce | ||
|
|
35889026d6 | ||
|
|
e81ac971b0 | ||
|
|
e05ce6aff0 | ||
|
|
e4598ba84a | ||
|
|
79513077f3 | ||
|
|
72acadca79 | ||
|
|
398e358826 | ||
|
|
7fc9b0fe97 | ||
|
|
71cc967f42 | ||
|
|
3ddad1e377 | ||
|
|
92805d3088 | ||
|
|
60acd05a03 | ||
|
|
4a18e241d9 | ||
|
|
1b88a1e3c7 | ||
|
|
b3feefafdf | ||
|
|
a7a16db908 | ||
|
|
9f92ba34a6 | ||
|
|
25cf039953 | ||
|
|
6bf145d7cf | ||
|
|
69240e18b4 | ||
|
|
5b9f431512 | ||
|
|
ad79762212 | ||
|
|
64149943cc | ||
|
|
714194d353 | ||
|
|
9d81c24934 | ||
|
|
25c194bf71 | ||
|
|
5a4c5ff668 | ||
|
|
f06e814d7c | ||
|
|
5eef2b0914 | ||
|
|
49988672f4 | ||
|
|
d192402800 | ||
|
|
646f1312f1 | ||
|
|
db62a6c1cc | ||
|
|
8e53519a27 | ||
|
|
d3d6932efd | ||
|
|
dea325cdcb | ||
|
|
2b50b31657 | ||
|
|
110b49a0c8 | ||
|
|
319746d3db | ||
|
|
2cb73fa85d | ||
|
|
5a832b470a | ||
|
|
42f04024d4 | ||
|
|
07852e206d | ||
|
|
156b47760d | ||
|
|
673501749f | ||
|
|
b56fc1a0cc | ||
|
|
2ed3222bbe | ||
|
|
c7da23795d | ||
|
|
8ca2bc0de7 | ||
|
|
0a85945767 | ||
|
|
51d13700d8 | ||
|
|
1983e889dc | ||
|
|
dfe9d7bcae | ||
|
|
4dee4cc673 | ||
|
|
a7ea651bef | ||
|
|
4c2afb8814 | ||
|
|
951833812a | ||
|
|
e456cc949d | ||
|
|
75e64cd47a | ||
|
|
3736a5791f | ||
|
|
0b0bf60137 | ||
|
|
d496d8eab6 | ||
|
|
22883def8d | ||
|
|
5826f1e149 | ||
|
|
ef6560298e | ||
|
|
e3a56816d6 | ||
|
|
5ef400882a | ||
|
|
333bb55d84 | ||
|
|
7d6318c3ad | ||
|
|
d5c722125a | ||
|
|
16ed0ac129 | ||
|
|
b69d2a2c55 | ||
|
|
e210379335 | ||
|
|
659976b8f3 | ||
|
|
96ba498d70 | ||
|
|
7ecb0b2f37 | ||
|
|
a14bbdb065 | ||
|
|
bfedfbb1fb | ||
|
|
41f46c7fd7 | ||
|
|
a62dd278e2 | ||
|
|
a20e4ede85 | ||
|
|
1ab0d147cb | ||
|
|
9aee576b99 | ||
|
|
0dcc72239d | ||
|
|
33d5e9aa07 | ||
|
|
e528741a8b |
135
CMakeLists.txt
135
CMakeLists.txt
@@ -2,60 +2,145 @@
|
||||
#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 2.8.6)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
set(TINYOBJLOADER_SOVERSION 1)
|
||||
set(TINYOBJLOADER_VERSION 1.0.4)
|
||||
|
||||
#optional double precision support
|
||||
option(TINYOBJLOADER_USE_DOUBLE "Build library with double precision instead of single (float)" OFF)
|
||||
|
||||
if(TINYOBJLOADER_USE_DOUBLE)
|
||||
add_definitions(-DTINYOBJLOADER_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
|
||||
)
|
||||
${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
|
||||
)
|
||||
${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
|
||||
)
|
||||
${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)
|
||||
option(TINYOBJLOADER_COMPILATION_SHARED "Build as shared library" OFF)
|
||||
|
||||
if (TINYOBJLOADER_COMPILATION_SHARED)
|
||||
add_library(tinyobjloader SHARED ${tinyobjloader-Source})
|
||||
if(TINYOBJLOADER_COMPILATION_SHARED)
|
||||
add_library(${LIBRARY_NAME} SHARED ${tinyobjloader-Source})
|
||||
set_target_properties(${LIBRARY_NAME} PROPERTIES
|
||||
SOVERSION ${TINYOBJLOADER_SOVERSION}
|
||||
)
|
||||
else()
|
||||
add_library(tinyobjloader STATIC ${tinyobjloader-Source})
|
||||
add_library(${LIBRARY_NAME} STATIC ${tinyobjloader-Source})
|
||||
endif()
|
||||
|
||||
set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${TINYOBJLOADER_VERSION})
|
||||
|
||||
target_include_directories(${LIBRARY_NAME} INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:${TINYOBJLOADER_INCLUDE_DIR}>
|
||||
)
|
||||
|
||||
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 tinyobjloader)
|
||||
target_link_libraries(test_loader ${LIBRARY_NAME})
|
||||
endif()
|
||||
|
||||
option(TINYOBJLOADER_BUILD_OBJ_STICHER "Build OBJ Sticher Application" OFF)
|
||||
if (TINYOBJLOADER_BUILD_OBJ_STICHER)
|
||||
if(TINYOBJLOADER_BUILD_OBJ_STICHER)
|
||||
add_executable(obj_sticher ${tinyobjloader-examples-objsticher})
|
||||
target_link_libraries(obj_sticher tinyobjloader)
|
||||
target_link_libraries(obj_sticher ${LIBRARY_NAME})
|
||||
|
||||
install ( TARGETS
|
||||
install(TARGETS
|
||||
obj_sticher
|
||||
DESTINATION
|
||||
bin
|
||||
${TINYOBJLOADER_RUNTIME_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
#Installation
|
||||
install ( TARGETS
|
||||
tinyobjloader
|
||||
DESTINATION
|
||||
lib
|
||||
#Write CMake package config files
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
configure_package_config_file(
|
||||
${PROJECT_NAME}-config.cmake.in
|
||||
${LIBRARY_NAME}-config.cmake
|
||||
INSTALL_DESTINATION
|
||||
${TINYOBJLOADER_CMAKE_DIR}
|
||||
PATH_VARS
|
||||
TINYOBJLOADER_INCLUDE_DIR
|
||||
TINYOBJLOADER_LIBRARY_DIR
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
install ( FILES
|
||||
|
||||
write_basic_package_version_file(${LIBRARY_NAME}-config-version.cmake
|
||||
VERSION
|
||||
${TINYOBJLOADER_VERSION}
|
||||
COMPATIBILITY
|
||||
SameMajorVersion
|
||||
)
|
||||
|
||||
#pkg-config file
|
||||
configure_file(${PROJECT_NAME}.pc.in ${LIBRARY_NAME}.pc @ONLY)
|
||||
|
||||
#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
|
||||
DESTINATION
|
||||
${TINYOBJLOADER_CMAKE_DIR}
|
||||
)
|
||||
install(FILES
|
||||
tiny_obj_loader.h
|
||||
DESTINATION
|
||||
include
|
||||
${TINYOBJLOADER_INCLUDE_DIR}
|
||||
)
|
||||
install(FILES
|
||||
LICENSE
|
||||
DESTINATION
|
||||
${TINYOBJLOADER_DOC_DIR}
|
||||
)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}-config-version.cmake"
|
||||
DESTINATION
|
||||
${TINYOBJLOADER_CMAKE_DIR}
|
||||
)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc"
|
||||
DESTINATION
|
||||
${TINYOBJLOADER_PKGCONFIG_DIR}
|
||||
)
|
||||
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2016 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.
|
||||
202
README.md
202
README.md
@@ -1,5 +1,4 @@
|
||||
tinyobjloader
|
||||
=============
|
||||
# tinyobjloader
|
||||
|
||||
[](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
@@ -11,49 +10,58 @@ tinyobjloader
|
||||
|
||||
[](https://coveralls.io/github/syoyo/tinyobjloader?branch=master)
|
||||
|
||||
http://syoyo.github.io/tinyobjloader/
|
||||
[https://github.com/syoyo/tinyobjloader](https://github.com/syoyo/tinyobjloader)
|
||||
|
||||
Tiny but powerful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time.
|
||||
Tiny but powerful single file wavefront obj loader written in C++. 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 .
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
* XX YY, ZZZZ : New data strcutre and API!
|
||||
* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert!
|
||||
* Nov 26, 2015 : Now single-header only!.
|
||||
* Nov 08, 2015 : Improved API.
|
||||
* Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors!
|
||||
* Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel!
|
||||
* Feb 06, 2015 : Fix parsing multi-material object
|
||||
* Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo!
|
||||
* Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran!
|
||||
* Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse!
|
||||
* Apr 21, 2014 : Define default material if no material definition exists in .obj. Thanks YarmUI!
|
||||
* Apr 10, 2014 : Add support for parsing 'illum' and 'd'/'Tr' statements. Thanks mmp!
|
||||
* Jan 27, 2014 : Added CMake project. Thanks bradc6!
|
||||
* Nov 26, 2013 : Performance optimization by NeuralSandwich. 9% improvement in his project, thanks!
|
||||
* Sep 12, 2013 : Added multiple .obj sticher example.
|
||||
|
||||
Example
|
||||
Notice!
|
||||
-------
|
||||
|
||||
We have released new version v1.0.0 on 20 Aug, 2016.
|
||||
Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x
|
||||
|
||||
## What's new
|
||||
|
||||
* 20 Aug, 2016 : Bump version v1.0.0. New data structure and API!
|
||||
|
||||
### Old version
|
||||
|
||||
Previous old version is avaiable in `v0.9.x` branch.
|
||||
|
||||
## Example
|
||||
|
||||

|
||||
|
||||
tinyobjloader can successfully load 6M triangles Rungholt scene.
|
||||
http://graphics.cs.williams.edu/data/meshes.xml
|
||||
http://casual-effects.com/data/index.html
|
||||
|
||||

|
||||
|
||||
* [examples/viewer/](examples/viewer) OpenGL .obj viewer
|
||||
* [examples/callback_api/](examples/callback_api/) Callback API example
|
||||
* [examples/voxelize/](examples/voxelize/) Voxelizer example
|
||||
|
||||
Use case
|
||||
--------
|
||||
## 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
|
||||
* 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
|
||||
@@ -65,40 +73,130 @@ TinyObjLoader is successfully used in ...
|
||||
* pbrt-v3 https://github.com/mmp/pbrt-v3
|
||||
* cocos2d-x https://github.com/cocos2d/cocos2d-x/
|
||||
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan
|
||||
* Your project here!
|
||||
* 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
|
||||
--------
|
||||
|
||||
## 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).
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
## TODO
|
||||
|
||||
* [ ] Fix Python binding.
|
||||
* [ ] Fix obj_sticker example.
|
||||
* [ ] More unit test codes.
|
||||
* [x] Texture options
|
||||
* [ ] Normal vector generation
|
||||
* [ ] Support smoothing groups
|
||||
|
||||
License
|
||||
-------
|
||||
## License
|
||||
|
||||
Licensed under MIT license.
|
||||
|
||||
Usage
|
||||
-----
|
||||
## Usage
|
||||
|
||||
### Data format
|
||||
|
||||
`attrib_t` contains single and linear array of vertex data(position, normal and texcoord).
|
||||
Each `shape_t` does not contain vertex data but contains array index to `attrib_t`.
|
||||
|
||||
```
|
||||
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` flas 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.
|
||||
|
||||
#### Example code
|
||||
|
||||
```c++
|
||||
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
|
||||
#include "tiny_obj_loader.h"
|
||||
@@ -130,14 +228,18 @@ for (size_t s = 0; s < shapes.size(); s++) {
|
||||
for (size_t v = 0; v < fv; v++) {
|
||||
// access to vertex
|
||||
tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v];
|
||||
float vx = attrib.vertices[3*idx.vertex_index+0];
|
||||
float vy = attrib.vertices[3*idx.vertex_index+1];
|
||||
float vz = attrib.vertices[3*idx.vertex_index+2];
|
||||
float nx = attrib.normals[3*idx.normal_index+0];
|
||||
float ny = attrib.normals[3*idx.normal_index+1];
|
||||
float nz = attrib.normals[3*idx.normal_index+2];
|
||||
float tx = attrib.texcoords[2*idx.texcoord_index+0];
|
||||
float ty = attrib.texcoords[2*idx.texcoord_index+1];
|
||||
tinyobj::real_t vx = attrib.vertices[3*idx.vertex_index+0];
|
||||
tinyobj::real_t vy = attrib.vertices[3*idx.vertex_index+1];
|
||||
tinyobj::real_t vz = attrib.vertices[3*idx.vertex_index+2];
|
||||
tinyobj::real_t nx = attrib.normals[3*idx.normal_index+0];
|
||||
tinyobj::real_t ny = attrib.normals[3*idx.normal_index+1];
|
||||
tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2];
|
||||
tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0];
|
||||
tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1];
|
||||
// Optional: vertex colors
|
||||
// tinyobj::real_t red = attrib.colors[3*idx.vertex_index+0];
|
||||
// tinyobj::real_t green = attrib.colors[3*idx.vertex_index+1];
|
||||
// tinyobj::real_t blue = attrib.colors[3*idx.vertex_index+2];
|
||||
}
|
||||
index_offset += fv;
|
||||
|
||||
@@ -148,8 +250,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
|
||||
|
||||
```
|
||||
|
||||
Optimized loader
|
||||
----------------
|
||||
## 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.
|
||||
@@ -160,10 +261,9 @@ Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core
|
||||
* 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 basedline)
|
||||
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than baseline)
|
||||
|
||||
|
||||
Tests
|
||||
-----
|
||||
## Tests
|
||||
|
||||
Unit tests are provided in `tests` directory. See `tests/README.md` for details.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 0.9.{build}
|
||||
version: 1.0.{build}
|
||||
|
||||
platform: x64
|
||||
|
||||
@@ -7,7 +7,6 @@ install:
|
||||
# All external dependencies are installed in C:\projects\deps
|
||||
#######################################################################################
|
||||
- mkdir C:\projects\deps
|
||||
- cd C:\projects\deps
|
||||
|
||||
#######################################################################################
|
||||
# Install Ninja
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
//
|
||||
// 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 <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
std::vector<float> vertices;
|
||||
std::vector<float> normals;
|
||||
std::vector<float> texcoords;
|
||||
std::vector<int> v_indices;
|
||||
std::vector<int> vn_indices;
|
||||
std::vector<int> vt_indices;
|
||||
std::vector<int> v_indices;
|
||||
std::vector<int> vn_indices;
|
||||
std::vector<int> vt_indices;
|
||||
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
} MyMesh;
|
||||
|
||||
void vertex_cb(void *user_data, float x, float y, float z)
|
||||
{
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z);
|
||||
void vertex_cb(void *user_data, float x, float y, float z, float w) {
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(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<MyMesh*>(user_data);
|
||||
void normal_cb(void *user_data, float x, float y, float z) {
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
|
||||
|
||||
mesh->normals.push_back(x);
|
||||
@@ -41,49 +46,56 @@ void normal_cb(void *user_data, float x, float y, float z)
|
||||
mesh->normals.push_back(z);
|
||||
}
|
||||
|
||||
void texcoord_cb(void *user_data, float x, float y)
|
||||
{
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y);
|
||||
void texcoord_cb(void *user_data, float x, float y, float z) {
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(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, int v_idx, int vn_idx, int vt_idx)
|
||||
{
|
||||
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(relative index).
|
||||
// (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, -2147483648(0x80000000) is set for the index value which does not exist in .obj
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx);
|
||||
// Also, 0 is set for the index value which
|
||||
// does not exist in .obj
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||
|
||||
if (v_idx != 0x80000000) {
|
||||
mesh->v_indices.push_back(v_idx);
|
||||
}
|
||||
if (vn_idx != 0x80000000) {
|
||||
mesh->vn_indices.push_back(vn_idx);
|
||||
}
|
||||
if (vt_idx != 0x80000000) {
|
||||
mesh->vt_indices.push_back(vt_idx);
|
||||
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<MyMesh*>(user_data);
|
||||
void usemtl_cb(void *user_data, const char *name, int material_idx) {
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(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());
|
||||
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<MyMesh*>(user_data);
|
||||
void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
|
||||
int num_materials) {
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||
printf("mtllib. # of materials = %d\n", num_materials);
|
||||
|
||||
for (int i = 0; i < num_materials; i++) {
|
||||
@@ -91,9 +103,8 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_ma
|
||||
}
|
||||
}
|
||||
|
||||
void group_cb(void *user_data, const char **names, int num_names)
|
||||
{
|
||||
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
void group_cb(void *user_data, const char **names, int num_names) {
|
||||
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("group : name = \n");
|
||||
|
||||
for (int i = 0; i < num_names; i++) {
|
||||
@@ -101,16 +112,12 @@ void group_cb(void *user_data, const char **names, int num_names)
|
||||
}
|
||||
}
|
||||
|
||||
void object_cb(void *user_data, const char *name)
|
||||
{
|
||||
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
void object_cb(void *user_data, const char *name) {
|
||||
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("object : name = %s\n", name);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
tinyobj::callback_t cb;
|
||||
cb.vertex_cb = vertex_cb;
|
||||
cb.normal_cb = normal_cb;
|
||||
@@ -123,7 +130,11 @@ main(int argc, char** argv)
|
||||
|
||||
MyMesh mesh;
|
||||
std::string err;
|
||||
std::ifstream ifs("../../models/cornell_box.obj");
|
||||
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;
|
||||
@@ -132,7 +143,7 @@ main(int argc, char** argv)
|
||||
|
||||
tinyobj::MaterialFileReader mtlReader("../../models/");
|
||||
|
||||
bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader);
|
||||
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
|
||||
@@ -172,5 +172,3 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* glfw3
|
||||
* glew
|
||||
|
||||
|
||||
## Build on MaCOSX
|
||||
|
||||
Install glfw3 and glew using brew.
|
||||
@@ -35,3 +34,9 @@ Put glfw3 and glew library somewhere and replace include and lib path in `premak
|
||||
Then,
|
||||
|
||||
> premake5.exe vs2013
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] Support per-face material.
|
||||
* [ ] Use shader-based GL rendering.
|
||||
* [ ] PBR shader support.
|
||||
|
||||
6755
examples/viewer/stb_image.h
Normal file
6755
examples/viewer/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,16 @@
|
||||
//
|
||||
// Simple .obj viewer(vertex only)
|
||||
//
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
@@ -26,11 +27,23 @@
|
||||
|
||||
#include "trackball.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <mmsystem.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -45,7 +58,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
class timerutil {
|
||||
public:
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
typedef DWORD time_t;
|
||||
|
||||
@@ -81,7 +94,7 @@ public:
|
||||
return (time_t)(t.tv_sec * 1000 + t.tv_usec);
|
||||
}
|
||||
|
||||
#else // C timer
|
||||
#else // C timer
|
||||
// using namespace std;
|
||||
typedef clock_t time_t;
|
||||
|
||||
@@ -96,7 +109,7 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
DWORD t_[2];
|
||||
#else
|
||||
@@ -110,8 +123,9 @@ private:
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
GLuint vb; // vertex buffer
|
||||
GLuint vb_id; // vertex buffer id
|
||||
int numTriangles;
|
||||
size_t material_id;
|
||||
} DrawObject;
|
||||
|
||||
std::vector<DrawObject> gDrawObjects;
|
||||
@@ -129,7 +143,26 @@ float eye[3], lookat[3], up[3];
|
||||
|
||||
GLFWwindow* window;
|
||||
|
||||
void CheckErrors(std::string desc) {
|
||||
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);
|
||||
@@ -137,7 +170,7 @@ void CheckErrors(std::string desc) {
|
||||
}
|
||||
}
|
||||
|
||||
void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
|
||||
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];
|
||||
@@ -157,22 +190,116 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
|
||||
float len = sqrtf(len2);
|
||||
|
||||
N[0] /= len;
|
||||
N[1] /= len;
|
||||
N[1] /= len;
|
||||
N[2] /= len;
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename)
|
||||
namespace // Local utility functions
|
||||
{
|
||||
void addBtoA(float a[3], float b[3])
|
||||
{
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
a[i] += b[i];
|
||||
}
|
||||
|
||||
void assignBtoA(float a[3], float b[3])
|
||||
{
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
a[i] = b[i];
|
||||
}
|
||||
|
||||
void normalizeVector(float N[3])
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void computeSmoothingNormals(tinyobj::attrib_t &attrib, tinyobj::shape_t &shape,
|
||||
std::map<int, float[3]>& smoothVertexNormals)
|
||||
{
|
||||
smoothVertexNormals.clear();
|
||||
std::map<int, float[3]>::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())
|
||||
addBtoA(iter->second, normal);
|
||||
else
|
||||
assignBtoA(smoothVertexNormals[vi[i]], normal);
|
||||
}
|
||||
|
||||
} // 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<DrawObject>* drawObjects,
|
||||
std::vector<tinyobj::material_t>& materials,
|
||||
std::map<std::string, GLuint>& textures,
|
||||
const char* filename) {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
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 err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
|
||||
base_dir.c_str());
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
@@ -184,7 +311,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Parsing time: %d [ms]\n", tm.msec());
|
||||
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);
|
||||
@@ -192,95 +319,267 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
||||
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<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
{
|
||||
for (size_t s = 0; s < shapes.size(); s++) {
|
||||
DrawObject o;
|
||||
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
||||
for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) {
|
||||
for (size_t s = 0; s < shapes.size(); s++) {
|
||||
DrawObject o;
|
||||
std::vector<float> buffer; // pos(3float), normal(3float), color(3float)
|
||||
|
||||
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];
|
||||
// Check for smoothing group and compute smoothing normals
|
||||
std::map<int, float[3]> smoothVertexNormals;
|
||||
if (shapes[s].smoothingGroupId > 0)
|
||||
computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals);
|
||||
|
||||
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);
|
||||
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];
|
||||
|
||||
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]);
|
||||
int current_material_id = shapes[s].mesh.material_ids[f];
|
||||
|
||||
if ((current_material_id < 0) ||
|
||||
(current_material_id >= static_cast<int>(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 n[3][3];
|
||||
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 f0 = idx0.normal_index;
|
||||
int f1 = idx1.normal_index;
|
||||
int f2 = idx2.normal_index;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
for (int k = 0; k < 3; k++) {
|
||||
n[0][k] = attrib.normals[3*f0+k];
|
||||
n[1][k] = attrib.normals[3*f1+k];
|
||||
n[2][k] = attrib.normals[3*f2+k];
|
||||
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) {
|
||||
assignBtoA(n[0], smoothVertexNormals[f0]);
|
||||
assignBtoA(n[1], smoothVertexNormals[f1]);
|
||||
assignBtoA(n[2], smoothVertexNormals[f2]);
|
||||
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];
|
||||
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 > 0.0f) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles);
|
||||
}
|
||||
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);
|
||||
|
||||
gDrawObjects.push_back(o);
|
||||
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<int>(s),
|
||||
o.numTriangles);
|
||||
}
|
||||
|
||||
drawObjects->push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,8 +589,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
||||
return true;
|
||||
}
|
||||
|
||||
void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||
{
|
||||
static void reshapeFunc(GLFWwindow* window, int w, int h) {
|
||||
int fb_w, fb_h;
|
||||
// Get actual framebuffer size.
|
||||
glfwGetFramebufferSize(window, &fb_w, &fb_h);
|
||||
@@ -307,100 +605,125 @@ void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||
height = h;
|
||||
}
|
||||
|
||||
void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int 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);
|
||||
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;
|
||||
}
|
||||
// init_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void clickFunc(GLFWwindow* window, int button, int action, int 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;
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
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){
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
// Update mouse point
|
||||
prevMouseX = mouse_x;
|
||||
prevMouseY = mouse_y;
|
||||
}
|
||||
|
||||
void Draw(const std::vector<DrawObject>& drawObjects)
|
||||
{
|
||||
static void Draw(const std::vector<DrawObject>& drawObjects,
|
||||
std::vector<tinyobj::material_t>& materials,
|
||||
std::map<std::string, GLuint>& textures) {
|
||||
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);
|
||||
GLsizei stride = (3 + 3 + 3 + 2) * sizeof(float);
|
||||
for (size_t i = 0; i < drawObjects.size(); i++) {
|
||||
DrawObject o = drawObjects[i];
|
||||
if (o.vb < 1) {
|
||||
if (o.vb_id < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
|
||||
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));
|
||||
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
|
||||
@@ -411,16 +734,19 @@ void Draw(const std::vector<DrawObject>& drawObjects)
|
||||
glColor3f(0.0f, 0.0f, 0.4f);
|
||||
for (size_t i = 0; i < drawObjects.size(); i++) {
|
||||
DrawObject o = drawObjects[i];
|
||||
if (o.vb < 1) {
|
||||
if (o.vb_id < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
|
||||
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));
|
||||
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");
|
||||
@@ -443,9 +769,7 @@ static void Init() {
|
||||
up[2] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Needs input.obj\n" << std::endl;
|
||||
return 0;
|
||||
@@ -453,16 +777,13 @@ int main(int argc, char **argv)
|
||||
|
||||
Init();
|
||||
|
||||
|
||||
if(!glfwInit()){
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
|
||||
if(window == NULL){
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to open GLFW window. " << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
@@ -486,7 +807,10 @@ int main(int argc, char **argv)
|
||||
reshapeFunc(window, width, height);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) {
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
std::map<std::string, GLuint> textures;
|
||||
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures,
|
||||
argv[1])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -498,18 +822,20 @@ int main(int argc, char **argv)
|
||||
maxExtent = 0.5f * (bmax[2] - bmin[2]);
|
||||
}
|
||||
|
||||
while(glfwWindowShouldClose(window) == GL_FALSE) {
|
||||
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]);
|
||||
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]);
|
||||
|
||||
@@ -517,9 +843,10 @@ int main(int argc, char **argv)
|
||||
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]));
|
||||
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
|
||||
-0.5 * (bmax[2] + bmin[2]));
|
||||
|
||||
Draw(gDrawObjects);
|
||||
Draw(gDrawObjects, materials, textures);
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
2
examples/voxelize/Makefile
Normal file
2
examples/voxelize/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
all:
|
||||
g++ -o voxelizer main.cc
|
||||
5
examples/voxelize/README.md
Normal file
5
examples/voxelize/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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.
|
||||
74
examples/voxelize/main.cc
Normal file
74
examples/voxelize/main.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
#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<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &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;
|
||||
}
|
||||
|
||||
764
examples/voxelize/voxelizer.h
Normal file
764
examples/voxelize/voxelizer.h
Normal file
@@ -0,0 +1,764 @@
|
||||
//
|
||||
// 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 <stdlib.h> // 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 <math.h> // ceil, fabs & al.
|
||||
#include <stdbool.h> // hughh
|
||||
#include <string.h> // 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
|
||||
@@ -1,26 +0,0 @@
|
||||
Copyright (c) 2013, Alexander Tretyak
|
||||
Copyright (c) 2015, r-lyeh
|
||||
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 the author 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
|
||||
HOLDER 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.
|
||||
16
experimental/README.md
Normal file
16
experimental/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Experimental code for .obj loader.
|
||||
|
||||
* Multi-threaded optimized parser : tinyobj_loader_opt.h
|
||||
|
||||
## Requirements
|
||||
|
||||
* C++-11 compiler
|
||||
|
||||
## Compile options
|
||||
|
||||
* zstd compressed .obj support. `--with-zstd` premake option.
|
||||
* gzip compressed .obj support. `--with-zlib` premake option.
|
||||
|
||||
## Licenses
|
||||
|
||||
* lfpAlloc : MIT license.
|
||||
89
experimental/lfpAlloc/Allocator.hpp
Normal file
89
experimental/lfpAlloc/Allocator.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef LF_POOL_ALLOCATOR
|
||||
#define LF_POOL_ALLOCATOR
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <lfpAlloc/PoolDispatcher.hpp>
|
||||
|
||||
namespace lfpAlloc {
|
||||
template <typename T, std::size_t NumPools = 70>
|
||||
class lfpAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = T const&;
|
||||
|
||||
template <typename U>
|
||||
struct rebind {
|
||||
typedef lfpAllocator<U, NumPools> other;
|
||||
};
|
||||
|
||||
lfpAllocator() {}
|
||||
|
||||
template <typename U>
|
||||
lfpAllocator(lfpAllocator<U, NumPools>&&) noexcept {}
|
||||
|
||||
template <typename U>
|
||||
lfpAllocator(const lfpAllocator<U, NumPools>&) noexcept {}
|
||||
|
||||
T* allocate(std::size_t count) {
|
||||
if (sizeof(T) * count <=
|
||||
alignof(std::max_align_t) * NumPools - sizeof(void*)) {
|
||||
return reinterpret_cast<T*>(
|
||||
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 <typename U>
|
||||
void destroy(U* p) {
|
||||
p->~U();
|
||||
}
|
||||
|
||||
template <typename U, typename... Args>
|
||||
void construct(U* p, Args&&... args) {
|
||||
new (p) U(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Ty, typename U, std::size_t N, std::size_t M>
|
||||
friend bool operator==(const lfpAllocator<Ty, N>&,
|
||||
const lfpAllocator<U, M>&) noexcept;
|
||||
|
||||
template <typename U, std::size_t M>
|
||||
friend class lfpAllocator;
|
||||
|
||||
private:
|
||||
static PoolDispatcher<NumPools> dispatcher_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
PoolDispatcher<N> lfpAllocator<T, N>::dispatcher_;
|
||||
|
||||
template <typename T, typename U, std::size_t N, std::size_t M>
|
||||
inline bool operator==(const lfpAllocator<T, N>&,
|
||||
const lfpAllocator<U, M>&) noexcept {
|
||||
return N == M;
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N, std::size_t M>
|
||||
inline bool operator!=(const lfpAllocator<T, N>& left,
|
||||
const lfpAllocator<U, M>& right) noexcept {
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
116
experimental/lfpAlloc/ChunkList.hpp
Normal file
116
experimental/lfpAlloc/ChunkList.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef LF_POOL_ALLOC_CHUNK_LIST
|
||||
#define LF_POOL_ALLOC_CHUNK_LIST
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef LFP_ALLOW_BLOCKING
|
||||
static_assert(ATOMIC_POINTER_LOCK_FREE == 2,
|
||||
"Atomic pointer is not lock-free.");
|
||||
#endif
|
||||
|
||||
namespace lfpAlloc {
|
||||
|
||||
template <std::size_t Size>
|
||||
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 <std::size_t Size, std::size_t AllocationsPerChunk>
|
||||
struct Chunk {
|
||||
Chunk() noexcept {
|
||||
auto& last = memBlock_[AllocationsPerChunk - 1];
|
||||
last.next_ = nullptr;
|
||||
}
|
||||
Cell<Size> memBlock_[AllocationsPerChunk];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Node {
|
||||
Node() : val_(), next_(nullptr) {}
|
||||
Node(const T& val) : val_(val), next_(nullptr) {}
|
||||
T val_;
|
||||
std::atomic<Node<T>*> next_;
|
||||
};
|
||||
|
||||
template <std::size_t Size, std::size_t AllocationsPerChunk>
|
||||
class ChunkList {
|
||||
static constexpr auto CellSize =
|
||||
(Size > sizeof(void*)) ? Size - sizeof(void*) : 0;
|
||||
using Chunk_t = Chunk<CellSize, AllocationsPerChunk>;
|
||||
using Cell_t = Cell<CellSize>;
|
||||
|
||||
using ChunkNode = Node<Chunk_t>;
|
||||
using CellNode = Node<Cell_t*>;
|
||||
|
||||
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<ChunkNode*> handle_;
|
||||
std::atomic<CellNode*> head_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
21
experimental/lfpAlloc/LICENSE
Normal file
21
experimental/lfpAlloc/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
||||
48
experimental/lfpAlloc/Pool.hpp
Normal file
48
experimental/lfpAlloc/Pool.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef LF_POOL_ALLOC_POOL
|
||||
#define LF_POOL_ALLOC_POOL
|
||||
|
||||
#include <lfpAlloc/Utils.hpp>
|
||||
#include <lfpAlloc/ChunkList.hpp>
|
||||
|
||||
namespace lfpAlloc {
|
||||
template <std::size_t Size, std::size_t AllocationsPerChunk>
|
||||
class Pool {
|
||||
using ChunkList_t = ChunkList<Size, AllocationsPerChunk>;
|
||||
|
||||
public:
|
||||
static constexpr auto CellSize =
|
||||
(Size > sizeof(void*)) ? Size - sizeof(void*) : 0;
|
||||
using Cell_t = Cell<CellSize>;
|
||||
|
||||
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<Cell_t*>(p);
|
||||
Cell_t* currentHead = head_;
|
||||
newHead->next_ = currentHead;
|
||||
head_ = newHead;
|
||||
}
|
||||
|
||||
private:
|
||||
Cell_t* head_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
79
experimental/lfpAlloc/PoolDispatcher.hpp
Normal file
79
experimental/lfpAlloc/PoolDispatcher.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef LF_POOL_DISPATCHER
|
||||
#define LF_POOL_DISPATCHER
|
||||
|
||||
#include <tuple>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <lfpAlloc/Pool.hpp>
|
||||
|
||||
#ifndef LFP_ALLOCATIONS_PER_CHUNK
|
||||
#define LFP_ALLOCATIONS_PER_CHUNK 64 * 100
|
||||
#endif
|
||||
|
||||
namespace lfpAlloc {
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t Num, uint16_t... Ts>
|
||||
struct Pools : Pools<Num - 1, alignof(std::max_align_t) * Num, Ts...> {};
|
||||
|
||||
template <uint16_t... Size>
|
||||
struct Pools<0, Size...> {
|
||||
using type = std::tuple<Pool<Size, LFP_ALLOCATIONS_PER_CHUNK>...>;
|
||||
};
|
||||
}
|
||||
|
||||
template <std::size_t NumPools>
|
||||
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<NumPools>::type pools_;
|
||||
static_assert(NumPools > 0, "Invalid number of pools");
|
||||
|
||||
template <std::size_t Index>
|
||||
typename std::enable_if <
|
||||
Index<NumPools, void*>::type
|
||||
dispatchAllocate(std::size_t const& requestSize) {
|
||||
if (requestSize <= std::get<Index>(pools_).CellSize) {
|
||||
return std::get<Index>(pools_).allocate();
|
||||
} else {
|
||||
return dispatchAllocate<Index + 1>(requestSize);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t Index>
|
||||
typename std::enable_if<!(Index < NumPools), void*>::type
|
||||
dispatchAllocate(std::size_t const&) {
|
||||
assert(false && "Invalid allocation size.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <std::size_t Index>
|
||||
typename std::enable_if <
|
||||
Index<NumPools>::type
|
||||
dispatchDeallocate(void* p, std::size_t const& requestSize) noexcept {
|
||||
if (requestSize <= std::get<Index>(pools_).CellSize) {
|
||||
std::get<Index>(pools_).deallocate(p);
|
||||
} else {
|
||||
dispatchDeallocate<Index + 1>(p, requestSize);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t Index>
|
||||
typename std::enable_if<!(Index < NumPools)>::type
|
||||
dispatchDeallocate(void*, std::size_t const&) noexcept {
|
||||
assert(false && "Invalid deallocation size.");
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t NumPools>
|
||||
thread_local typename detail::Pools<NumPools>::type
|
||||
PoolDispatcher<NumPools>::pools_;
|
||||
}
|
||||
|
||||
#endif
|
||||
20
experimental/lfpAlloc/Utils.hpp
Normal file
20
experimental/lfpAlloc/Utils.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace lfpAlloc {
|
||||
namespace detail {
|
||||
template <std::size_t Val, std::size_t base = 2>
|
||||
struct Log {
|
||||
enum { value = 1 + Log<Val / base, base>::value };
|
||||
};
|
||||
|
||||
template <std::size_t base>
|
||||
struct Log<1, base> {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template <std::size_t base>
|
||||
struct Log<0, base> {
|
||||
enum { value = 0 };
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,973 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, Alexander Tretyak
|
||||
Copyright (c) 2015, r-lyeh
|
||||
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 the author 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
|
||||
HOLDER 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.
|
||||
|
||||
|
||||
Note: The latest version of this memory allocator obtainable at
|
||||
http://ltalloc.googlecode.com/hg/ltalloc.cc
|
||||
|
||||
Project URL: http://code.google.com/p/ltalloc
|
||||
*/
|
||||
|
||||
#include "ltalloc.h"
|
||||
|
||||
#define LTALLOC_VERSION "2.0.0" /* (2015/06/16) - ltcalloc(), ltmsize(), ltrealloc(), ltmemalign(), LTALLOC_AUTO_GC_INTERVAL
|
||||
#define LTALLOC_VERSION "1.0.0" /* (2015/06/16) - standard STL allocator provided [see ltalloc.hpp file](ltalloc.hpp)
|
||||
#define LTALLOC_VERSION "0.0.0" /* (2013/xx/xx) - fork from public repository */
|
||||
|
||||
//Customizable constants
|
||||
//#define LTALLOC_DISABLE_OPERATOR_NEW_OVERRIDE
|
||||
//#define LTALLOC_AUTO_GC_INTERVAL 3.0
|
||||
#ifndef LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO
|
||||
#define LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO 2//determines how accurately size classes are spaced (i.e. when = 0, allocation requests are rounded up to the nearest power of two (2^n), when = 1, rounded to 2^n, or (2^n)*1.5, when = 2, rounded to 2^n, (2^n)*1.25, (2^n)*1.5, or (2^n)*1.75, and so on); this parameter have direct influence on memory fragmentation - bigger values lead to reducing internal fragmentation (which can be approximately estimated as pow(0.5, VALUE)*100%), but at the same time increasing external fragmentation
|
||||
#endif
|
||||
#define CHUNK_SIZE (64*1024)//size of chunk (basic allocation unit for all allocations of size <= MAX_BLOCK_SIZE); must be a power of two (as well as all following parameters), also should not be less than allocation granularity on Windows (which is always 64K by design of NT kernel)
|
||||
#define CACHE_LINE_SIZE 64
|
||||
#define MAX_NUM_OF_BLOCKS_IN_BATCH 256//maximum number of blocks to move between a thread cache and a central cache in one shot
|
||||
static const unsigned int MAX_BATCH_SIZE = 64*1024;//maximum total size of blocks to move between a thread cache and a central cache in one shot (corresponds to half size of thread cache of each size class)
|
||||
static const unsigned int MAX_BLOCK_SIZE = CHUNK_SIZE;//requesting memory of any size greater than this value will lead to direct call of system virtual memory allocation routine
|
||||
|
||||
//Platform-specific stuff
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define CPPCODE(code) code
|
||||
#include <new>
|
||||
#else
|
||||
#define CPPCODE(code)
|
||||
#endif
|
||||
|
||||
#ifdef LTALLOC_AUTO_GC_INTERVAL
|
||||
#include <time.h>
|
||||
# if LTALLOC_AUTO_GC_INTERVAL <= 0
|
||||
# undef LTALLOC_AUTO_GC_INTERVAL
|
||||
# define LTALLOC_AUTO_GC_INTERVAL 3.00
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h> //for SIZE_MAX
|
||||
#include <limits.h> //for UINT_MAX
|
||||
#define alignas(a) __attribute__((aligned(a)))
|
||||
#define thread_local __thread
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#define CAS_LOCK(lock) __sync_lock_test_and_set(lock, 1)
|
||||
#define SPINLOCK_RELEASE(lock) __sync_lock_release(lock)
|
||||
#define PAUSE __asm__ __volatile__("pause" ::: "memory")
|
||||
#define BSR(r, v) r = CODE3264(__builtin_clz(v) ^ 31, __builtin_clzll(v) ^ 63)//x ^ 31 = 31 - x, but gcc does not optimize 31 - __builtin_clz(x) to bsr(x), but generates 31 - (bsr(x) ^ 31)
|
||||
|
||||
#elif _MSC_VER
|
||||
|
||||
#define _ALLOW_KEYWORD_MACROS
|
||||
#include <limits.h> //for SIZE_MAX and UINT_MAX
|
||||
#define alignas(a) __declspec(align(a))
|
||||
#define thread_local __declspec(thread)
|
||||
#define NOINLINE __declspec(noinline)
|
||||
#define CAS_LOCK(lock) _InterlockedExchange((long*)lock, 1)
|
||||
#define SPINLOCK_RELEASE(lock) _InterlockedExchange((long*)lock, 0)
|
||||
#define PAUSE _mm_pause()
|
||||
#define BSR(r, v) CODE3264(_BitScanReverse, _BitScanReverse64)((unsigned long*)&r, v)
|
||||
CPPCODE(extern "C") long _InterlockedExchange(long volatile *, long);
|
||||
CPPCODE(extern "C") void _mm_pause();
|
||||
#pragma warning(disable: 4127 4201 4324 4290)//"conditional expression is constant", "nonstandard extension used : nameless struct/union", and "structure was padded due to __declspec(align())"
|
||||
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
|
||||
#if __GNUC__ || __INTEL_COMPILER
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
static void SPINLOCK_ACQUIRE(volatile int *lock) {if (CAS_LOCK(lock)) while (*lock || CAS_LOCK(lock)) PAUSE;}
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> //for memset
|
||||
|
||||
#if SIZE_MAX == UINT_MAX
|
||||
#define CODE3264(c32, c64) c32
|
||||
#else
|
||||
#define CODE3264(c32, c64) c64
|
||||
#endif
|
||||
typedef char CODE3264_check[sizeof(void*) == CODE3264(4, 8) ? 1 : -1];
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define VMALLOC(size) VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)
|
||||
#define VMFREE(p, size) VirtualFree(p, 0, MEM_RELEASE)
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define VMALLOC(size) (void*)(((uintptr_t)mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)+1)&~1)//with the conversion of MAP_FAILED to 0
|
||||
#define VMFREE(p, size) munmap(p, size)
|
||||
|
||||
static size_t page_size()
|
||||
{
|
||||
assert((uintptr_t)MAP_FAILED+1 == 0);//have to use dynamic check somewhere, because some gcc versions (e.g. 4.4.5) won't compile typedef char MAP_FAILED_value_static_check[(uintptr_t)MAP_FAILED+1 == 0 ? 1 : -1];
|
||||
static size_t pagesize = 0;
|
||||
if (!pagesize) pagesize = sysconf(_SC_PAGE_SIZE);//assuming that writing of size_t value is atomic, so this can be done safely in different simultaneously running threads
|
||||
return pagesize;
|
||||
}
|
||||
|
||||
typedef struct PTrieNode // Compressed radix tree (patricia trie) for storing sizes of system allocations
|
||||
{ // This implementation have some specific properties:
|
||||
uintptr_t keys[2]; // 1. There are no separate leaf nodes (with both null children), as leaf's value is stored directly in place of corresponding child node pointer. Least significant bit of that pointer used to determine its meaning (i.e., is it a value, or child node pointer).
|
||||
struct PTrieNode *childNodes[2];// 2. Inserting a new element (key/value) into this tree require to create always an exactly one new node (and similarly for remove key/node operation).
|
||||
} PTrieNode; // 3. Tree always contains just one node with null child (i.e. all but one nodes in any possible tree are always have two children).
|
||||
#define PTRIE_NULL_NODE (PTrieNode*)(uintptr_t)1
|
||||
static PTrieNode *ptrieRoot = PTRIE_NULL_NODE, *ptrieFreeNodesList = NULL, *ptrieNewAllocatedPage = NULL;
|
||||
static volatile int ptrieLock = 0;
|
||||
|
||||
static uintptr_t ptrie_lookup(uintptr_t key)
|
||||
{
|
||||
PTrieNode *node = ptrieRoot;
|
||||
uintptr_t *lastKey = NULL;
|
||||
while (!((uintptr_t)node & 1))
|
||||
{
|
||||
int branch = (key >> (node->keys[0] & 0xFF)) & 1;
|
||||
lastKey = &node->keys[branch];
|
||||
node = node->childNodes[branch];
|
||||
}
|
||||
assert(lastKey && (*lastKey & ~0xFF) == key);
|
||||
return (uintptr_t)node & ~1;
|
||||
}
|
||||
|
||||
static void ptrie_insert(uintptr_t key, uintptr_t value, PTrieNode *newNode/* = (PTrieNode*)malloc(sizeof(PTrieNode))*/)
|
||||
{
|
||||
PTrieNode **node = &ptrieRoot, *n;
|
||||
uintptr_t *prevKey = NULL, x, pkey;
|
||||
unsigned int index, b;
|
||||
assert(!((value & 1) | (key & 0xFF)));//check constraints for key/value
|
||||
for (;;)
|
||||
{
|
||||
n = *node;
|
||||
if (!((uintptr_t)n & 1))//not a leaf
|
||||
{
|
||||
int prefixEnd = n->keys[0] & 0xFF;
|
||||
x = key ^ n->keys[0];// & ~0xFF;
|
||||
if (!(x & (~(uintptr_t)1 << prefixEnd))) {//prefix matches, so go on
|
||||
int branch = (key >> prefixEnd) & 1;
|
||||
node = &n->childNodes[branch];
|
||||
prevKey = &n->keys[branch];
|
||||
} else {//insert a new node before current
|
||||
pkey = n->keys[0] & ~0xFF;
|
||||
break;
|
||||
}
|
||||
} else {//leaf
|
||||
if (*node == PTRIE_NULL_NODE) {
|
||||
*node = newNode;
|
||||
newNode->keys[0] = key;//left prefixEnd = 0, so all following insertions will be before this node
|
||||
newNode->childNodes[0] = (PTrieNode*)(value | 1);
|
||||
newNode->childNodes[1] = PTRIE_NULL_NODE;
|
||||
return;
|
||||
} else {
|
||||
pkey = *prevKey & ~0xFF;
|
||||
x = key ^ pkey;
|
||||
assert(x/*key != pkey*/ && "key already inserted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BSR(index, x);
|
||||
b = (key >> index) & 1;
|
||||
newNode->keys[b] = key;
|
||||
newNode->keys[b^1] = pkey;
|
||||
newNode->keys[0] |= index;
|
||||
newNode->childNodes[b] = (PTrieNode*)(value | 1);
|
||||
newNode->childNodes[b^1] = n;
|
||||
*node = newNode;
|
||||
}
|
||||
|
||||
static uintptr_t ptrie_remove(uintptr_t key)
|
||||
{
|
||||
PTrieNode **node = &ptrieRoot;
|
||||
uintptr_t *pkey = NULL;
|
||||
assert(ptrieRoot != PTRIE_NULL_NODE && "trie is empty!");
|
||||
for (;;)
|
||||
{
|
||||
PTrieNode *n = *node;
|
||||
int branch = (key >> (n->keys[0] & 0xFF)) & 1;
|
||||
PTrieNode *cn = n->childNodes[branch];//current child node
|
||||
if ((uintptr_t)cn & 1)//leaf
|
||||
{
|
||||
PTrieNode *other = n->childNodes[branch^1];
|
||||
assert((n->keys[branch] & ~0xFF) == key);
|
||||
assert(cn != PTRIE_NULL_NODE && "node's key is probably broken");
|
||||
// if (other == PTRIE_NULL_NODE) *node = PTRIE_NULL_NODE; else//special handling for null child nodes is not necessary
|
||||
if (((uintptr_t)other & 1) && other != PTRIE_NULL_NODE)//if other node is not a pointer
|
||||
*pkey = (n->keys[branch^1] & ~0xFF) | ((*pkey) & 0xFF);
|
||||
*node = other;
|
||||
*(PTrieNode**)n = ptrieFreeNodesList; ptrieFreeNodesList = n;//free(n);
|
||||
return (uintptr_t)cn & ~1;
|
||||
}
|
||||
pkey = &n->keys[branch];
|
||||
node = &n->childNodes[branch];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *sys_aligned_alloc(size_t alignment, size_t size)
|
||||
{
|
||||
void *p = VMALLOC(size);//optimistically try mapping precisely the right amount before falling back to the slow method
|
||||
assert(!(alignment & (alignment-1)) && "alignment must be a power of two");
|
||||
if ((uintptr_t)p & (alignment-1)/* && p != MAP_FAILED*/)
|
||||
{
|
||||
VMFREE(p, size);
|
||||
#ifdef _WIN32
|
||||
{static DWORD allocationGranularity = 0;
|
||||
if (!allocationGranularity) {
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
allocationGranularity = si.dwAllocationGranularity;
|
||||
}
|
||||
if ((uintptr_t)p < 16*1024*1024)//fill "bubbles" (reserve unaligned regions) at the beginning of virtual address space, otherwise there will be always falling back to the slow method
|
||||
VirtualAlloc(p, alignment - ((uintptr_t)p & (alignment-1)), MEM_RESERVE, PAGE_NOACCESS);
|
||||
do
|
||||
{
|
||||
p = VirtualAlloc(NULL, size + alignment - allocationGranularity, MEM_RESERVE, PAGE_NOACCESS);
|
||||
if (p == NULL) return NULL;
|
||||
VirtualFree(p, 0, MEM_RELEASE);//unfortunately, WinAPI doesn't support release a part of allocated region, so release a whole region
|
||||
p = VirtualAlloc((void*)(((uintptr_t)p + (alignment-1)) & ~(alignment-1)), size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||
} while (p == NULL);}
|
||||
#else
|
||||
p = VMALLOC(size + alignment - page_size());
|
||||
if (p/* != MAP_FAILED*/)
|
||||
{
|
||||
uintptr_t ap = ((uintptr_t)p + (alignment-1)) & ~(alignment-1);
|
||||
uintptr_t diff = ap - (uintptr_t)p;
|
||||
if (diff) VMFREE(p, diff);
|
||||
diff = alignment - page_size() - diff;
|
||||
assert((intptr_t)diff >= 0);
|
||||
if (diff) VMFREE((void*)(ap + size), diff);
|
||||
return (void*)ap;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//if (p == 0) p = sys_aligned_alloc(alignment, size);//just in case (because 0 pointer is handled specially elsewhere)
|
||||
//if (p == MAP_FAILED) p = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
static NOINLINE void sys_free(void *p)
|
||||
{
|
||||
if (p == NULL) return;
|
||||
#ifdef _WIN32
|
||||
VirtualFree(p, 0, MEM_RELEASE);
|
||||
#else
|
||||
SPINLOCK_ACQUIRE(&ptrieLock);
|
||||
size_t size = ptrie_remove((uintptr_t)p);
|
||||
SPINLOCK_RELEASE(&ptrieLock);
|
||||
munmap(p, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void release_thread_cache(void*);
|
||||
#ifdef __GNUC__
|
||||
#include <pthread.h>
|
||||
#pragma weak pthread_once
|
||||
#pragma weak pthread_key_create
|
||||
#pragma weak pthread_setspecific
|
||||
static pthread_key_t pthread_key;
|
||||
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
|
||||
static void init_pthread_key() { pthread_key_create(&pthread_key, release_thread_cache); }
|
||||
static thread_local int thread_initialized = 0;
|
||||
static void init_pthread_destructor()//must be called only when some block placed into a thread cache's free list
|
||||
{
|
||||
if (unlikely(!thread_initialized))
|
||||
{
|
||||
thread_initialized = 1;
|
||||
if (pthread_once)
|
||||
{
|
||||
pthread_once(&init_once, init_pthread_key);
|
||||
pthread_setspecific(pthread_key, (void*)1);//set nonzero value to force calling of release_thread_cache() on thread terminate
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void NTAPI on_tls_callback(PVOID h, DWORD reason, PVOID pv) { h; pv; if (reason == DLL_THREAD_DETACH) release_thread_cache(0); }
|
||||
#pragma comment(linker, "/INCLUDE:" CODE3264("_","") "p_thread_callback_ltalloc")
|
||||
#pragma const_seg(".CRT$XLL")
|
||||
extern CPPCODE("C") const PIMAGE_TLS_CALLBACK p_thread_callback_ltalloc = on_tls_callback;
|
||||
#pragma const_seg()
|
||||
#define init_pthread_destructor()
|
||||
#endif
|
||||
|
||||
//End of platform-specific stuff
|
||||
|
||||
#define MAX_BLOCK_SIZE (MAX_BLOCK_SIZE < CHUNK_SIZE - (CHUNK_SIZE >> (1 + LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)) ? \
|
||||
MAX_BLOCK_SIZE : CHUNK_SIZE - (CHUNK_SIZE >> (1 + LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)))
|
||||
#define NUMBER_OF_SIZE_CLASSES ((sizeof(void*)*8 + 1) << LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)
|
||||
|
||||
typedef struct FreeBlock
|
||||
{
|
||||
struct FreeBlock *next,
|
||||
*nextBatch;//in the central cache blocks are organized into batches to allow fast moving blocks from thread cache and back
|
||||
} FreeBlock;
|
||||
|
||||
typedef struct alignas(CACHE_LINE_SIZE) ChunkBase//force sizeof(Chunk) = cache line size to avoid false sharing
|
||||
{
|
||||
unsigned int sizeClass;
|
||||
} Chunk;
|
||||
|
||||
typedef struct alignas(CACHE_LINE_SIZE) ChunkSm//chunk of smallest blocks of size = sizeof(void*)
|
||||
{
|
||||
unsigned int sizeClass;//struct ChunkBase chunk;
|
||||
struct ChunkSm *prev, *next;
|
||||
int numBatches;
|
||||
#define NUM_OF_BATCHES_IN_CHUNK_SM CHUNK_SIZE/(sizeof(void*)*MAX_NUM_OF_BLOCKS_IN_BATCH)
|
||||
FreeBlock *batches[NUM_OF_BATCHES_IN_CHUNK_SM];//batches of blocks inside ChunkSm have to be stored separately (as smallest blocks of size = sizeof(void*) do not have enough space to store second pointer for the batch)
|
||||
} ChunkSm;
|
||||
|
||||
typedef struct alignas(CACHE_LINE_SIZE)//align needed to prevent cache line sharing between adjacent classes accessed from different threads
|
||||
{
|
||||
volatile int lock;
|
||||
unsigned int freeBlocksInLastChunk;
|
||||
char *lastChunk;//Chunk or ChunkSm
|
||||
union {
|
||||
FreeBlock *firstBatch;
|
||||
ChunkSm *chunkWithFreeBatches;
|
||||
};
|
||||
FreeBlock *freeList;//short list of free blocks that for some reason are not organized into batches
|
||||
unsigned int freeListSize;//should be less than batch size
|
||||
uintptr_t minChunkAddr, maxChunkAddr;
|
||||
} CentralCache;
|
||||
static CentralCache centralCache[NUMBER_OF_SIZE_CLASSES];// = {{0}};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FreeBlock *freeList;
|
||||
FreeBlock *tempList;//intermediate list providing a hysteresis in order to avoid a corner case of too frequent moving free blocks to the central cache and back from
|
||||
int counter;//number of blocks in freeList (used to determine when to move free blocks list to the central cache)
|
||||
} ThreadCache;
|
||||
static thread_local ThreadCache threadCache[NUMBER_OF_SIZE_CLASSES];// = {{0}};
|
||||
|
||||
static struct
|
||||
{
|
||||
volatile int lock;
|
||||
void *freeChunk;
|
||||
size_t size;
|
||||
} pad = {0, NULL, 0};
|
||||
|
||||
static CPPCODE(inline) unsigned int get_size_class(size_t size)
|
||||
{
|
||||
unsigned int index;
|
||||
#if _MSC_VER && LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO == 2
|
||||
static const unsigned char small_size_classes[256] = {//have to use a const array here, because MS compiler unfortunately does not evaluate _BitScanReverse with a constant argument at compile time (as gcc does for __builtin_clz)
|
||||
#if CODE3264(1, 0)
|
||||
131, 4, 15, 17, 19, 20, 21, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43
|
||||
#else
|
||||
131, 15, 19, 21, 23, 24, 25, 26, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47
|
||||
#endif
|
||||
};
|
||||
if (size < 256 * sizeof(void*) - (sizeof(void*)-1))
|
||||
return small_size_classes[(size + (sizeof(void*)-1)) / sizeof(void*)];
|
||||
#endif
|
||||
|
||||
size = (size + (sizeof(void*)-1)) & ~(sizeof(void*)-1);//minimum block size is sizeof(void*), doing this is better than just "size = max(size, sizeof(void*))"
|
||||
|
||||
BSR(index, (size-1)|1);//"|1" needed because result of BSR is undefined for zero input
|
||||
#if LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO == 0
|
||||
return index;
|
||||
#else
|
||||
return (index<<LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO) + (unsigned int)((size-1) >> (index-LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO));
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned int class_to_size(unsigned int c)
|
||||
{
|
||||
#if LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO == 0
|
||||
return 2 << c;
|
||||
#else
|
||||
#if LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO >= CODE3264(2, 3)
|
||||
if (unlikely(c < (LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO<<LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)))//for block sizes less than or equal to pow(2, LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)
|
||||
return 2 << (c>>LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
c -= (1<<LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)-1;
|
||||
return ((c & ((1<<LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)-1)) | (1<<LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)) << ((c>>LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)-LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned int batch_size(unsigned int sizeClass)//calculates a number of blocks to move between a thread cache and a central cache in one shot
|
||||
{
|
||||
return ((MAX_BATCH_SIZE-1) >> (sizeClass >> LTALLOC_SIZE_CLASSES_SUBPOWER_OF_TWO)) & (MAX_NUM_OF_BLOCKS_IN_BATCH-1);
|
||||
}
|
||||
|
||||
CPPCODE(template <bool> static) void *ltmalloc(size_t size);
|
||||
|
||||
CPPCODE(template <bool throw_>) static void *fetch_from_central_cache(size_t size, ThreadCache *tc, unsigned int sizeClass)
|
||||
{
|
||||
void *p;
|
||||
if (likely(size-1u <= MAX_BLOCK_SIZE-1u))//<=> if (size <= MAX_BLOCK_SIZE && size != 0)
|
||||
{
|
||||
FreeBlock *fb = tc->tempList;
|
||||
if (fb)
|
||||
{
|
||||
assert(tc->counter == (int)batch_size(sizeClass)+1);
|
||||
tc->counter = 1;
|
||||
tc->freeList = fb->next;
|
||||
tc->tempList = NULL;
|
||||
return fb;
|
||||
}
|
||||
|
||||
assert(tc->counter == 0 || tc->counter == (int)batch_size(sizeClass)+1);
|
||||
tc->counter = 1;
|
||||
|
||||
{CentralCache *cc = ¢ralCache[sizeClass];
|
||||
SPINLOCK_ACQUIRE(&cc->lock);
|
||||
if (unlikely(!cc->firstBatch))//no free batch
|
||||
{no_free_batch:{
|
||||
unsigned int batchSize = batch_size(sizeClass)+1;
|
||||
|
||||
if (cc->freeList)
|
||||
{
|
||||
assert(cc->freeListSize);
|
||||
if (likely(cc->freeListSize <= batchSize + 1))
|
||||
{
|
||||
tc->counter = batchSize - cc->freeListSize + 1;
|
||||
// batchSize = cc->freeListSize;
|
||||
cc->freeListSize = 0;
|
||||
fb = cc->freeList;
|
||||
cc->freeList = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
cc->freeListSize -= batchSize;
|
||||
fb = cc->freeList;
|
||||
{FreeBlock *b = cc->freeList;
|
||||
while (--batchSize) b = b->next;
|
||||
cc->freeList = b->next;
|
||||
b->next = NULL;}
|
||||
}
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
tc->freeList = fb->next;
|
||||
init_pthread_destructor();//this call must be placed carefully to allow recursive memory allocation from pthread_key_create (in case when ltalloc replaces the system malloc)
|
||||
return fb;
|
||||
}
|
||||
|
||||
{unsigned int blockSize = class_to_size(sizeClass);
|
||||
if (cc->freeBlocksInLastChunk)
|
||||
{
|
||||
char *firstFree = cc->lastChunk;
|
||||
assert(cc->lastChunk && cc->freeBlocksInLastChunk == (CHUNK_SIZE - ((uintptr_t)cc->lastChunk & (CHUNK_SIZE-1)))/blockSize);
|
||||
if (cc->freeBlocksInLastChunk < batchSize) {
|
||||
tc->counter = batchSize - cc->freeBlocksInLastChunk + 1;
|
||||
batchSize = cc->freeBlocksInLastChunk;
|
||||
}
|
||||
cc->freeBlocksInLastChunk -= batchSize;
|
||||
cc->lastChunk += blockSize * batchSize;
|
||||
if (cc->freeBlocksInLastChunk == 0) {
|
||||
assert(((uintptr_t)cc->lastChunk & (CHUNK_SIZE-1)) == 0);
|
||||
cc->lastChunk = ((char**)cc->lastChunk)[-1];
|
||||
if (cc->lastChunk)
|
||||
cc->freeBlocksInLastChunk = (CHUNK_SIZE - ((uintptr_t)cc->lastChunk & (CHUNK_SIZE-1)))/blockSize;
|
||||
}
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
fb = (FreeBlock*)firstFree;
|
||||
while (--batchSize)
|
||||
firstFree = (char*)(((FreeBlock*)firstFree)->next = (FreeBlock*)(firstFree + blockSize));
|
||||
((FreeBlock*)firstFree)->next = NULL;
|
||||
tc->freeList = fb->next;
|
||||
init_pthread_destructor();
|
||||
return fb;
|
||||
}
|
||||
|
||||
//Allocate new chunk
|
||||
SPINLOCK_RELEASE(&cc->lock);//release lock for a while
|
||||
|
||||
SPINLOCK_ACQUIRE(&pad.lock);
|
||||
if (pad.freeChunk)
|
||||
{
|
||||
p = pad.freeChunk;
|
||||
pad.freeChunk = *(void**)p;
|
||||
pad.size -= CHUNK_SIZE;
|
||||
SPINLOCK_RELEASE(&pad.lock);
|
||||
((char**)((char*)p + CHUNK_SIZE))[-1] = 0;
|
||||
} else {
|
||||
SPINLOCK_RELEASE(&pad.lock);
|
||||
p = sys_aligned_alloc(CHUNK_SIZE, CHUNK_SIZE);
|
||||
if (unlikely(!p)) { CPPCODE(if (throw_) throw std::bad_alloc(); else) return NULL; }
|
||||
}
|
||||
|
||||
#define CHUNK_IS_SMALL unlikely(sizeClass < get_size_class(2*sizeof(void*)))
|
||||
{unsigned int numBlocksInChunk = (CHUNK_SIZE - (CHUNK_IS_SMALL ? sizeof(ChunkSm) : sizeof(Chunk)))/blockSize;
|
||||
#ifndef _WIN32
|
||||
//intptr_t sz = ((CHUNK_SIZE - numBlocksInChunk*blockSize) & ~(page_size()-1)) - page_size();
|
||||
//if (sz > 0) mprotect((char*)p + page_size(), sz, PROT_NONE);//munmap((char*)p + page_size(), sz);//to make possible unmapping, we need to be more careful when returning memory to the system, not simply VMFREE(firstFreeChunk, CHUNK_SIZE), so let there be just mprotect
|
||||
#endif
|
||||
assert(((char**)((char*)p + CHUNK_SIZE))[-1] == 0);//assume that allocated memory is always zero filled (on first access); it is better not to zero it explicitly because it will lead to allocation of physical page which may never needed otherwise
|
||||
if (numBlocksInChunk < batchSize) {
|
||||
tc->counter = batchSize - numBlocksInChunk + 1;
|
||||
batchSize = numBlocksInChunk;
|
||||
}
|
||||
|
||||
//Prepare chunk
|
||||
((Chunk*)p)->sizeClass = sizeClass;
|
||||
{char *firstFree = (char*)p + CHUNK_SIZE - numBlocksInChunk*blockSize;//blocks in chunk are located in such way to achieve a maximum possible alignment
|
||||
fb = (FreeBlock*)firstFree;
|
||||
{int n = batchSize; while (--n)
|
||||
firstFree = (char*)(((FreeBlock*)firstFree)->next = (FreeBlock*)(firstFree + blockSize));}
|
||||
((FreeBlock*)firstFree)->next = NULL;
|
||||
firstFree += blockSize;
|
||||
|
||||
SPINLOCK_ACQUIRE(&cc->lock);
|
||||
if ((uintptr_t)p < cc->minChunkAddr || !cc->minChunkAddr) cc->minChunkAddr = (uintptr_t)p;
|
||||
if ((uintptr_t)p > cc->maxChunkAddr ) cc->maxChunkAddr = (uintptr_t)p;
|
||||
|
||||
if (CHUNK_IS_SMALL)//special handling for smallest blocks of size = sizeof(void*)
|
||||
{
|
||||
ChunkSm *cs = (ChunkSm*)p;
|
||||
cs->numBatches = 0;
|
||||
//Insert new chunk right after chunkWithFreeBatches
|
||||
cs->prev = cc->chunkWithFreeBatches;
|
||||
if (cc->chunkWithFreeBatches) {
|
||||
cs->next = cc->chunkWithFreeBatches->next;
|
||||
if (cc->chunkWithFreeBatches->next) cc->chunkWithFreeBatches->next->prev = cs;
|
||||
cc->chunkWithFreeBatches->next = cs;
|
||||
} else {
|
||||
cs->next = NULL;
|
||||
cc->chunkWithFreeBatches = cs;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(cc->freeBlocksInLastChunk))//so happened that other thread have already allocated chunk for the same size class while the lock was released
|
||||
{
|
||||
//Hook pointer to the current lastChunk at the end of new chunk (another way is just put all blocks to cc->freeList which is much less effecient)
|
||||
((char**)(((uintptr_t)firstFree & ~(CHUNK_SIZE-1)) + CHUNK_SIZE))[-1] = cc->lastChunk;
|
||||
}
|
||||
cc->freeBlocksInLastChunk = numBlocksInChunk - batchSize;
|
||||
cc->lastChunk = firstFree;
|
||||
}}}}}
|
||||
else {
|
||||
if (!CHUNK_IS_SMALL)//smallest blocks of size = sizeof(void*) are handled specially
|
||||
{
|
||||
fb = cc->firstBatch;
|
||||
cc->firstBatch = fb->nextBatch;
|
||||
}
|
||||
else//size of block = sizeof(void*)
|
||||
{
|
||||
ChunkSm *cs = cc->chunkWithFreeBatches;
|
||||
if (unlikely(cs->numBatches == 0))
|
||||
{
|
||||
if (unlikely(cs->prev == NULL)) goto no_free_batch;
|
||||
cs = cc->chunkWithFreeBatches = cs->prev;
|
||||
assert(cs->numBatches == NUM_OF_BATCHES_IN_CHUNK_SM);
|
||||
}
|
||||
fb = cs->batches[--cs->numBatches];
|
||||
}
|
||||
}
|
||||
SPINLOCK_RELEASE(&cc->lock);}
|
||||
tc->freeList = fb->next;
|
||||
init_pthread_destructor();
|
||||
return fb;
|
||||
}
|
||||
else//allocate block directly from the system
|
||||
{
|
||||
if (unlikely(size == 0)) return ltmalloc CPPCODE(<throw_>)(1);//return NULL;//doing this check here is better than on the top level
|
||||
|
||||
size = (size + CHUNK_SIZE-1) & ~(CHUNK_SIZE-1);
|
||||
p = sys_aligned_alloc(CHUNK_SIZE, size);
|
||||
#ifndef _WIN32
|
||||
if (p) {
|
||||
SPINLOCK_ACQUIRE(&ptrieLock);
|
||||
PTrieNode *newNode;
|
||||
if (ptrieFreeNodesList)
|
||||
ptrieFreeNodesList = *(PTrieNode**)(newNode = ptrieFreeNodesList);
|
||||
else if (ptrieNewAllocatedPage) {
|
||||
newNode = ptrieNewAllocatedPage;
|
||||
if (!((uintptr_t)++ptrieNewAllocatedPage & (page_size()-1)))
|
||||
ptrieNewAllocatedPage = ((PTrieNode**)ptrieNewAllocatedPage)[-1];
|
||||
} else {
|
||||
SPINLOCK_RELEASE(&ptrieLock);
|
||||
newNode = (PTrieNode*)VMALLOC(page_size());
|
||||
if (unlikely(!newNode)) { CPPCODE(if (throw_) throw std::bad_alloc(); else) return NULL; }
|
||||
assert(((char**)((char*)newNode + page_size()))[-1] == 0);
|
||||
SPINLOCK_ACQUIRE(&ptrieLock);
|
||||
((PTrieNode**)((char*)newNode + page_size()))[-1] = ptrieNewAllocatedPage;//in case if other thread also have just allocated a new page
|
||||
ptrieNewAllocatedPage = newNode + 1;
|
||||
}
|
||||
ptrie_insert((uintptr_t)p, size, newNode);
|
||||
SPINLOCK_RELEASE(&ptrieLock);
|
||||
}
|
||||
#endif
|
||||
CPPCODE(if (throw_) if (unlikely(!p)) throw std::bad_alloc();)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
CPPCODE(template <bool throw_> static) void *ltmalloc(size_t size)
|
||||
{
|
||||
unsigned int sizeClass = get_size_class(size);
|
||||
ThreadCache *tc = &threadCache[sizeClass];
|
||||
FreeBlock *fb = tc->freeList;
|
||||
if (likely(fb))
|
||||
{
|
||||
tc->freeList = fb->next;
|
||||
tc->counter++;
|
||||
return fb;
|
||||
}
|
||||
else
|
||||
return fetch_from_central_cache CPPCODE(<throw_>)(size, tc, sizeClass);
|
||||
}
|
||||
CPPCODE(void *ltmalloc(size_t size) {return ltmalloc<false>(size);})//for possible external usage
|
||||
|
||||
static void add_batch_to_central_cache(CentralCache *cc, unsigned int sizeClass, FreeBlock *batch)
|
||||
{
|
||||
if (!CHUNK_IS_SMALL)
|
||||
{
|
||||
batch->nextBatch = cc->firstBatch;
|
||||
cc->firstBatch = batch;
|
||||
}
|
||||
else
|
||||
{
|
||||
ChunkSm *cs = cc->chunkWithFreeBatches;
|
||||
if (unlikely(cs->numBatches == NUM_OF_BATCHES_IN_CHUNK_SM))
|
||||
{
|
||||
cs = cc->chunkWithFreeBatches = cc->chunkWithFreeBatches->next;
|
||||
assert(cs && cs->numBatches == 0);
|
||||
}
|
||||
cs->batches[cs->numBatches++] = batch;
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE void move_to_central_cache(ThreadCache *tc, unsigned int sizeClass)
|
||||
{
|
||||
init_pthread_destructor();//needed for cases when freed memory was allocated in the other thread and no alloc was called in this thread till its termination
|
||||
|
||||
tc->counter = batch_size(sizeClass);
|
||||
if (tc->tempList)//move temp list to the central cache
|
||||
{
|
||||
CentralCache *cc = ¢ralCache[sizeClass];
|
||||
SPINLOCK_ACQUIRE(&cc->lock);
|
||||
add_batch_to_central_cache(cc, sizeClass, tc->tempList);
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
}
|
||||
// else if (unlikely(!tc->freeList))//this is a first call (i.e. when counter = 0) - just initialization of counter needed
|
||||
// {
|
||||
// tc->counter--;
|
||||
// return;
|
||||
// }
|
||||
|
||||
tc->tempList = tc->freeList;
|
||||
tc->freeList = NULL;
|
||||
}
|
||||
|
||||
void ltfree(void *p)
|
||||
{
|
||||
if (likely((uintptr_t)p & (CHUNK_SIZE-1)))
|
||||
{
|
||||
unsigned int sizeClass = ((Chunk*)((uintptr_t)p & ~(CHUNK_SIZE-1)))->sizeClass;
|
||||
ThreadCache *tc = &threadCache[sizeClass];
|
||||
|
||||
if (unlikely(--tc->counter < 0))
|
||||
move_to_central_cache(tc, sizeClass);
|
||||
|
||||
((FreeBlock*)p)->next = tc->freeList;
|
||||
tc->freeList = (FreeBlock*)p;
|
||||
}
|
||||
else
|
||||
sys_free(p);
|
||||
}
|
||||
|
||||
size_t ltmsize(void *p)
|
||||
{
|
||||
if (likely((uintptr_t)p & (CHUNK_SIZE-1)))
|
||||
{
|
||||
return class_to_size(((Chunk*)((uintptr_t)p & ~(CHUNK_SIZE-1)))->sizeClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p == NULL) return 0;
|
||||
#ifdef _WIN32
|
||||
{MEMORY_BASIC_INFORMATION mi;
|
||||
VirtualQuery(p, &mi, sizeof(mi));
|
||||
return mi.RegionSize;}
|
||||
#else
|
||||
SPINLOCK_ACQUIRE(&ptrieLock);
|
||||
size_t size = ptrie_lookup((uintptr_t)p);
|
||||
SPINLOCK_RELEASE(&ptrieLock);
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void release_thread_cache(void *p)
|
||||
{
|
||||
unsigned int sizeClass = 0; (void)p;
|
||||
for (;sizeClass < NUMBER_OF_SIZE_CLASSES; sizeClass++)
|
||||
{
|
||||
ThreadCache *tc = &threadCache[sizeClass];
|
||||
if (tc->freeList || tc->tempList)
|
||||
{
|
||||
FreeBlock *tail = tc->freeList;
|
||||
unsigned int freeListSize = 1;
|
||||
CentralCache *cc = ¢ralCache[sizeClass];
|
||||
|
||||
if (tail)
|
||||
while (tail->next)//search for end of list
|
||||
tail = tail->next, freeListSize++;
|
||||
|
||||
SPINLOCK_ACQUIRE(&cc->lock);
|
||||
if (tc->tempList)
|
||||
add_batch_to_central_cache(cc, sizeClass, tc->tempList);
|
||||
if (tc->freeList) {//append tc->freeList to cc->freeList
|
||||
tail->next = cc->freeList;
|
||||
cc->freeList = tc->freeList;
|
||||
assert(freeListSize == batch_size(sizeClass)+1 - tc->counter);
|
||||
cc->freeListSize += freeListSize;
|
||||
}
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ltsqueeze(size_t padsz)
|
||||
{
|
||||
unsigned int sizeClass = get_size_class(2*sizeof(void*));//skip small chunks because corresponding batches can not be efficiently detached from the central cache (if that becomes relevant, may be it worths to reimplement batches for small chunks from array to linked lists)
|
||||
for (;sizeClass < NUMBER_OF_SIZE_CLASSES; sizeClass++)
|
||||
{
|
||||
CentralCache *cc = ¢ralCache[sizeClass];
|
||||
if (cc->maxChunkAddr - cc->minChunkAddr <= CHUNK_SIZE)//preliminary check without lock (assume that writing to minChunkAddr/maxChunkAddr is atomic)
|
||||
continue;
|
||||
|
||||
SPINLOCK_ACQUIRE(&cc->lock);
|
||||
if (cc->maxChunkAddr - cc->minChunkAddr <= CHUNK_SIZE) {//quick check for theoretical possibility that at least one chunk is totally free
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
continue;
|
||||
}
|
||||
{uintptr_t minChunkAddr = cc->minChunkAddr;
|
||||
size_t bufferSize = ((cc->maxChunkAddr - minChunkAddr) / CHUNK_SIZE + 1) * sizeof(short);
|
||||
//Quickly detach all batches of the current size class from the central cache
|
||||
unsigned int freeListSize = cc->freeListSize;
|
||||
FreeBlock *firstBatch = cc->firstBatch, *freeList = cc->freeList;
|
||||
cc->firstBatch = NULL;
|
||||
cc->freeList = NULL;
|
||||
cc->freeListSize = 0;
|
||||
SPINLOCK_RELEASE(&cc->lock);
|
||||
|
||||
//1. Find out chunks with only free blocks via a simple counting the number of free blocks in each chunk
|
||||
{char buffer[32*1024];//enough for 1GB address space
|
||||
unsigned short *inChunkFreeBlocks = (unsigned short*)(bufferSize <= sizeof(buffer) ? memset(buffer, 0, bufferSize) : VMALLOC(bufferSize));
|
||||
unsigned int numBlocksInChunk = (CHUNK_SIZE - (/*CHUNK_IS_SMALL ? sizeof(ChunkSm) : */sizeof(Chunk)))/class_to_size(sizeClass);
|
||||
FreeBlock **pbatch, *block, **pblock;
|
||||
Chunk *firstFreeChunk = NULL;
|
||||
assert(numBlocksInChunk < (1U<<(sizeof(short)*8)));//in case if CHUNK_SIZE is too big that total count of blocks in it doesn't fit at short type (...may be use static_assert instead?)
|
||||
if (inChunkFreeBlocks)//consider VMALLOC can fail
|
||||
{
|
||||
for (pbatch = &firstBatch; *pbatch; pbatch = &(*pbatch)->nextBatch)
|
||||
for (block = *pbatch; block; block = block->next)
|
||||
#define FREE_BLOCK(block) \
|
||||
if (++inChunkFreeBlocks[((uintptr_t)block - minChunkAddr) / CHUNK_SIZE] == numBlocksInChunk)/*chunk is totally free*/\
|
||||
{\
|
||||
Chunk *chunk = (Chunk*)((uintptr_t)block & ~(CHUNK_SIZE-1));\
|
||||
assert(chunk->sizeClass == sizeClass);/*just in case check before overwriting this info*/\
|
||||
*(Chunk**)chunk = firstFreeChunk;/*put nextFreeChunk pointer right at the beginning of Chunk as there are always must be a space for one pointer before first memory block*/\
|
||||
firstFreeChunk = chunk;\
|
||||
}
|
||||
FREE_BLOCK(block)
|
||||
for (pblock = &freeList; *pblock; pblock = &(*pblock)->next)
|
||||
FREE_BLOCK(*pblock)
|
||||
#undef FREE_BLOCK
|
||||
}
|
||||
else {
|
||||
for (pbatch = &firstBatch; *pbatch; pbatch = &(*pbatch)->nextBatch);
|
||||
for (pblock = &freeList; *pblock; pblock = &(*pblock)->next);
|
||||
}
|
||||
|
||||
if (firstFreeChunk)//is anything to release
|
||||
{
|
||||
//2. Unlink all matching blocks from the corresponding free lists
|
||||
FreeBlock *additionalBatchesList = NULL, *additionalBlocksList = NULL, **abatch = &additionalBatchesList, **ablock = &additionalBlocksList;
|
||||
unsigned int additionalBlocksListSize = 0, batchSize = batch_size(sizeClass)+1;
|
||||
for (pbatch = &firstBatch; *pbatch;)
|
||||
{
|
||||
for (block = *pbatch; block; block = block->next)
|
||||
if (inChunkFreeBlocks[((uintptr_t)block - minChunkAddr) / CHUNK_SIZE] == numBlocksInChunk)//if at least one block belongs to a releasable chunk, then this batch should be handled specially
|
||||
{
|
||||
FreeBlock *nextBatch = (*pbatch)->nextBatch;
|
||||
for (block = *pbatch; block;)//re-add blocks of not-for-release chunks and organize them into another batches' list (to join it with the main later)
|
||||
if (inChunkFreeBlocks[((uintptr_t)block - minChunkAddr) / CHUNK_SIZE] != numBlocksInChunk)//skip matching-for-release blocks
|
||||
{
|
||||
*ablock = block;
|
||||
do//this loop needed only to minimize memory write operations, otherwise a simpler approach could be used (like in the next loop below)
|
||||
{
|
||||
ablock = &block->next;
|
||||
block = block->next;
|
||||
if (++additionalBlocksListSize == batchSize)
|
||||
{
|
||||
abatch = &(*abatch = additionalBlocksList)->nextBatch;
|
||||
*abatch = NULL;
|
||||
*ablock = NULL;
|
||||
ablock = &additionalBlocksList;
|
||||
additionalBlocksList = NULL;
|
||||
additionalBlocksListSize = 0;
|
||||
break;//to force *ablock = block; for starting a new batch
|
||||
}
|
||||
} while (block && inChunkFreeBlocks[((uintptr_t)block - minChunkAddr) / CHUNK_SIZE] != numBlocksInChunk);
|
||||
}
|
||||
else
|
||||
block = block->next;
|
||||
*ablock = NULL;
|
||||
*pbatch = nextBatch;//unlink batch
|
||||
goto continue_;
|
||||
}
|
||||
pbatch = &(*pbatch)->nextBatch;
|
||||
continue_:;
|
||||
}
|
||||
for (block = freeList; block;)
|
||||
if (inChunkFreeBlocks[((uintptr_t)block - minChunkAddr) / CHUNK_SIZE] != numBlocksInChunk)
|
||||
{
|
||||
//*pblock = (*pblock)->next, freeListSize--;//unlink block
|
||||
ablock = &(*ablock = block)->next;
|
||||
block = block->next;
|
||||
*ablock = NULL;
|
||||
if (++additionalBlocksListSize == batchSize)
|
||||
{
|
||||
abatch = &(*abatch = additionalBlocksList)->nextBatch;
|
||||
*abatch = NULL;
|
||||
ablock = &additionalBlocksList;
|
||||
additionalBlocksList = NULL;
|
||||
additionalBlocksListSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
block = block->next;
|
||||
//Add additional lists
|
||||
*abatch = *pbatch;
|
||||
*pbatch = additionalBatchesList;
|
||||
pblock = ablock;
|
||||
freeList = additionalBlocksList;
|
||||
freeListSize = additionalBlocksListSize;
|
||||
|
||||
//Return back all left not-for-release blocks to the central cache as quickly as possible (as other threads may want to allocate a new memory)
|
||||
#define GIVE_LISTS_BACK_TO_CC \
|
||||
SPINLOCK_ACQUIRE(&cc->lock);\
|
||||
*pbatch = cc->firstBatch;\
|
||||
cc->firstBatch = firstBatch;\
|
||||
*pblock = cc->freeList;\
|
||||
cc->freeList = freeList;\
|
||||
cc->freeListSize += freeListSize;\
|
||||
SPINLOCK_RELEASE(&cc->lock);\
|
||||
if (bufferSize > sizeof(buffer)) VMFREE(inChunkFreeBlocks, bufferSize);//this better to do before 3. as kernel is likely optimized for release of just allocated range
|
||||
GIVE_LISTS_BACK_TO_CC
|
||||
|
||||
if (padsz)
|
||||
{
|
||||
SPINLOCK_ACQUIRE(&pad.lock);
|
||||
if (pad.size < padsz)
|
||||
{
|
||||
Chunk *first = firstFreeChunk, **c;
|
||||
do//put off free chunks up to a specified pad size
|
||||
{
|
||||
c = (Chunk**)firstFreeChunk;
|
||||
firstFreeChunk = *c;
|
||||
pad.size += CHUNK_SIZE;
|
||||
} while (pad.size < padsz && firstFreeChunk);
|
||||
*c = (Chunk*)pad.freeChunk;
|
||||
pad.freeChunk = first;
|
||||
}
|
||||
SPINLOCK_RELEASE(&pad.lock);
|
||||
}
|
||||
|
||||
//3. Return memory to the system
|
||||
while (firstFreeChunk)
|
||||
{
|
||||
Chunk *nextFreeChunk = *(Chunk**)firstFreeChunk;
|
||||
VMFREE(firstFreeChunk, CHUNK_SIZE);
|
||||
firstFreeChunk = nextFreeChunk;
|
||||
}
|
||||
}
|
||||
else//nothing to release - just return batches back to the central cache
|
||||
{
|
||||
GIVE_LISTS_BACK_TO_CC
|
||||
#undef GIVE_LISTS_BACK_TO_CC
|
||||
}}}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) && !defined(LTALLOC_DISABLE_OPERATOR_NEW_OVERRIDE)
|
||||
void *operator new (size_t size) throw(std::bad_alloc) {return ltmalloc<true> (size);}
|
||||
void *operator new (size_t size, const std::nothrow_t&) throw() {return ltmalloc<false>(size);}
|
||||
void *operator new[](size_t size) throw(std::bad_alloc) {return ltmalloc<true> (size);}
|
||||
void *operator new[](size_t size, const std::nothrow_t&) throw() {return ltmalloc<false>(size);}
|
||||
|
||||
void operator delete (void* p) throw() {ltfree(p);}
|
||||
void operator delete (void* p, const std::nothrow_t&) throw() {ltfree(p);}
|
||||
void operator delete[](void* p) throw() {ltfree(p);}
|
||||
void operator delete[](void* p, const std::nothrow_t&) throw() {ltfree(p);}
|
||||
#endif
|
||||
|
||||
/* @r-lyeh's { */
|
||||
#include <string.h>
|
||||
void *ltcalloc(size_t elems, size_t size) {
|
||||
size *= elems;
|
||||
return memset( ltmalloc( size ), 0, size );
|
||||
}
|
||||
void *ltmemalign( size_t align, size_t size ) {
|
||||
return --align, ltmalloc( (size+align)&~align );
|
||||
}
|
||||
void *ltrealloc( void *ptr, size_t sz ) {
|
||||
if( !ptr ) return ltmalloc( sz );
|
||||
if( !sz ) return ltfree( ptr ), (void *)0;
|
||||
size_t osz = ltmsize( ptr );
|
||||
if( sz <= osz ) {
|
||||
return ptr;
|
||||
}
|
||||
void *nptr = memcpy( ltmalloc(sz), ptr, osz );
|
||||
ltfree( ptr );
|
||||
|
||||
#ifdef LTALLOC_AUTO_GC_INTERVAL
|
||||
/* this is kind of compromise; the following timer is to guarantee
|
||||
that memory gets wiped out at least every given seconds between consecutive
|
||||
ltrealloc() calls (I am assuming frequency usage for ltrealloc() is smaller
|
||||
than ltmalloc() or ltfree() too) - @r-lyeh */
|
||||
clock_t now = clock();
|
||||
static clock_t then = now;
|
||||
if( ( double(now - then) / CLOCKS_PER_SEC ) > LTALLOC_AUTO_GC_INTERVAL ) {
|
||||
ltsqueeze(0);
|
||||
}
|
||||
then = now;
|
||||
#endif
|
||||
|
||||
return nptr;
|
||||
}
|
||||
/* } */
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <stdlib.h> /*a more portable size_t definition than stddef.h itself*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void* ltmalloc(size_t);
|
||||
void ltfree(void*);
|
||||
void* ltrealloc( void *, size_t );
|
||||
void* ltcalloc( size_t, size_t );
|
||||
void* ltmemalign( size_t, size_t );
|
||||
void ltsqueeze(size_t pad); /*return memory to system (see README.md)*/
|
||||
size_t ltmsize(void*);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,59 +0,0 @@
|
||||
// based on code by Jerry Coffin (most likely Public Domain)
|
||||
// - rlyeh
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
#include <limits>
|
||||
|
||||
#include "ltalloc.h"
|
||||
|
||||
namespace lt {
|
||||
template <class T>
|
||||
struct allocator {
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
template <class U> struct rebind { typedef allocator<U> other; };
|
||||
allocator() throw() {}
|
||||
allocator(const allocator&) throw() {}
|
||||
|
||||
template <class U> allocator(const allocator<U>&) throw(){}
|
||||
|
||||
~allocator() throw() {}
|
||||
|
||||
pointer address(reference x) const { return &x; }
|
||||
const_pointer address(const_reference x) const { return &x; }
|
||||
|
||||
pointer allocate(size_type s, void const * = 0) {
|
||||
if (0 == s)
|
||||
return NULL;
|
||||
pointer temp = (pointer)ltmalloc(s * sizeof(T));
|
||||
if (temp == NULL)
|
||||
throw std::bad_alloc();
|
||||
return temp;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type) {
|
||||
ltfree(p);
|
||||
}
|
||||
|
||||
size_type max_size() const throw() {
|
||||
return std::numeric_limits<size_t>::max() / sizeof(T);
|
||||
}
|
||||
|
||||
void construct(pointer p, const T& val) {
|
||||
new((void *)p) T(val);
|
||||
}
|
||||
|
||||
void destroy(pointer p) {
|
||||
p->~T();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,21 @@ newoption {
|
||||
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" }
|
||||
@@ -12,17 +27,35 @@ solution "objview"
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
files { "viewer.cc", "trackball.cc", "ltalloc.cc" }
|
||||
files { "viewer.cc", "trackball.cc" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
|
||||
buildoptions { "-std=c++11" }
|
||||
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" }
|
||||
@@ -34,14 +67,15 @@ solution "objview"
|
||||
|
||||
configuration { "windows" }
|
||||
-- Path to GLFW3
|
||||
includedirs { '../../../../local/glfw-3.1.2.bin.WIN64/include' }
|
||||
libdirs { '../../../../local/glfw-3.1.2.bin.WIN64/lib-vc2013' }
|
||||
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' }
|
||||
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" }
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// Optimized wavefront .obj loader.
|
||||
// Requires ltalloc and C++11
|
||||
// Requires lfpAlloc and C++11
|
||||
//
|
||||
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2016 Syoyo Fujita and many contributors.
|
||||
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
|
||||
@@ -29,7 +29,7 @@ THE SOFTWARE.
|
||||
#ifndef TINOBJ_LOADER_OPT_H_
|
||||
#define TINOBJ_LOADER_OPT_H_
|
||||
|
||||
#ifdef _WIN64
|
||||
#ifdef _WIN32
|
||||
#define atoll(S) _atoi64(S)
|
||||
#include <windows.h>
|
||||
#else
|
||||
@@ -38,7 +38,6 @@ THE SOFTWARE.
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
@@ -55,7 +54,7 @@ THE SOFTWARE.
|
||||
#include <chrono> // C++11
|
||||
#include <thread> // C++11
|
||||
|
||||
#include "ltalloc.hpp"
|
||||
#include "lfpAlloc/Allocator.hpp"
|
||||
|
||||
namespace tinyobj_opt {
|
||||
|
||||
@@ -292,6 +291,22 @@ typedef struct {
|
||||
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<std::string, std::string> unknown_parameter;
|
||||
} material_t;
|
||||
|
||||
@@ -301,21 +316,22 @@ typedef struct {
|
||||
unsigned int length;
|
||||
} shape_t;
|
||||
|
||||
struct vertex_index {
|
||||
int v_idx, vt_idx, vn_idx;
|
||||
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
|
||||
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
|
||||
vertex_index(int vidx, int vtidx, int vnidx)
|
||||
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
|
||||
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<float, lt::allocator<float> > vertices;
|
||||
std::vector<float, lt::allocator<float> > normals;
|
||||
std::vector<float, lt::allocator<float> > texcoords;
|
||||
std::vector<vertex_index, lt::allocator<vertex_index> > faces;
|
||||
std::vector<int, lt::allocator<int> > face_num_verts;
|
||||
std::vector<int, lt::allocator<int> > material_ids;
|
||||
std::vector<float, lfpAlloc::lfpAllocator<float> > vertices;
|
||||
std::vector<float, lfpAlloc::lfpAllocator<float> > normals;
|
||||
std::vector<float, lfpAlloc::lfpAllocator<float> > texcoords;
|
||||
std::vector<index_t, lfpAlloc::lfpAllocator<index_t> > indices;
|
||||
std::vector<int, lfpAlloc::lfpAllocator<int> > face_num_verts;
|
||||
std::vector<int, lfpAlloc::lfpAllocator<int> > material_ids;
|
||||
} attrib_t;
|
||||
|
||||
typedef StackVector<char, 256> ShortString;
|
||||
@@ -386,12 +402,11 @@ static inline int fixIndex(int idx, int n) {
|
||||
}
|
||||
|
||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseRawTriple(const char **token) {
|
||||
vertex_index vi(
|
||||
static index_t parseRawTriple(const char **token) {
|
||||
index_t vi(
|
||||
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
||||
|
||||
vi.v_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.vertex_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -404,8 +419,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
// i//k
|
||||
if ((*token)[0] == '/') {
|
||||
(*token)++;
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.normal_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -414,8 +428,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
}
|
||||
|
||||
// i/j/k or i/j
|
||||
vi.vt_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.texcoord_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -426,8 +439,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
|
||||
// i/j/k
|
||||
(*token)++; // skip '/'
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.normal_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -436,17 +448,17 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
}
|
||||
|
||||
static inline bool parseString(ShortString *s, const char **token) {
|
||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
||||
size_t e = until_space((*token)); // strcspn((*token), " \t\r");
|
||||
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); //(*token) += strspn((*token), " \t");
|
||||
skip_space(token);
|
||||
int i = my_atoi((*token));
|
||||
(*token) += until_space((*token)); // strcspn((*token), " \t\r");
|
||||
(*token) += until_space((*token));
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -585,9 +597,9 @@ assemble :
|
||||
|
||||
{
|
||||
// = pow(5.0, exponent);
|
||||
double a = 5.0;
|
||||
double a = 1.0;
|
||||
for (int i = 0; i < exponent; i++) {
|
||||
a = a * a;
|
||||
a = a * 5.0;
|
||||
}
|
||||
*result =
|
||||
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
|
||||
@@ -601,13 +613,12 @@ fail:
|
||||
}
|
||||
|
||||
static inline float parseFloat(const char **token) {
|
||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
||||
skip_space(token);
|
||||
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
|
||||
float f = static_cast<float>(atof(*token));
|
||||
(*token) += strcspn((*token), " \t\r");
|
||||
#else
|
||||
const char *end =
|
||||
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
|
||||
const char *end = (*token) + until_space((*token));
|
||||
double val = 0.0;
|
||||
tryParseDouble((*token), end, &val);
|
||||
float f = static_cast<float>(val);
|
||||
@@ -665,6 +676,11 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
|
||||
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')
|
||||
@@ -747,7 +763,8 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
}
|
||||
|
||||
// transmittance
|
||||
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||
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);
|
||||
@@ -795,6 +812,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
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])
|
||||
@@ -802,6 +820,55 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
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;
|
||||
@@ -858,6 +925,41 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
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) {
|
||||
@@ -896,9 +998,9 @@ typedef struct {
|
||||
float tx, ty;
|
||||
|
||||
// for f
|
||||
std::vector<vertex_index, lt::allocator<vertex_index> > f;
|
||||
// std::vector<vertex_index> f;
|
||||
std::vector<int, lt::allocator<int> > f_num_verts;
|
||||
std::vector<index_t, lfpAlloc::lfpAllocator<index_t> > f;
|
||||
// std::vector<index_t> f;
|
||||
std::vector<int, lfpAlloc::lfpAllocator<int> > f_num_verts;
|
||||
|
||||
const char *group_name;
|
||||
unsigned int group_name_len;
|
||||
@@ -918,44 +1020,48 @@ struct CommandCount {
|
||||
size_t num_vn;
|
||||
size_t num_vt;
|
||||
size_t num_f;
|
||||
size_t num_faces;
|
||||
size_t num_indices;
|
||||
CommandCount() {
|
||||
num_v = 0;
|
||||
num_vn = 0;
|
||||
num_vt = 0;
|
||||
num_f = 0;
|
||||
num_faces = 0;
|
||||
num_indices = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class
|
||||
LoadOption
|
||||
{
|
||||
class LoadOption {
|
||||
public:
|
||||
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
|
||||
|
||||
int req_num_threads;
|
||||
bool triangulate;
|
||||
bool verbose;
|
||||
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<shape_t> *shapes, const char *buf,
|
||||
size_t len, const LoadOption& option);
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
std::vector<material_t> *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);
|
||||
// StackVector<char, 256> linebuf;
|
||||
// linebuf->resize(p_len + 1);
|
||||
memcpy(&linebuf, p, p_len);
|
||||
memcpy(linebuf, p, p_len);
|
||||
linebuf[p_len] = '\0';
|
||||
|
||||
const char *token = linebuf;
|
||||
@@ -963,8 +1069,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
command->type = COMMAND_EMPTY;
|
||||
|
||||
// Skip leading space.
|
||||
// token += strspn(token, " \t");
|
||||
skip_space(&token); //(*token) += strspn((*token), " \t");
|
||||
skip_space(&token);
|
||||
|
||||
assert(token);
|
||||
if (token[0] == '\0') { // empty line
|
||||
@@ -978,7 +1083,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
// vertex
|
||||
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
||||
token += 2;
|
||||
float x, y, z;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
command->vx = x;
|
||||
command->vy = y;
|
||||
@@ -990,7 +1095,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
// normal
|
||||
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
|
||||
token += 3;
|
||||
float x, y, z;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
command->nx = x;
|
||||
command->ny = y;
|
||||
@@ -1002,7 +1107,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
// texcoord
|
||||
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||
token += 3;
|
||||
float x, y;
|
||||
float x = 0.0f, y = 0.0f;
|
||||
parseFloat2(&x, &y, &token);
|
||||
command->tx = x;
|
||||
command->ty = y;
|
||||
@@ -1013,19 +1118,12 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
// face
|
||||
if (token[0] == 'f' && IS_SPACE((token[1]))) {
|
||||
token += 2;
|
||||
// token += strspn(token, " \t");
|
||||
skip_space(&token);
|
||||
|
||||
StackVector<vertex_index, 8> f;
|
||||
StackVector<index_t, 8> f;
|
||||
|
||||
while (!IS_NEW_LINE(token[0])) {
|
||||
vertex_index vi = parseRawTriple(&token);
|
||||
// printf("v = %d, %d, %d\n", vi.v_idx, vi.vn_idx, vi.vt_idx);
|
||||
// if (callback.index_cb) {
|
||||
// callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
|
||||
//}
|
||||
// size_t n = strspn(token, " \t\r");
|
||||
// token += n;
|
||||
index_t vi = parseRawTriple(&token);
|
||||
skip_space_and_cr(&token);
|
||||
|
||||
f->push_back(vi);
|
||||
@@ -1034,9 +1132,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
command->type = COMMAND_F;
|
||||
|
||||
if (triangulate) {
|
||||
vertex_index i0 = f[0];
|
||||
vertex_index i1(-1);
|
||||
vertex_index i2 = f[1];
|
||||
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;
|
||||
@@ -1153,31 +1251,33 @@ static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
size_t len, const LoadOption& option)
|
||||
{
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
std::vector<material_t> *materials, const char *buf, size_t len,
|
||||
const LoadOption &option) {
|
||||
attrib->vertices.clear();
|
||||
attrib->normals.clear();
|
||||
attrib->texcoords.clear();
|
||||
attrib->faces.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;
|
||||
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<int>(num_threads), kMaxThreads));
|
||||
(std::max)(1, (std::min)(static_cast<int>(num_threads), kMaxThreads));
|
||||
|
||||
if (option.verbose) {
|
||||
std::cout << "# of threads = " << num_threads << std::endl;
|
||||
}
|
||||
if (option.verbose) {
|
||||
std::cout << "# of threads = " << num_threads << std::endl;
|
||||
}
|
||||
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::vector<LineInfo, lt::allocator<LineInfo> > line_infos[kMaxThreads];
|
||||
std::vector<LineInfo, lfpAlloc::lfpAllocator<LineInfo> >
|
||||
line_infos[kMaxThreads];
|
||||
for (size_t t = 0; t < static_cast<size_t>(num_threads); t++) {
|
||||
// Pre allocate enough memory. len / 128 / num_threads is just a heuristic
|
||||
// value.
|
||||
@@ -1201,7 +1301,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
for (size_t t = 0; t < static_cast<size_t>(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);
|
||||
auto end_idx = (std::min)((t + 1) * chunk_size, len - 1);
|
||||
if (t == static_cast<size_t>((num_threads - 1))) {
|
||||
end_idx = len - 1;
|
||||
}
|
||||
@@ -1231,7 +1331,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
|
||||
// Find extra line which spand across chunk boundary.
|
||||
if ((t < num_threads) && (buf[end_idx - 1] != '\n')) {
|
||||
auto extra_span_idx = std::min(end_idx - 1 + chunk_size, len - 1);
|
||||
auto extra_span_idx = (std::min)(end_idx - 1 + chunk_size, len);
|
||||
for (size_t i = end_idx; i < extra_span_idx; i++) {
|
||||
if (is_line_ending(buf, i, extra_span_idx)) {
|
||||
LineInfo info;
|
||||
@@ -1304,7 +1404,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
command_count[t].num_vt++;
|
||||
} else if (command.type == COMMAND_F) {
|
||||
command_count[t].num_f += command.f.size();
|
||||
command_count[t].num_faces++;
|
||||
command_count[t].num_indices += command.f_num_verts.size();
|
||||
}
|
||||
|
||||
if (command.type == COMMAND_MTLLIB) {
|
||||
@@ -1329,7 +1429,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
}
|
||||
|
||||
std::map<std::string, int> material_map;
|
||||
std::vector<material_t> materials;
|
||||
|
||||
// Load material(if exits)
|
||||
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
|
||||
@@ -1341,10 +1440,12 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
// 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);
|
||||
LoadMtl(&material_map, materials, &ifs);
|
||||
|
||||
// std::cout << "maetrials = " << materials.size() << std::endl;
|
||||
|
||||
@@ -1368,14 +1469,15 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
size_t num_vn = 0;
|
||||
size_t num_vt = 0;
|
||||
size_t num_f = 0;
|
||||
size_t num_faces = 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_faces += command_count[t].num_faces;
|
||||
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;
|
||||
@@ -1389,9 +1491,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
attrib->vertices.resize(num_v * 3);
|
||||
attrib->normals.resize(num_vn * 3);
|
||||
attrib->texcoords.resize(num_vt * 2);
|
||||
attrib->faces.resize(num_f);
|
||||
attrib->face_num_verts.resize(num_faces);
|
||||
attrib->material_ids.resize(num_faces);
|
||||
attrib->indices.resize(num_f);
|
||||
attrib->face_num_verts.resize(num_indices);
|
||||
attrib->material_ids.resize(num_indices);
|
||||
|
||||
size_t v_offsets[kMaxThreads];
|
||||
size_t n_offsets[kMaxThreads];
|
||||
@@ -1410,19 +1512,19 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
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_faces;
|
||||
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices;
|
||||
}
|
||||
|
||||
StackVector<std::thread, 16> workers;
|
||||
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
int material_id = -1; // -1 = default unknown material.
|
||||
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];
|
||||
int material_id = -1; // -1 = default unknown material.
|
||||
|
||||
for (size_t i = 0; i < commands[t].size(); i++) {
|
||||
if (commands[t][i].type == COMMAND_EMPTY) {
|
||||
@@ -1456,17 +1558,21 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
t_count++;
|
||||
} else if (commands[t][i].type == COMMAND_F) {
|
||||
for (size_t k = 0; k < commands[t][i].f.size(); k++) {
|
||||
vertex_index &vi = commands[t][i].f[k];
|
||||
int v_idx = fixIndex(vi.v_idx, v_count);
|
||||
int vn_idx = fixIndex(vi.vn_idx, n_count);
|
||||
int vt_idx = fixIndex(vi.vt_idx, t_count);
|
||||
attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
|
||||
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->material_ids[face_count + k] = material_id;
|
||||
attrib->face_num_verts[face_count + k] =
|
||||
commands[t][i].f_num_verts[k];
|
||||
}
|
||||
attrib->material_ids[face_count] = material_id;
|
||||
attrib->face_num_verts[face_count] = commands[t][i].f.size();
|
||||
|
||||
f_count += commands[t][i].f.size();
|
||||
face_count++;
|
||||
face_count += commands[t][i].f_num_verts.size();
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -1475,7 +1581,19 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
for (size_t t = 0; t < workers->size(); t++) {
|
||||
workers[t].join();
|
||||
}
|
||||
|
||||
if(material_map.size()>1&& num_threads>1) {
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
size_t face_count = face_offsets[t];
|
||||
if (-1 == attrib->material_ids[face_count]) {
|
||||
int prev_material_id = attrib->material_ids[face_count - 1];
|
||||
size_t max_face_offset = (t == num_threads - 1) ? attrib->material_ids.size() : face_offsets[t + 1];
|
||||
for (int i = face_count; i<max_face_offset; ++i) {
|
||||
if (attrib->material_ids[i] != -1) break;
|
||||
attrib->material_ids[i] = prev_material_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
ms_merge = t_end - t_start;
|
||||
}
|
||||
@@ -1556,26 +1674,25 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
}
|
||||
|
||||
std::chrono::duration<double, std::milli> 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->faces.size() << std::endl;
|
||||
std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
|
||||
std::cout << "# of shapes = " << shapes->size() << std::endl;
|
||||
}
|
||||
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;
|
||||
}
|
||||
#endif // TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||
|
||||
} // namespace tinyobj_opt
|
||||
|
||||
#endif // TINOBJ_LOADER_OPT_H_
|
||||
#endif // TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||
|
||||
@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
|
||||
}
|
||||
|
||||
static void vcopy(const float *v1, float *v2) {
|
||||
register int i;
|
||||
int i;
|
||||
for (i = 0; i < 3; i++)
|
||||
v2[i] = v1[i];
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_ZSTD)
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
@@ -86,23 +90,40 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
|
||||
const char *mmap_file(size_t *len, const char* filename)
|
||||
{
|
||||
(*len) = 0;
|
||||
#ifdef _WIN64
|
||||
#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);
|
||||
assert(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
assert(fileMapping != 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<size_t>(fileSize.QuadPart);
|
||||
return fileMapViewChar;
|
||||
|
||||
LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
auto fileMapViewChar = (const char*)fileMapView;
|
||||
assert(fileMapView != NULL);
|
||||
#else
|
||||
|
||||
FILE* f = fopen(filename, "r" );
|
||||
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;
|
||||
@@ -110,29 +131,29 @@ const char *mmap_file(size_t *len, const char* filename)
|
||||
fd = open (filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror ("open");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (fstat (fd, &sb) == -1) {
|
||||
perror ("fstat");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!S_ISREG (sb.st_mode)) {
|
||||
fprintf (stderr, "%s is not a file\n", "lineitem.tbl");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p = (char*)mmap (0, fileSize, PROT_READ, MAP_SHARED, fd, 0);
|
||||
|
||||
if (p == MAP_FAILED) {
|
||||
perror ("mmap");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (close (fd) == -1) {
|
||||
perror ("close");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*len) = fileSize;
|
||||
@@ -182,6 +203,71 @@ bool gz_load(std::vector<char>* buf, const char* filename)
|
||||
#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<char>* 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)
|
||||
{
|
||||
|
||||
@@ -204,9 +290,24 @@ const char* get_file_data(size_t *len, const char* filename)
|
||||
data_len = buf.size();
|
||||
}
|
||||
|
||||
} else if (strcmp(ext, ".zst") == 0) {
|
||||
printf("zstd\n");
|
||||
// Zstandard data.
|
||||
|
||||
std::vector<char> buf;
|
||||
bool ret = zstd_load(&buf, filename);
|
||||
|
||||
if (ret) {
|
||||
char *p = static_cast<char*>(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;
|
||||
@@ -214,26 +315,43 @@ const char* get_file_data(size_t *len, const char* filename)
|
||||
}
|
||||
|
||||
|
||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads)
|
||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose)
|
||||
{
|
||||
#if 1
|
||||
tinyobj_opt::attrib_t attrib;
|
||||
std::vector<tinyobj_opt::shape_t> shapes;
|
||||
std::vector<tinyobj_opt::material_t> 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;
|
||||
}
|
||||
printf("filesize: %d\n", (int)data_len);
|
||||
auto load_t_end = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double, std::milli> 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;
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
||||
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<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
//std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl;
|
||||
//std::cout << "normals.size() = " << attrib.normals.size() << std::endl;
|
||||
|
||||
{
|
||||
DrawObject o;
|
||||
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
||||
@@ -241,15 +359,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
|
||||
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
|
||||
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
|
||||
tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
|
||||
tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
|
||||
tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2];
|
||||
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.v_idx;
|
||||
int f1 = idx1.v_idx;
|
||||
int f2 = idx2.v_idx;
|
||||
int f0 = idx0.vertex_index;
|
||||
int f1 = idx1.vertex_index;
|
||||
int f2 = idx2.vertex_index;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
@@ -268,19 +386,24 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
float n[3][3];
|
||||
|
||||
if (attrib.normals.size() > 0) {
|
||||
int f0 = idx0.vn_idx;
|
||||
int f1 = idx1.vn_idx;
|
||||
int f2 = idx2.vn_idx;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
assert(3*f0+2 < attrib.normals.size());
|
||||
assert(3*f1+2 < attrib.normals.size());
|
||||
assert(3*f2+2 < attrib.normals.size());
|
||||
for (int k = 0; k < 3; k++) {
|
||||
n[0][k] = attrib.normals[3*f0+k];
|
||||
n[1][k] = attrib.normals[3*f1+k];
|
||||
n[2][k] = attrib.normals[3*f2+k];
|
||||
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
|
||||
@@ -299,7 +422,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
// 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 > 0.0f) {
|
||||
if (len2 > 1.0e-6f) {
|
||||
float len = sqrtf(len2);
|
||||
|
||||
c[0] /= len;
|
||||
@@ -330,9 +453,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||
@@ -499,35 +619,51 @@ static void Init() {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
std::cout << "Needs input.obj\n" << std::endl;
|
||||
std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << 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 = true;
|
||||
benchmark_only = (atoi(argv[3]) > 0) ? true : false;
|
||||
}
|
||||
|
||||
if (argc > 4) {
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
if (benchmark_only) {
|
||||
|
||||
tinyobj_opt::attrib_t attrib;
|
||||
std::vector<tinyobj_opt::shape_t> shapes;
|
||||
std::vector<tinyobj_opt::material_t> 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;
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
||||
option.verbose = true;
|
||||
|
||||
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -569,7 +705,7 @@ int main(int argc, char **argv)
|
||||
reshapeFunc(window, width, height);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads)) {
|
||||
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) {
|
||||
printf("failed to load & conv\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include "tiny_obj_loader.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __cplusplus
|
||||
@@ -29,8 +29,15 @@ extern "C" {
|
||||
#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:
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
typedef DWORD time_t;
|
||||
|
||||
@@ -58,7 +65,8 @@ public:
|
||||
static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000);
|
||||
}
|
||||
time_t usec() {
|
||||
return this->sec() * 1000000 + static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
|
||||
return this->sec() * 1000000 +
|
||||
static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
|
||||
}
|
||||
time_t current() {
|
||||
struct timeval t;
|
||||
@@ -66,7 +74,7 @@ public:
|
||||
return static_cast<time_t>(t.tv_sec * 1000 + t.tv_usec);
|
||||
}
|
||||
|
||||
#else // C timer
|
||||
#else // C timer
|
||||
// using namespace std;
|
||||
typedef clock_t time_t;
|
||||
|
||||
@@ -81,7 +89,7 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
DWORD t_[2];
|
||||
#else
|
||||
@@ -94,96 +102,103 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials)
|
||||
{
|
||||
static void PrintInfo(const tinyobj::attrib_t& attrib,
|
||||
const std::vector<tinyobj::shape_t>& shapes,
|
||||
const std::vector<tinyobj::material_t>& 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 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<long>(v),
|
||||
static_cast<const double>(attrib.vertices[3*v+0]),
|
||||
static_cast<const double>(attrib.vertices[3*v+1]),
|
||||
static_cast<const double>(attrib.vertices[3*v+2]));
|
||||
static_cast<const double>(attrib.vertices[3 * v + 0]),
|
||||
static_cast<const double>(attrib.vertices[3 * v + 1]),
|
||||
static_cast<const double>(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<long>(v),
|
||||
static_cast<const double>(attrib.normals[3*v+0]),
|
||||
static_cast<const double>(attrib.normals[3*v+1]),
|
||||
static_cast<const double>(attrib.normals[3*v+2]));
|
||||
static_cast<const double>(attrib.normals[3 * v + 0]),
|
||||
static_cast<const double>(attrib.normals[3 * v + 1]),
|
||||
static_cast<const double>(attrib.normals[3 * v + 2]));
|
||||
}
|
||||
|
||||
for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) {
|
||||
printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v),
|
||||
static_cast<const double>(attrib.texcoords[2*v+0]),
|
||||
static_cast<const double>(attrib.texcoords[2*v+1]));
|
||||
static_cast<const double>(attrib.texcoords[2 * v + 0]),
|
||||
static_cast<const double>(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<long>(i), shapes[i].name.c_str());
|
||||
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.indices.size()));
|
||||
printf("shape[%ld].name = %s\n", static_cast<long>(i),
|
||||
shapes[i].name.c_str());
|
||||
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i),
|
||||
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
|
||||
|
||||
size_t index_offset = 0;
|
||||
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.material_ids.size());
|
||||
|
||||
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
|
||||
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
|
||||
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
|
||||
|
||||
// For each face
|
||||
// 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];
|
||||
size_t fnum = shapes[i].mesh.num_face_vertices[f];
|
||||
|
||||
printf(" face[%ld].fnum = %ld\n", static_cast<long>(f), static_cast<unsigned long>(fnum));
|
||||
printf(" face[%ld].fnum = %ld\n", static_cast<long>(f),
|
||||
static_cast<unsigned long>(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<long>(f), static_cast<long>(v), idx.vertex_index, idx.normal_index, idx.texcoord_index);
|
||||
}
|
||||
// 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<long>(f),
|
||||
static_cast<long>(v), idx.vertex_index, idx.normal_index,
|
||||
idx.texcoord_index);
|
||||
}
|
||||
|
||||
printf(" face[%ld].material_id = %d\n", static_cast<long>(f), shapes[i].mesh.material_ids[f]);
|
||||
printf(" face[%ld].material_id = %d\n", static_cast<long>(f),
|
||||
shapes[i].mesh.material_ids[f]);
|
||||
|
||||
index_offset += fnum;
|
||||
index_offset += fnum;
|
||||
}
|
||||
|
||||
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.tags.size()));
|
||||
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i),
|
||||
static_cast<unsigned long>(shapes[i].mesh.tags.size()));
|
||||
for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
|
||||
printf(" tag[%ld] = %s ", static_cast<long>(t), shapes[i].mesh.tags[t].name.c_str());
|
||||
printf(" tag[%ld] = %s ", static_cast<long>(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<long>(shapes[i].mesh.tags[t].intValues[j]));
|
||||
if (j < (shapes[i].mesh.tags[t].intValues.size()-1))
|
||||
{
|
||||
printf(", ");
|
||||
}
|
||||
for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) {
|
||||
printf("%ld", static_cast<long>(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<const double>(shapes[i].mesh.tags[t].floatValues[j]));
|
||||
if (j < (shapes[i].mesh.tags[t].floatValues.size()-1))
|
||||
{
|
||||
printf(", ");
|
||||
}
|
||||
for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) {
|
||||
printf("%f", static_cast<const double>(
|
||||
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(", ");
|
||||
}
|
||||
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");
|
||||
@@ -191,25 +206,60 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < materials.size(); i++) {
|
||||
printf("material[%ld].name = %s\n", static_cast<long>(i), materials[i].name.c_str());
|
||||
printf(" material.Ka = (%f, %f ,%f)\n", static_cast<const double>(materials[i].ambient[0]), static_cast<const double>(materials[i].ambient[1]), static_cast<const double>(materials[i].ambient[2]));
|
||||
printf(" material.Kd = (%f, %f ,%f)\n", static_cast<const double>(materials[i].diffuse[0]), static_cast<const double>(materials[i].diffuse[1]), static_cast<const double>(materials[i].diffuse[2]));
|
||||
printf(" material.Ks = (%f, %f ,%f)\n", static_cast<const double>(materials[i].specular[0]), static_cast<const double>(materials[i].specular[1]), static_cast<const double>(materials[i].specular[2]));
|
||||
printf(" material.Tr = (%f, %f ,%f)\n", static_cast<const double>(materials[i].transmittance[0]), static_cast<const double>(materials[i].transmittance[1]), static_cast<const double>(materials[i].transmittance[2]));
|
||||
printf(" material.Ke = (%f, %f ,%f)\n", static_cast<const double>(materials[i].emission[0]), static_cast<const double>(materials[i].emission[1]), static_cast<const double>(materials[i].emission[2]));
|
||||
printf(" material.Ns = %f\n", static_cast<const double>(materials[i].shininess));
|
||||
printf("material[%ld].name = %s\n", static_cast<long>(i),
|
||||
materials[i].name.c_str());
|
||||
printf(" material.Ka = (%f, %f ,%f)\n",
|
||||
static_cast<const double>(materials[i].ambient[0]),
|
||||
static_cast<const double>(materials[i].ambient[1]),
|
||||
static_cast<const double>(materials[i].ambient[2]));
|
||||
printf(" material.Kd = (%f, %f ,%f)\n",
|
||||
static_cast<const double>(materials[i].diffuse[0]),
|
||||
static_cast<const double>(materials[i].diffuse[1]),
|
||||
static_cast<const double>(materials[i].diffuse[2]));
|
||||
printf(" material.Ks = (%f, %f ,%f)\n",
|
||||
static_cast<const double>(materials[i].specular[0]),
|
||||
static_cast<const double>(materials[i].specular[1]),
|
||||
static_cast<const double>(materials[i].specular[2]));
|
||||
printf(" material.Tr = (%f, %f ,%f)\n",
|
||||
static_cast<const double>(materials[i].transmittance[0]),
|
||||
static_cast<const double>(materials[i].transmittance[1]),
|
||||
static_cast<const double>(materials[i].transmittance[2]));
|
||||
printf(" material.Ke = (%f, %f ,%f)\n",
|
||||
static_cast<const double>(materials[i].emission[0]),
|
||||
static_cast<const double>(materials[i].emission[1]),
|
||||
static_cast<const double>(materials[i].emission[2]));
|
||||
printf(" material.Ns = %f\n",
|
||||
static_cast<const double>(materials[i].shininess));
|
||||
printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior));
|
||||
printf(" material.dissolve = %f\n", static_cast<const double>(materials[i].dissolve));
|
||||
printf(" material.illum = %d\n", materials[i].illum);
|
||||
printf(" material.dissolve = %f\n",
|
||||
static_cast<const double>(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_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<const double>(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());
|
||||
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
|
||||
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
|
||||
printf(" <<PBR>>\n");
|
||||
printf(" material.Pr = %f\n", static_cast<const double>(materials[i].roughness));
|
||||
printf(" material.Pm = %f\n", static_cast<const double>(materials[i].metallic));
|
||||
printf(" material.Ps = %f\n", static_cast<const double>(materials[i].sheen));
|
||||
printf(" material.Pc = %f\n", static_cast<const double>(materials[i].clearcoat_thickness));
|
||||
printf(" material.Pcr = %f\n", static_cast<const double>(materials[i].clearcoat_thickness));
|
||||
printf(" material.aniso = %f\n", static_cast<const double>(materials[i].anisotropy));
|
||||
printf(" material.anisor = %f\n", static_cast<const double>(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<std::string, std::string>::const_iterator it(
|
||||
materials[i].unknown_parameter.begin());
|
||||
std::map<std::string, std::string>::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());
|
||||
@@ -218,12 +268,8 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
TestLoadObj(
|
||||
const char* filename,
|
||||
const char* basepath = NULL,
|
||||
bool triangulate = true)
|
||||
{
|
||||
static bool TestLoadObj(const char* filename, const char* basepath = NULL,
|
||||
bool triangulate = true) {
|
||||
std::cout << "Loading " << filename << std::endl;
|
||||
|
||||
tinyobj::attrib_t attrib;
|
||||
@@ -233,7 +279,8 @@ TestLoadObj(
|
||||
timerutil t;
|
||||
t.start();
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate);
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
|
||||
basepath, triangulate);
|
||||
t.end();
|
||||
printf("Parsing time: %lu [msecs]\n", t.msec());
|
||||
|
||||
@@ -251,101 +298,101 @@ TestLoadObj(
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
TestStreamLoadObj()
|
||||
{
|
||||
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";
|
||||
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");
|
||||
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<material_t>* materials,
|
||||
std::map<std::string, int>* matMap,
|
||||
std::string* err)
|
||||
{
|
||||
(void)matId;
|
||||
(void)err;
|
||||
LoadMtl(matMap, materials, &m_matSStream);
|
||||
return true;
|
||||
}
|
||||
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<material_t>* materials,
|
||||
std::map<std::string, int>* matMap,
|
||||
std::string* err) {
|
||||
(void)matId;
|
||||
std::string warning;
|
||||
LoadMtl(matMap, materials, &m_matSStream, &warning);
|
||||
|
||||
private:
|
||||
std::stringstream m_matSStream;
|
||||
};
|
||||
if (!warning.empty()) {
|
||||
if (err) {
|
||||
(*err) += warning;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::stringstream m_matSStream;
|
||||
};
|
||||
|
||||
MaterialStringStreamReader matSSReader(matStream);
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader);
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream,
|
||||
&matSSReader);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
@@ -360,11 +407,7 @@ std::string matStream(
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(
|
||||
int argc,
|
||||
char **argv)
|
||||
{
|
||||
int main(int argc, char** argv) {
|
||||
if (argc > 1) {
|
||||
const char* basepath = "models/";
|
||||
if (argc > 2) {
|
||||
@@ -372,10 +415,11 @@ main(
|
||||
}
|
||||
assert(true == TestLoadObj(argv[1], basepath));
|
||||
} else {
|
||||
//assert(true == TestLoadObj("cornell_box.obj"));
|
||||
//assert(true == TestLoadObj("cube.obj"));
|
||||
// assert(true == TestLoadObj("cornell_box.obj"));
|
||||
// assert(true == TestLoadObj("cube.obj"));
|
||||
assert(true == TestStreamLoadObj());
|
||||
assert(true == TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
|
||||
assert(true ==
|
||||
TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
31
models/cube-vertexcol.obj
Normal file
31
models/cube-vertexcol.obj
Normal file
@@ -0,0 +1,31 @@
|
||||
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
|
||||
23
models/issue-138.mtl
Normal file
23
models/issue-138.mtl
Normal file
@@ -0,0 +1,23 @@
|
||||
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
|
||||
51
models/issue-138.obj
Normal file
51
models/issue-138.obj
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
# 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
|
||||
2
models/issue-140-zero-face-idx.mtl
Normal file
2
models/issue-140-zero-face-idx.mtl
Normal file
@@ -0,0 +1,2 @@
|
||||
newmtl main
|
||||
Kd 1 1 1
|
||||
17
models/issue-140-zero-face-idx.obj
Normal file
17
models/issue-140-zero-face-idx.obj
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
37
models/issue-161-inconsistent-f.obj
Normal file
37
models/issue-161-inconsistent-f.obj
Normal file
@@ -0,0 +1,37 @@
|
||||
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
|
||||
23
models/issue-162-smoothing-group.mtl
Normal file
23
models/issue-162-smoothing-group.mtl
Normal file
@@ -0,0 +1,23 @@
|
||||
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
|
||||
51
models/issue-162-smoothing-group.obj
Normal file
51
models/issue-162-smoothing-group.obj
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
# 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
|
||||
6
models/issue-92.mtl
Normal file
6
models/issue-92.mtl
Normal file
@@ -0,0 +1,6 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
map_Kd tmp.png
|
||||
|
||||
7
models/issue-92.obj
Normal file
7
models/issue-92.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
5
models/issue-95-2.mtl
Normal file
5
models/issue-95-2.mtl
Normal file
@@ -0,0 +1,5 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
Tf 0.1 0.2 0.3
|
||||
7
models/issue-95-2.obj
Normal file
7
models/issue-95-2.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
5
models/issue-95.mtl
Normal file
5
models/issue-95.mtl
Normal file
@@ -0,0 +1,5 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
Kt 0.1 0.2 0.3
|
||||
7
models/issue-95.obj
Normal file
7
models/issue-95.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
10
models/map-bump.mtl
Normal file
10
models/map-bump.mtl
Normal file
@@ -0,0 +1,10 @@
|
||||
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
|
||||
817
models/map-bump.obj
Normal file
817
models/map-bump.obj
Normal file
@@ -0,0 +1,817 @@
|
||||
# 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
|
||||
6
models/mtllib-multiple-files-issue-112.mtl
Normal file
6
models/mtllib-multiple-files-issue-112.mtl
Normal file
@@ -0,0 +1,6 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
map_Kd tmp.png
|
||||
|
||||
7
models/mtllib-multiple-files-issue-112.obj
Normal file
7
models/mtllib-multiple-files-issue-112.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
7
models/norm-texopt.mtl
Normal file
7
models/norm-texopt.mtl
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
|
||||
7
models/norm-texopt.obj
Normal file
7
models/norm-texopt.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
19
models/pbr-mat-ext.mtl
Normal file
19
models/pbr-mat-ext.mtl
Normal file
@@ -0,0 +1,19 @@
|
||||
# .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
|
||||
|
||||
10
models/pbr-mat-ext.obj
Normal file
10
models/pbr-mat-ext.obj
Normal file
@@ -0,0 +1,10 @@
|
||||
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
|
||||
25
models/refl.mtl
Normal file
25
models/refl.mtl
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
32
models/refl.obj
Normal file
32
models/refl.obj
Normal file
@@ -0,0 +1,32 @@
|
||||
# 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
|
||||
28
models/texture-filename-with-whitespace.mtl
Normal file
28
models/texture-filename-with-whitespace.mtl
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
31
models/texture-filename-with-whitespace.obj
Normal file
31
models/texture-filename-with-whitespace.obj
Normal file
@@ -0,0 +1,31 @@
|
||||
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
|
||||
36
models/texture-options-issue-85.mtl
Normal file
36
models/texture-options-issue-85.mtl
Normal file
@@ -0,0 +1,36 @@
|
||||
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
|
||||
7
models/texture-options-issue-85.obj
Normal file
7
models/texture-options-issue-85.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
13
models/tr-and-d-issue-43.mtl
Normal file
13
models/tr-and-d-issue-43.mtl
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
817
models/tr-and-d-issue-43.obj
Normal file
817
models/tr-and-d-issue-43.obj
Normal file
@@ -0,0 +1,817 @@
|
||||
# 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
|
||||
30
models/usemtl-issue-104.obj
Normal file
30
models/usemtl-issue-104.obj
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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
|
||||
2
python/TODO.md
Normal file
2
python/TODO.md
Normal file
@@ -0,0 +1,2 @@
|
||||
* PBR material
|
||||
* Define index_t struct
|
||||
282
python/main.cpp
282
python/main.cpp
@@ -1,160 +1,206 @@
|
||||
//python3 module for tinyobjloader
|
||||
// python2/3 module for tinyobjloader
|
||||
//
|
||||
//usage:
|
||||
// usage:
|
||||
// import tinyobjloader as tol
|
||||
// model = tol.LoadObj(name)
|
||||
// print(model["shapes"])
|
||||
// print(model["materials"]
|
||||
// note:
|
||||
// `shape.mesh.index_t` is represented as flattened array: (vertex_index, normal_index, texcoord_index) * num_faces
|
||||
|
||||
#include <Python.h>
|
||||
#include <vector>
|
||||
#include "../tiny_obj_loader.h"
|
||||
|
||||
typedef std::vector<double> vectd;
|
||||
typedef std::vector<int> vecti;
|
||||
|
||||
PyObject*
|
||||
pyTupleFromfloat3 (float array[3])
|
||||
{
|
||||
int i;
|
||||
PyObject* tuple = PyTuple_New(3);
|
||||
PyObject* pyTupleFromfloat3(float array[3]) {
|
||||
int i;
|
||||
PyObject* tuple = PyTuple_New(3);
|
||||
|
||||
for(i=0; i<=2 ; i++){
|
||||
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
|
||||
}
|
||||
for (i = 0; i <= 2; i++) {
|
||||
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
|
||||
}
|
||||
|
||||
return tuple;
|
||||
return tuple;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
|
||||
static PyObject*
|
||||
pyLoadObj(PyObject* self, PyObject* args)
|
||||
{
|
||||
PyObject *rtndict, *pyshapes, *pymaterials,
|
||||
*current, *meshobj;
|
||||
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
||||
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
|
||||
|
||||
char const* filename;
|
||||
char *current_name;
|
||||
vectd vect;
|
||||
char const* current_name;
|
||||
char const* filename;
|
||||
vectd vect;
|
||||
std::vector<tinyobj::index_t> indices;
|
||||
std::vector<unsigned char> face_verts;
|
||||
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "s", &filename))
|
||||
return NULL;
|
||||
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
|
||||
|
||||
std::string err;
|
||||
tinyobj::LoadObj(shapes, materials, err, filename);
|
||||
std::string err;
|
||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
|
||||
|
||||
pyshapes = PyDict_New();
|
||||
pymaterials = PyDict_New();
|
||||
rtndict = PyDict_New();
|
||||
pyshapes = PyDict_New();
|
||||
pymaterials = PyDict_New();
|
||||
pymaterial_indices = PyList_New(0);
|
||||
rtndict = PyDict_New();
|
||||
|
||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ;
|
||||
shape != shapes.end(); shape++)
|
||||
{
|
||||
meshobj = PyDict_New();
|
||||
tinyobj::mesh_t cm = (*shape).mesh;
|
||||
attribobj = PyDict_New();
|
||||
|
||||
for (int i = 0; i <= 4; i++ )
|
||||
{
|
||||
current = PyList_New(0);
|
||||
for (int i = 0; i <= 2; i++) {
|
||||
current = PyList_New(0);
|
||||
|
||||
switch(i) {
|
||||
|
||||
case 0:
|
||||
current_name = "positions";
|
||||
vect = vectd(cm.positions.begin(), cm.positions.end()); break;
|
||||
case 1:
|
||||
current_name = "normals";
|
||||
vect = vectd(cm.normals.begin(), cm.normals.end()); break;
|
||||
case 2:
|
||||
current_name = "texcoords";
|
||||
vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break;
|
||||
case 3:
|
||||
current_name = "indicies";
|
||||
vect = vectd(cm.indices.begin(), cm.indices.end()); break;
|
||||
case 4:
|
||||
current_name = "material_ids";
|
||||
vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); break;
|
||||
|
||||
}
|
||||
|
||||
for (vectd::iterator it = vect.begin() ;
|
||||
it != vect.end(); it++)
|
||||
{
|
||||
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, current_name, current);
|
||||
|
||||
}
|
||||
|
||||
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
||||
switch (i) {
|
||||
case 0:
|
||||
current_name = "vertices";
|
||||
vect = vectd(attrib.vertices.begin(), attrib.vertices.end());
|
||||
break;
|
||||
case 1:
|
||||
current_name = "normals";
|
||||
vect = vectd(attrib.normals.begin(), attrib.normals.end());
|
||||
break;
|
||||
case 2:
|
||||
current_name = "texcoords";
|
||||
vect = vectd(attrib.texcoords.begin(), attrib.texcoords.end());
|
||||
break;
|
||||
}
|
||||
|
||||
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin() ;
|
||||
mat != materials.end(); mat++)
|
||||
{
|
||||
PyObject *matobj = PyDict_New();
|
||||
PyObject *unknown_parameter = PyDict_New();
|
||||
|
||||
for (std::map<std::string, std::string>::iterator p = (*mat).unknown_parameter.begin() ;
|
||||
p != (*mat).unknown_parameter.end(); ++p)
|
||||
{
|
||||
PyDict_SetItemString(unknown_parameter, p->first.c_str(), PyUnicode_FromString(p->second.c_str()));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess));
|
||||
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
|
||||
PyDict_SetItemString(matobj, "dissolve", PyFloat_FromDouble((*mat).dissolve));
|
||||
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
|
||||
PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "specular_highlight_texname", PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "bump_texname", PyUnicode_FromString((*mat).bump_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "displacement_texname", PyUnicode_FromString((*mat).displacement_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "alpha_texname", PyUnicode_FromString((*mat).alpha_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient));
|
||||
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
|
||||
PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular));
|
||||
PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance));
|
||||
PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission));
|
||||
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
||||
|
||||
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
|
||||
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
|
||||
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
||||
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
||||
PyDict_SetItemString(attribobj, current_name, current);
|
||||
}
|
||||
|
||||
return rtndict;
|
||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
|
||||
shape != shapes.end(); shape++) {
|
||||
meshobj = PyDict_New();
|
||||
tinyobj::mesh_t cm = (*shape).mesh;
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.indices.size(); i++) {
|
||||
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
|
||||
// ...
|
||||
PyList_Insert(current, 3 * i + 0,
|
||||
PyLong_FromLong(cm.indices[i].vertex_index));
|
||||
PyList_Insert(current, 3 * i + 1,
|
||||
PyLong_FromLong(cm.indices[i].normal_index));
|
||||
PyList_Insert(current, 3 * i + 2,
|
||||
PyLong_FromLong(cm.indices[i].texcoord_index));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "indices", current);
|
||||
}
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
|
||||
// Widen data type to long.
|
||||
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i]));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "num_face_vertices", current);
|
||||
}
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.material_ids.size(); i++) {
|
||||
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "material_ids", current);
|
||||
}
|
||||
|
||||
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
||||
}
|
||||
|
||||
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
|
||||
mat != materials.end(); mat++) {
|
||||
PyObject* matobj = PyDict_New();
|
||||
PyObject* unknown_parameter = PyDict_New();
|
||||
|
||||
for (std::map<std::string, std::string>::iterator p =
|
||||
mat->unknown_parameter.begin();
|
||||
p != mat->unknown_parameter.end(); ++p) {
|
||||
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
|
||||
PyUnicode_FromString(p->second.c_str()));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(matobj, "shininess",
|
||||
PyFloat_FromDouble(mat->shininess));
|
||||
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior));
|
||||
PyDict_SetItemString(matobj, "dissolve",
|
||||
PyFloat_FromDouble(mat->dissolve));
|
||||
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum));
|
||||
PyDict_SetItemString(matobj, "ambient_texname",
|
||||
PyUnicode_FromString(mat->ambient_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "diffuse_texname",
|
||||
PyUnicode_FromString(mat->diffuse_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "specular_texname",
|
||||
PyUnicode_FromString(mat->specular_texname.c_str()));
|
||||
PyDict_SetItemString(
|
||||
matobj, "specular_highlight_texname",
|
||||
PyUnicode_FromString(mat->specular_highlight_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "bump_texname",
|
||||
PyUnicode_FromString(mat->bump_texname.c_str()));
|
||||
PyDict_SetItemString(
|
||||
matobj, "displacement_texname",
|
||||
PyUnicode_FromString(mat->displacement_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "alpha_texname",
|
||||
PyUnicode_FromString(mat->alpha_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
|
||||
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
|
||||
PyDict_SetItemString(matobj, "specular",
|
||||
pyTupleFromfloat3(mat->specular));
|
||||
PyDict_SetItemString(matobj, "transmittance",
|
||||
pyTupleFromfloat3(mat->transmittance));
|
||||
PyDict_SetItemString(matobj, "emission",
|
||||
pyTupleFromfloat3(mat->emission));
|
||||
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
||||
|
||||
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
|
||||
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str()));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
||||
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
||||
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
|
||||
PyDict_SetItemString(rtndict, "attribs", attribobj);
|
||||
|
||||
return rtndict;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef mMethods[] = {
|
||||
|
||||
{"LoadObj", pyLoadObj, METH_VARARGS},
|
||||
{NULL, NULL, 0, NULL}
|
||||
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
|
||||
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"tinyobjloader",
|
||||
NULL,
|
||||
-1,
|
||||
mMethods
|
||||
};
|
||||
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
|
||||
NULL, -1, mMethods};
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_tinyobjloader(void)
|
||||
{
|
||||
return PyModule_Create(&moduledef);
|
||||
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
|
||||
return PyModule_Create(&moduledef);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
PyMODINIT_FUNC inittinyobjloader(void) {
|
||||
Py_InitModule3("tinyobjloader", mMethods, NULL);
|
||||
}
|
||||
|
||||
#endif // PY_MAJOR_VERSION >= 3
|
||||
|
||||
}
|
||||
|
||||
432
tests/tester.cc
432
tests/tester.cc
@@ -145,6 +145,7 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
||||
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<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
|
||||
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
|
||||
|
||||
@@ -184,6 +185,48 @@ TestLoadObj(
|
||||
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<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &fileStream, materialReader);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << 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()
|
||||
@@ -265,7 +308,8 @@ std::string matStream(
|
||||
{
|
||||
(void)matId;
|
||||
(void)err;
|
||||
LoadMtl(matMap, materials, &m_matSStream);
|
||||
std::string warning;
|
||||
LoadMtl(matMap, materials, &m_matSStream, &warning);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -293,7 +337,7 @@ std::string matStream(
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* gMtlBasePath = "../models";
|
||||
const char* gMtlBasePath = "../models/";
|
||||
|
||||
TEST_CASE("cornell_box", "[Loader]") {
|
||||
|
||||
@@ -319,10 +363,394 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") {
|
||||
REQUIRE(8 == shapes[0].mesh.tags.size());
|
||||
}
|
||||
|
||||
TEST_CASE("pbr", "[Loader]") {
|
||||
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/pbr-mat-ext.obj", gMtlBasePath, /*triangulate*/false);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(0.2 == Approx(materials[0].roughness));
|
||||
REQUIRE(0.3 == Approx(materials[0].metallic));
|
||||
REQUIRE(0.4 == Approx(materials[0].sheen));
|
||||
REQUIRE(0.5 == Approx(materials[0].clearcoat_thickness));
|
||||
REQUIRE(0.6 == Approx(materials[0].clearcoat_roughness));
|
||||
REQUIRE(0.7 == Approx(materials[0].anisotropy));
|
||||
REQUIRE(0.8 == Approx(materials[0].anisotropy_rotation));
|
||||
REQUIRE(0 == materials[0].roughness_texname.compare("roughness.tex"));
|
||||
REQUIRE(0 == materials[0].metallic_texname.compare("metallic.tex"));
|
||||
REQUIRE(0 == materials[0].sheen_texname.compare("sheen.tex"));
|
||||
REQUIRE(0 == materials[0].emissive_texname.compare("emissive.tex"));
|
||||
REQUIRE(0 == materials[0].normal_texname.compare("normalmap.tex"));
|
||||
}
|
||||
|
||||
TEST_CASE("stream_load", "[Stream]") {
|
||||
REQUIRE(true == TestStreamLoadObj());
|
||||
}
|
||||
|
||||
TEST_CASE("stream_load_from_file_skipping_materials", "[Stream]") {
|
||||
REQUIRE(true == TestLoadObjFromPreopenedFile(
|
||||
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/false, /*triangulate*/false));
|
||||
}
|
||||
|
||||
TEST_CASE("stream_load_from_file_with_materials", "[Stream]") {
|
||||
REQUIRE(true == TestLoadObjFromPreopenedFile(
|
||||
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/true, /*triangulate*/false));
|
||||
}
|
||||
|
||||
TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-92.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(0 == materials[0].diffuse_texname.compare("tmp.png"));
|
||||
}
|
||||
|
||||
TEST_CASE("transmittance_filter", "[Issue95]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||
}
|
||||
|
||||
TEST_CASE("transmittance_filter_Tf", "[Issue95-Tf]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95-2.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||
}
|
||||
|
||||
TEST_CASE("transmittance_filter_Kt", "[Issue95-Kt]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||
}
|
||||
|
||||
TEST_CASE("usemtl_at_last_line", "[Issue104]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/usemtl-issue-104.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == shapes.size());
|
||||
}
|
||||
|
||||
TEST_CASE("texture_opts", "[Issue85]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-options-issue-85.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == shapes.size());
|
||||
REQUIRE(3 == materials.size());
|
||||
REQUIRE(0 == materials[0].name.compare("default"));
|
||||
REQUIRE(0 == materials[1].name.compare("bm2"));
|
||||
REQUIRE(0 == materials[2].name.compare("bm3"));
|
||||
REQUIRE(true == materials[0].ambient_texopt.clamp);
|
||||
REQUIRE(0.1 == Approx(materials[0].diffuse_texopt.origin_offset[0]));
|
||||
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[1]));
|
||||
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[2]));
|
||||
REQUIRE(0.1 == Approx(materials[0].specular_texopt.scale[0]));
|
||||
REQUIRE(0.2 == Approx(materials[0].specular_texopt.scale[1]));
|
||||
REQUIRE(1.0 == Approx(materials[0].specular_texopt.scale[2]));
|
||||
REQUIRE(0.1 == Approx(materials[0].specular_highlight_texopt.turbulence[0]));
|
||||
REQUIRE(0.2 == Approx(materials[0].specular_highlight_texopt.turbulence[1]));
|
||||
REQUIRE(0.3 == Approx(materials[0].specular_highlight_texopt.turbulence[2]));
|
||||
REQUIRE(3.0 == Approx(materials[0].bump_texopt.bump_multiplier));
|
||||
|
||||
REQUIRE(0.1 == Approx(materials[1].specular_highlight_texopt.brightness));
|
||||
REQUIRE(0.3 == Approx(materials[1].specular_highlight_texopt.contrast));
|
||||
REQUIRE('r' == materials[1].bump_texopt.imfchan);
|
||||
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_TOP == materials[2].specular_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == materials[2].specular_highlight_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_LEFT == materials[2].ambient_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_RIGHT == materials[2].alpha_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type);
|
||||
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BACK == materials[2].displacement_texopt.type);
|
||||
}
|
||||
|
||||
TEST_CASE("mtllib_multiple_filenames", "[Issue112]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/mtllib-multiple-files-issue-112.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == materials.size());
|
||||
}
|
||||
|
||||
TEST_CASE("tr_and_d", "[Issue43]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/tr-and-d-issue-43.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(2 == materials.size());
|
||||
|
||||
REQUIRE(0.75 == Approx(materials[0].dissolve));
|
||||
REQUIRE(0.75 == Approx(materials[1].dissolve));
|
||||
}
|
||||
|
||||
TEST_CASE("refl", "[refl]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/refl.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
PrintInfo(attrib, shapes, materials);
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(5 == materials.size());
|
||||
|
||||
REQUIRE(materials[0].reflection_texname.compare("reflection.tga") == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("map_Bump", "[bump]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/map-bump.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
PrintInfo(attrib, shapes, materials);
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(2 == materials.size());
|
||||
|
||||
REQUIRE(materials[0].bump_texname.compare("bump.jpg") == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("g_ignored", "[Issue138]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-138.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
PrintInfo(attrib, shapes, materials);
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(2 == shapes.size());
|
||||
REQUIRE(2 == materials.size());
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("vertex-col-ext", "[Issue144]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube-vertexcol.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
//PrintInfo(attrib, shapes, materials);
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE((8 * 3) == attrib.colors.size());
|
||||
|
||||
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 0]));
|
||||
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 1]));
|
||||
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 2]));
|
||||
|
||||
REQUIRE(0 == Approx(attrib.colors[3 * 1 + 0]));
|
||||
REQUIRE(0 == Approx(attrib.colors[3 * 1 + 1]));
|
||||
REQUIRE(1 == Approx(attrib.colors[3 * 1 + 2]));
|
||||
|
||||
REQUIRE(1 == Approx(attrib.colors[3 * 4 + 0]));
|
||||
|
||||
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 0]));
|
||||
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 1]));
|
||||
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 2]));
|
||||
}
|
||||
|
||||
TEST_CASE("norm_texopts", "[norm]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/norm-texopt.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == shapes.size());
|
||||
REQUIRE(1 == materials.size());
|
||||
REQUIRE(3.0 == Approx(materials[0].normal_texopt.bump_multiplier));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("zero-face-idx-value", "[Issue140]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-140-zero-face-idx.obj", gMtlBasePath);
|
||||
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(false == ret);
|
||||
REQUIRE(!err.empty());
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("texture-name-whitespace", "[Issue145]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-filename-with-whitespace.obj", gMtlBasePath);
|
||||
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << "[Issue145] " << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(err.empty());
|
||||
REQUIRE(2 < materials.size());
|
||||
|
||||
REQUIRE(0 == materials[0].diffuse_texname.compare("texture 01.png"));
|
||||
REQUIRE(0 == materials[1].bump_texname.compare("bump 01.png"));
|
||||
REQUIRE(2 == Approx(materials[1].bump_texopt.bump_multiplier));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("smoothing-group", "[Issue162]") {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
|
||||
std::string err;
|
||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-162-smoothing-group.obj", gMtlBasePath);
|
||||
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << "[Issue162] " << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(2 == shapes.size());
|
||||
|
||||
REQUIRE(2 == shapes[0].mesh.smoothing_group_ids.size());
|
||||
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[0]);
|
||||
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[1]);
|
||||
|
||||
REQUIRE(10 == shapes[1].mesh.smoothing_group_ids.size());
|
||||
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[0]);
|
||||
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[1]);
|
||||
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[2]);
|
||||
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[3]);
|
||||
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[4]);
|
||||
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[5]);
|
||||
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[6]);
|
||||
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[7]);
|
||||
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[8]);
|
||||
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[9]);
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
int
|
||||
main(
|
||||
|
||||
1642
tiny_obj_loader.h
1642
tiny_obj_loader.h
File diff suppressed because it is too large
Load Diff
9
tinyobjloader-config.cmake.in
Normal file
9
tinyobjloader-config.cmake.in
Normal file
@@ -0,0 +1,9 @@
|
||||
@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")
|
||||
15
tinyobjloader.pc.in
Normal file
15
tinyobjloader.pc.in
Normal file
@@ -0,0 +1,15 @@
|
||||
# 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}
|
||||
Reference in New Issue
Block a user