├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml ├── src ├── issue_54.obj ├── lex.rs ├── lib.rs ├── mtl.rs ├── obj.rs └── util.rs └── tests └── corrupted_data.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.old 8 | *.bak 9 | *.kate-swp 10 | *.dylib 11 | *.dSYM 12 | *.dll 13 | *.rlib 14 | *.dummy 15 | *.exe 16 | *-test 17 | /bin/main 18 | /bin/test-internal 19 | /bin/test-external 20 | /doc/ 21 | /target/ 22 | /build/ 23 | /.rust/ 24 | rusti.sh 25 | watch.sh 26 | /examples/** 27 | !/examples/*.rs 28 | !/examples/assets/ 29 | !/bin/assets/ 30 | 31 | Cargo.lock 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | notifications: 3 | irc: "irc.mozilla.org#piston-internals" 4 | os: 5 | - linux 6 | - osx 7 | env: 8 | global: 9 | - secure: Cjl/01MsEBnDyRq0o/oQomjyYib7Td5mCDnSvaF7xQkVKj7t/TYWI3r+MRr7Wrbq5zJ+aNzHGrAxO2W63g0DB/LDnbZdMoQhyLBCvbBqsnM0RW9P8CCT7kcZuyaNShjzo64HllwfcC8fZBws/2kOzBIVBF+xJP+ljH0p1p3RQEw= 10 | before_script: 11 | - rustc --version 12 | - cargo -V 13 | script: 14 | - cargo build -v 15 | - cargo test -v 16 | - cargo bench -v 17 | - cargo doc -v 18 | after_success: 19 | - cp -R target/doc doc 20 | - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh 21 | - rm -r doc 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "wavefront_obj" 4 | version = "11.0.0" 5 | authors = [ 6 | "Clark Gaebel ", 7 | ] 8 | keywords = ["3d", "format", "wavefront", "obj", "piston"] 9 | description = "A parser for the Wavefront .obj file format." 10 | license = "MIT" 11 | readme = "README.md" 12 | repository = "https://github.com/PistonDevelopers/wavefront_obj.git" 13 | homepage = "https://github.com/PistonDevelopers/wavefront_obj" 14 | 15 | [dev-dependencies] 16 | proptest = "0.9" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Clark Gaebel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wavefront_obj [![Build Status](https://travis-ci.org/PistonDevelopers/wavefront_obj.svg?branch=master)](https://travis-ci.org/PistonDevelopers/wavefront_obj) 2 | 3 | This is a parser for the wavefront .obj file format. There are informative 4 | error messages with line number reporting. The full file format is not 5 | supported, although patches for adding support for unimplemented parts of the 6 | format are very welcome. 7 | 8 | This is a best-effort attempt to parse the subset of .obj files exported by 9 | blender. 10 | 11 | [How to contribute](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md) 12 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | 3 | fn_args_layout = "Tall" 4 | force_explicit_abi = true 5 | hard_tabs = false 6 | max_width = 100 7 | merge_derives = true 8 | newline_style = "Unix" 9 | remove_nested_parens = true 10 | reorder_imports = true 11 | reorder_modules = true 12 | tab_spaces = 2 13 | use_field_init_shorthand = true 14 | use_small_heuristics = "Default" 15 | use_try_shorthand = true 16 | -------------------------------------------------------------------------------- /src/issue_54.obj: -------------------------------------------------------------------------------- 1 | # WaveFront *.obj file (generated by CINEMA 4D) 2 | 3 | mtllib ./simple-sphere.mtl 4 | 5 | v 0 -100 0 6 | v 0 100 0 7 | v 25.88190460205078 -96.59258270263672 0 8 | v 25 -96.59258270263672 -6.69872999191284 9 | v 22.41438674926758 -96.59258270263672 -12.94095230102539 10 | v 18.30126953125 -96.59258270263672 -18.30126953125 11 | v 12.94095230102539 -96.59258270263672 -22.41438674926758 12 | v 6.69872999191284 -96.59258270263672 -25 13 | v 0 -96.59258270263672 -25.88190460205078 14 | v -6.69872999191284 -96.59258270263672 -25 15 | v -12.94095230102539 -96.59258270263672 -22.41438674926758 16 | v -18.30126953125 -96.59258270263672 -18.30126953125 17 | v -22.41438674926758 -96.59258270263672 -12.94095230102539 18 | v -25 -96.59258270263672 -6.69872999191284 19 | v -25.88190460205078 -96.59258270263672 -0 20 | v -25 -96.59258270263672 6.69872999191284 21 | v -22.41438674926758 -96.59258270263672 12.94095230102539 22 | v -18.30126953125 -96.59258270263672 18.30126953125 23 | v -12.94095230102539 -96.59258270263672 22.41438674926758 24 | v -6.69872999191284 -96.59258270263672 25 25 | v -0 -96.59258270263672 25.88190460205078 26 | v 6.69872999191284 -96.59258270263672 25 27 | v 12.94095230102539 -96.59258270263672 22.41438674926758 28 | v 18.30126953125 -96.59258270263672 18.30126953125 29 | v 22.41438674926758 -96.59258270263672 12.94095230102539 30 | v 25 -96.59258270263672 6.69872999191284 31 | v 50 -86.6025390625 0 32 | v 48.29629135131836 -86.6025390625 -12.94095230102539 33 | v 43.30126953125 -86.6025390625 -25 34 | v 35.35533905029297 -86.6025390625 -35.35533905029297 35 | v 25 -86.6025390625 -43.30126953125 36 | v 12.94095230102539 -86.6025390625 -48.29629135131836 37 | v 0 -86.6025390625 -50 38 | v -12.94095230102539 -86.6025390625 -48.29629135131836 39 | v -25 -86.6025390625 -43.30126953125 40 | v -35.35533905029297 -86.6025390625 -35.35533905029297 41 | v -43.30126953125 -86.6025390625 -25 42 | v -48.29629135131836 -86.6025390625 -12.94095230102539 43 | v -50 -86.6025390625 -0.00000000000001 44 | v -48.29629135131836 -86.6025390625 12.94095230102539 45 | v -43.30126953125 -86.6025390625 25 46 | v -35.35533905029297 -86.6025390625 35.35533905029297 47 | v -25 -86.6025390625 43.30126953125 48 | v -12.94095230102539 -86.6025390625 48.29629135131836 49 | v -0.00000000000001 -86.6025390625 50 50 | v 12.94095230102539 -86.6025390625 48.29629135131836 51 | v 25 -86.6025390625 43.30126953125 52 | v 35.35533905029297 -86.6025390625 35.35533905029297 53 | v 43.30126953125 -86.6025390625 25 54 | v 48.29629135131836 -86.6025390625 12.94095230102539 55 | v 70.71067810058594 -70.71067810058594 0 56 | v 68.30126953125 -70.71067810058594 -18.30126953125 57 | v 61.23724365234375 -70.71067810058594 -35.35533905029297 58 | v 50 -70.71067810058594 -50 59 | v 35.35533905029297 -70.71067810058594 -61.23724365234375 60 | v 18.30126953125 -70.71067810058594 -68.30126953125 61 | v 0 -70.71067810058594 -70.71067810058594 62 | v -18.30126953125 -70.71067810058594 -68.30126953125 63 | v -35.35533905029297 -70.71067810058594 -61.23724365234375 64 | v -50 -70.71067810058594 -50 65 | v -61.23724365234375 -70.71067810058594 -35.35533905029297 66 | v -68.30126953125 -70.71067810058594 -18.30126953125 67 | v -70.71067810058594 -70.71067810058594 -0.00000000000001 68 | v -68.30126953125 -70.71067810058594 18.30126953125 69 | v -61.23724365234375 -70.71067810058594 35.35533905029297 70 | v -50 -70.71067810058594 50 71 | v -35.35533905029297 -70.71067810058594 61.23724365234375 72 | v -18.30126953125 -70.71067810058594 68.30126953125 73 | v -0.00000000000001 -70.71067810058594 70.71067810058594 74 | v 18.30126953125 -70.71067810058594 68.30126953125 75 | v 35.35533905029297 -70.71067810058594 61.23724365234375 76 | v 50 -70.71067810058594 50 77 | v 61.23724365234375 -70.71067810058594 35.35533905029297 78 | v 68.30126953125 -70.71067810058594 18.30126953125 79 | v 86.6025390625 -50 0 80 | v 83.65162658691406 -50 -22.41438674926758 81 | v 75 -50 -43.30126953125 82 | v 61.23724365234375 -50 -61.23724365234375 83 | v 43.30126953125 -50 -75 84 | v 22.41438674926758 -50 -83.65162658691406 85 | v 0.00000000000001 -50 -86.6025390625 86 | v -22.41438674926758 -50 -83.65162658691406 87 | v -43.30126953125 -50 -75 88 | v -61.23724365234375 -50 -61.23724365234375 89 | v -75 -50 -43.30126953125 90 | v -83.65162658691406 -50 -22.41438674926758 91 | v -86.6025390625 -50 -0.00000000000001 92 | v -83.65162658691406 -50 22.41438674926758 93 | v -75 -50 43.30126953125 94 | v -61.23724365234375 -50 61.23724365234375 95 | v -43.30126953125 -50 75 96 | v -22.41438674926758 -50 83.65162658691406 97 | v -0.00000000000002 -50 86.6025390625 98 | v 22.41438674926758 -50 83.65162658691406 99 | v 43.30126953125 -50 75 100 | v 61.23724365234375 -50 61.23724365234375 101 | v 75 -50 43.30126953125 102 | v 83.65162658691406 -50 22.41438674926758 103 | v 96.59258270263672 -25.88190460205078 0 104 | v 93.30126953125 -25.88190460205078 -25 105 | v 83.65162658691406 -25.88190460205078 -48.29629135131836 106 | v 68.30126953125 -25.88190460205078 -68.30126953125 107 | v 48.29629135131836 -25.88190460205078 -83.65162658691406 108 | v 25 -25.88190460205078 -93.30126953125 109 | v 0.00000000000001 -25.88190460205078 -96.59258270263672 110 | v -25 -25.88190460205078 -93.30126953125 111 | v -48.29629135131836 -25.88190460205078 -83.65162658691406 112 | v -68.30126953125 -25.88190460205078 -68.30126953125 113 | v -83.65162658691406 -25.88190460205078 -48.29629135131836 114 | v -93.30126953125 -25.88190460205078 -25 115 | v -96.59258270263672 -25.88190460205078 -0.00000000000001 116 | v -93.30126953125 -25.88190460205078 25 117 | v -83.65162658691406 -25.88190460205078 48.29629135131836 118 | v -68.30126953125 -25.88190460205078 68.30126953125 119 | v -48.29629135131836 -25.88190460205078 83.65162658691406 120 | v -25 -25.88190460205078 93.30126953125 121 | v -0.00000000000002 -25.88190460205078 96.59258270263672 122 | v 25 -25.88190460205078 93.30126953125 123 | v 48.29629135131836 -25.88190460205078 83.65162658691406 124 | v 68.30126953125 -25.88190460205078 68.30126953125 125 | v 83.65162658691406 -25.88190460205078 48.29629135131836 126 | v 93.30126953125 -25.88190460205078 25 127 | v 100 0 0 128 | v 96.59258270263672 0 -25.88190460205078 129 | v 86.6025390625 0 -50 130 | v 70.71067810058594 0 -70.71067810058594 131 | v 50 0 -86.6025390625 132 | v 25.88190460205078 0 -96.59258270263672 133 | v 0.00000000000001 0 -100 134 | v -25.88190460205078 0 -96.59258270263672 135 | v -50 0 -86.6025390625 136 | v -70.71067810058594 0 -70.71067810058594 137 | v -86.6025390625 0 -50 138 | v -96.59258270263672 0 -25.88190460205078 139 | v -100 0 -0.00000000000001 140 | v -96.59258270263672 0 25.88190460205078 141 | v -86.6025390625 0 50 142 | v -70.71067810058594 0 70.71067810058594 143 | v -50 0 86.6025390625 144 | v -25.88190460205078 0 96.59258270263672 145 | v -0.00000000000002 0 100 146 | v 25.88190460205078 0 96.59258270263672 147 | v 50 0 86.6025390625 148 | v 70.71067810058594 0 70.71067810058594 149 | v 86.6025390625 0 50 150 | v 96.59258270263672 0 25.88190460205078 151 | v 96.59258270263672 25.88190460205078 0 152 | v 93.30126953125 25.88190460205078 -25 153 | v 83.65162658691406 25.88190460205078 -48.29629135131836 154 | v 68.30126953125 25.88190460205078 -68.30126953125 155 | v 48.29629135131836 25.88190460205078 -83.65162658691406 156 | v 25 25.88190460205078 -93.30126953125 157 | v 0.00000000000001 25.88190460205078 -96.59258270263672 158 | v -25 25.88190460205078 -93.30126953125 159 | v -48.29629135131836 25.88190460205078 -83.65162658691406 160 | v -68.30126953125 25.88190460205078 -68.30126953125 161 | v -83.65162658691406 25.88190460205078 -48.29629135131836 162 | v -93.30126953125 25.88190460205078 -25 163 | v -96.59258270263672 25.88190460205078 -0.00000000000001 164 | v -93.30126953125 25.88190460205078 25 165 | v -83.65162658691406 25.88190460205078 48.29629135131836 166 | v -68.30126953125 25.88190460205078 68.30126953125 167 | v -48.29629135131836 25.88190460205078 83.65162658691406 168 | v -25 25.88190460205078 93.30126953125 169 | v -0.00000000000002 25.88190460205078 96.59258270263672 170 | v 25 25.88190460205078 93.30126953125 171 | v 48.29629135131836 25.88190460205078 83.65162658691406 172 | v 68.30126953125 25.88190460205078 68.30126953125 173 | v 83.65162658691406 25.88190460205078 48.29629135131836 174 | v 93.30126953125 25.88190460205078 25 175 | v 86.6025390625 50 0 176 | v 83.65162658691406 50 -22.41438674926758 177 | v 75 50 -43.30126953125 178 | v 61.23724365234375 50 -61.23724365234375 179 | v 43.30126953125 50 -75 180 | v 22.41438674926758 50 -83.65162658691406 181 | v 0.00000000000001 50 -86.6025390625 182 | v -22.41438674926758 50 -83.65162658691406 183 | v -43.30126953125 50 -75 184 | v -61.23724365234375 50 -61.23724365234375 185 | v -75 50 -43.30126953125 186 | v -83.65162658691406 50 -22.41438674926758 187 | v -86.6025390625 50 -0.00000000000001 188 | v -83.65162658691406 50 22.41438674926758 189 | v -75 50 43.30126953125 190 | v -61.23724365234375 50 61.23724365234375 191 | v -43.30126953125 50 75 192 | v -22.41438674926758 50 83.65162658691406 193 | v -0.00000000000002 50 86.6025390625 194 | v 22.41438674926758 50 83.65162658691406 195 | v 43.30126953125 50 75 196 | v 61.23724365234375 50 61.23724365234375 197 | v 75 50 43.30126953125 198 | v 83.65162658691406 50 22.41438674926758 199 | v 70.71067810058594 70.71067810058594 0 200 | v 68.30126953125 70.71067810058594 -18.30126953125 201 | v 61.23724365234375 70.71067810058594 -35.35533905029297 202 | v 50 70.71067810058594 -50 203 | v 35.35533905029297 70.71067810058594 -61.23724365234375 204 | v 18.30126953125 70.71067810058594 -68.30126953125 205 | v 0 70.71067810058594 -70.71067810058594 206 | v -18.30126953125 70.71067810058594 -68.30126953125 207 | v -35.35533905029297 70.71067810058594 -61.23724365234375 208 | v -50 70.71067810058594 -50 209 | v -61.23724365234375 70.71067810058594 -35.35533905029297 210 | v -68.30126953125 70.71067810058594 -18.30126953125 211 | v -70.71067810058594 70.71067810058594 -0.00000000000001 212 | v -68.30126953125 70.71067810058594 18.30126953125 213 | v -61.23724365234375 70.71067810058594 35.35533905029297 214 | v -50 70.71067810058594 50 215 | v -35.35533905029297 70.71067810058594 61.23724365234375 216 | v -18.30126953125 70.71067810058594 68.30126953125 217 | v -0.00000000000001 70.71067810058594 70.71067810058594 218 | v 18.30126953125 70.71067810058594 68.30126953125 219 | v 35.35533905029297 70.71067810058594 61.23724365234375 220 | v 50 70.71067810058594 50 221 | v 61.23724365234375 70.71067810058594 35.35533905029297 222 | v 68.30126953125 70.71067810058594 18.30126953125 223 | v 50 86.6025390625 0 224 | v 48.29629135131836 86.6025390625 -12.94095230102539 225 | v 43.30126953125 86.6025390625 -25 226 | v 35.35533905029297 86.6025390625 -35.35533905029297 227 | v 25 86.6025390625 -43.30126953125 228 | v 12.94095230102539 86.6025390625 -48.29629135131836 229 | v 0 86.6025390625 -50 230 | v -12.94095230102539 86.6025390625 -48.29629135131836 231 | v -25 86.6025390625 -43.30126953125 232 | v -35.35533905029297 86.6025390625 -35.35533905029297 233 | v -43.30126953125 86.6025390625 -25 234 | v -48.29629135131836 86.6025390625 -12.94095230102539 235 | v -50 86.6025390625 -0.00000000000001 236 | v -48.29629135131836 86.6025390625 12.94095230102539 237 | v -43.30126953125 86.6025390625 25 238 | v -35.35533905029297 86.6025390625 35.35533905029297 239 | v -25 86.6025390625 43.30126953125 240 | v -12.94095230102539 86.6025390625 48.29629135131836 241 | v -0.00000000000001 86.6025390625 50 242 | v 12.94095230102539 86.6025390625 48.29629135131836 243 | v 25 86.6025390625 43.30126953125 244 | v 35.35533905029297 86.6025390625 35.35533905029297 245 | v 43.30126953125 86.6025390625 25 246 | v 48.29629135131836 86.6025390625 12.94095230102539 247 | v 25.88190460205078 96.59258270263672 0 248 | v 25 96.59258270263672 -6.69872999191284 249 | v 22.41438674926758 96.59258270263672 -12.94095230102539 250 | v 18.30126953125 96.59258270263672 -18.30126953125 251 | v 12.94095230102539 96.59258270263672 -22.41438674926758 252 | v 6.69872999191284 96.59258270263672 -25 253 | v 0 96.59258270263672 -25.88190460205078 254 | v -6.69872999191284 96.59258270263672 -25 255 | v -12.94095230102539 96.59258270263672 -22.41438674926758 256 | v -18.30126953125 96.59258270263672 -18.30126953125 257 | v -22.41438674926758 96.59258270263672 -12.94095230102539 258 | v -25 96.59258270263672 -6.69872999191284 259 | v -25.88190460205078 96.59258270263672 -0 260 | v -25 96.59258270263672 6.69872999191284 261 | v -22.41438674926758 96.59258270263672 12.94095230102539 262 | v -18.30126953125 96.59258270263672 18.30126953125 263 | v -12.94095230102539 96.59258270263672 22.41438674926758 264 | v -6.69872999191284 96.59258270263672 25 265 | v -0 96.59258270263672 25.88190460205078 266 | v 6.69872999191284 96.59258270263672 25 267 | v 12.94095230102539 96.59258270263672 22.41438674926758 268 | v 18.30126953125 96.59258270263672 18.30126953125 269 | v 22.41438674926758 96.59258270263672 12.94095230102539 270 | v 25 96.59258270263672 6.69872999191284 271 | # 266 vertices 272 | 273 | vn 0.28407144546509 -0.95874935388565 -0.010154617019 274 | vn 0.49988362193108 -0.86607825756073 -0.00498215900734 275 | vn 0.48156097531319 -0.86607825756073 -0.13419176638126 276 | vn 0 0 0 277 | vn 0.27176371216774 -0.95874935388565 -0.08333172649145 278 | vn 0.43042078614235 -0.86607831716537 -0.25425645709038 279 | vn 0.24093577265739 -0.95874935388565 -0.15082988142967 280 | vn 0.3499481678009 -0.86607831716537 -0.35699397325516 281 | vn 0.1936884522438 -0.95874935388565 -0.20804923772812 282 | vn 0.24562713503838 -0.86607831716537 -0.43540292978287 283 | vn 0.13324157893658 -0.95874935388565 -0.2510903775692 284 | vn 0.12456703186035 -0.86607831716537 -0.48413988947868 285 | vn 0.06371449679136 -0.95874935388565 -0.27702012658119 286 | vn -0.00498215900734 -0.86607831716537 -0.49988356232643 287 | vn -0.010154617019 -0.95874935388565 -0.28407144546509 288 | vn -0.13419176638126 -0.86607825756073 -0.48156097531319 289 | vn -0.08333172649145 -0.95874935388565 -0.27176371216774 290 | vn -0.25425645709038 -0.86607831716537 -0.43042078614235 291 | vn -0.15082988142967 -0.95874935388565 -0.24093577265739 292 | vn -0.35699397325516 -0.86607831716537 -0.3499481678009 293 | vn -0.20804923772812 -0.95874935388565 -0.1936884522438 294 | vn -0.43540292978287 -0.86607831716537 -0.24562713503838 295 | vn -0.2510903775692 -0.95874935388565 -0.13324157893658 296 | vn -0.48413988947868 -0.86607831716537 -0.12456703186035 297 | vn -0.27702012658119 -0.95874935388565 -0.06371449679136 298 | vn -0.49988356232643 -0.86607831716537 0.00498215900734 299 | vn -0.28407144546509 -0.95874935388565 0.010154617019 300 | vn -0.48156097531319 -0.86607825756073 0.13419176638126 301 | vn -0.27176371216774 -0.95874935388565 0.08333172649145 302 | vn -0.43042078614235 -0.86607831716537 0.25425645709038 303 | vn -0.24093577265739 -0.95874935388565 0.15082988142967 304 | vn -0.3499481678009 -0.86607831716537 0.35699397325516 305 | vn -0.1936884522438 -0.95874935388565 0.20804923772812 306 | vn -0.24562713503838 -0.86607831716537 0.43540292978287 307 | vn -0.13324157893658 -0.95874935388565 0.2510903775692 308 | vn -0.12456703186035 -0.86607831716537 0.48413988947868 309 | vn -0.06371449679136 -0.95874935388565 0.27702012658119 310 | vn 0.00498215900734 -0.86607831716537 0.49988356232643 311 | vn 0.010154617019 -0.95874935388565 0.28407144546509 312 | vn 0.13419176638126 -0.86607825756073 0.48156097531319 313 | vn 0.08333172649145 -0.95874935388565 0.27176371216774 314 | vn 0.25425645709038 -0.86607831716537 0.43042078614235 315 | vn 0.15082988142967 -0.95874935388565 0.24093577265739 316 | vn 0.35699397325516 -0.86607831716537 0.3499481678009 317 | vn 0.20804923772812 -0.95874935388565 0.1936884522438 318 | vn 0.43540292978287 -0.86607831716537 0.24562713503838 319 | vn 0.2510903775692 -0.95874935388565 0.13324157893658 320 | vn 0.48413988947868 -0.86607831716537 0.12456703186035 321 | vn 0.27702012658119 -0.95874935388565 0.06371449679136 322 | vn 0.70699745416641 -0.70720452070236 -0.00405076658353 323 | vn 0.68185871839523 -0.70720446109772 -0.18689714372158 324 | vn 0.61025243997574 -0.70720446109772 -0.35700675845146 325 | vn 0.49705836176872 -0.70720446109772 -0.50278705358505 326 | vn 0.34999066591263 -0.70720446109772 -0.61430317163467 327 | vn 0.17907170951366 -0.70720446109772 -0.68395555019379 328 | vn -0.00405076658353 -0.70720446109772 -0.70699745416641 329 | vn -0.18689714372158 -0.70720446109772 -0.68185871839523 330 | vn -0.35700675845146 -0.70720446109772 -0.61025243997574 331 | vn -0.50278705358505 -0.70720446109772 -0.49705836176872 332 | vn -0.61430317163467 -0.70720446109772 -0.34999066591263 333 | vn -0.68395555019379 -0.70720446109772 -0.17907170951366 334 | vn -0.70699745416641 -0.70720446109772 0.00405076798052 335 | vn -0.68185871839523 -0.70720446109772 0.18689714372158 336 | vn -0.61025243997574 -0.70720446109772 0.35700675845146 337 | vn -0.49705836176872 -0.70720446109772 0.50278705358505 338 | vn -0.34999066591263 -0.70720446109772 0.61430317163467 339 | vn -0.17907170951366 -0.70720446109772 0.68395555019379 340 | vn 0.00405076658353 -0.70720446109772 0.70699745416641 341 | vn 0.18689714372158 -0.70720446109772 0.68185871839523 342 | vn 0.35700675845146 -0.70720446109772 0.61025243997574 343 | vn 0.50278705358505 -0.70720446109772 0.49705836176872 344 | vn 0.61430317163467 -0.70720446109772 0.34999066591263 345 | vn 0.68395555019379 -0.70720446109772 0.17907170951366 346 | vn 0.86595875024796 -0.50010734796524 -0.0028522729408 347 | vn 0.83571374416351 -0.50010722875595 -0.22688172757626 348 | vn 0.74851620197296 -0.50010722875595 -0.43544942140579 349 | vn 0.61030846834183 -0.5001072883606 -0.61434215307236 350 | vn 0.43050920963287 -0.50010722875595 -0.7513684630394 351 | vn 0.22137151658535 -0.50010722875595 -0.8371901512146 352 | vn -0.00285227154382 -0.50010734796524 -0.86595875024796 353 | vn -0.22688172757626 -0.50010722875595 -0.83571374416351 354 | vn -0.43544942140579 -0.50010722875595 -0.74851620197296 355 | vn -0.61434215307236 -0.5001072883606 -0.61030846834183 356 | vn -0.7513684630394 -0.50010722875595 -0.43050920963287 357 | vn -0.8371901512146 -0.50010722875595 -0.22137151658535 358 | vn -0.86595875024796 -0.50010734796524 0.0028522729408 359 | vn -0.83571374416351 -0.50010722875595 0.22688172757626 360 | vn -0.74851620197296 -0.50010722875595 0.43544942140579 361 | vn -0.61030846834183 -0.5001072883606 0.61434215307236 362 | vn -0.43050920963287 -0.50010722875595 0.7513684630394 363 | vn -0.22137151658535 -0.50010722875595 0.8371901512146 364 | vn 0.00285227154382 -0.50010734796524 0.86595875024796 365 | vn 0.22688172757626 -0.50010722875595 0.83571374416351 366 | vn 0.43544942140579 -0.50010722875595 0.74851620197296 367 | vn 0.61434215307236 -0.5001072883606 0.61030846834183 368 | vn 0.7513684630394 -0.50010722875595 0.43050920963287 369 | vn 0.8371901512146 -0.50010722875595 0.22137151658535 370 | vn 0.96590596437454 -0.25888901948929 -0.00147190282587 371 | vn 0.93261253833771 -0.25888898968697 -0.25141668319702 372 | vn 0.83576315641403 -0.25888898968697 -0.48422768712044 373 | vn 0.68195790052414 -0.25888893008232 -0.68403947353363 374 | vn 0.48167824745178 -0.25888893008232 -0.83723509311676 375 | vn 0.2485730946064 -0.25888895988464 -0.93337446451187 376 | vn -0.00147190771531 -0.25888898968697 -0.96590596437454 377 | vn -0.25141668319702 -0.25888898968697 -0.93261253833771 378 | vn -0.48422768712044 -0.25888898968697 -0.83576315641403 379 | vn -0.68403947353363 -0.25888893008232 -0.68195790052414 380 | vn -0.83723509311676 -0.25888893008232 -0.48167824745178 381 | vn -0.93337446451187 -0.25888895988464 -0.2485730946064 382 | vn -0.96590596437454 -0.25888898968697 0.00147190771531 383 | vn -0.93261253833771 -0.25888898968697 0.25141668319702 384 | vn -0.83576315641403 -0.25888898968697 0.48422768712044 385 | vn -0.68195790052414 -0.25888893008232 0.68403947353363 386 | vn -0.48167824745178 -0.25888893008232 0.83723509311676 387 | vn -0.2485730946064 -0.25888895988464 0.93337446451187 388 | vn 0.00147190771531 -0.25888898968697 0.96590596437454 389 | vn 0.25141668319702 -0.25888898968697 0.93261253833771 390 | vn 0.48422768712044 -0.25888898968697 0.83576315641403 391 | vn 0.68403947353363 -0.25888893008232 0.68195790052414 392 | vn 0.83723509311676 -0.25888893008232 0.48167824745178 393 | vn 0.93337446451187 -0.25888895988464 0.2485730946064 394 | vn 1 0.00000000252621 -0.00000000252621 395 | vn 0.96592581272125 0.00000001010482 -0.25881907343864 396 | vn 0.86602538824081 0.00000000757862 -0.5 397 | vn 0.70710670948029 0.00000001010482 -0.70710682868958 398 | vn 0.5 0.00000001768344 -0.86602538824081 399 | vn 0.25881910324097 -0.00000000505241 -0.96592581272125 400 | vn 0 0 -1 401 | vn -0.25881907343864 0.00000001010482 -0.96592581272125 402 | vn -0.5 0.00000000757862 -0.86602538824081 403 | vn -0.70710682868958 0.00000001010482 -0.70710670948029 404 | vn -0.86602538824081 0.00000001768344 -0.5 405 | vn -0.96592581272125 -0.00000000505241 -0.25881910324097 406 | vn -1 0 0 407 | vn -0.96592581272125 0.00000001010482 0.25881907343864 408 | vn -0.86602538824081 0.00000000757862 0.5 409 | vn -0.70710670948029 0.00000001010482 0.70710682868958 410 | vn -0.5 0.00000001768344 0.86602538824081 411 | vn -0.25881910324097 -0.00000000505241 0.96592581272125 412 | vn 0 0 1 413 | vn 0.25881907343864 0.00000001010482 0.96592581272125 414 | vn 0.5 0.00000000757862 0.86602538824081 415 | vn 0.70710682868958 0.00000001010482 0.70710670948029 416 | vn 0.86602538824081 0.00000001768344 0.5 417 | vn 0.96592581272125 -0.00000000505241 0.25881910324097 418 | vn 0.96590596437454 0.25888895988464 0.00147190771531 419 | vn 0.93337446451187 0.25888898968697 -0.24857313930988 420 | vn 0.83723509311676 0.25888893008232 -0.4816782772541 421 | vn 0.68403947353363 0.25888893008232 -0.68195784091949 422 | vn 0.48422768712044 0.25888898968697 -0.83576315641403 423 | vn 0.25141668319702 0.25888898968697 -0.93261253833771 424 | vn 0.00147191027645 0.25888895988464 -0.96590596437454 425 | vn -0.24857313930988 0.25888898968697 -0.93337446451187 426 | vn -0.4816782772541 0.25888893008232 -0.83723509311676 427 | vn -0.68195784091949 0.25888893008232 -0.68403947353363 428 | vn -0.83576315641403 0.25888898968697 -0.48422768712044 429 | vn -0.93261253833771 0.25888898968697 -0.25141668319702 430 | vn -0.96590596437454 0.25888898968697 -0.00147190771531 431 | vn -0.93337446451187 0.25888898968697 0.24857313930988 432 | vn -0.83723509311676 0.25888893008232 0.4816782772541 433 | vn -0.68403947353363 0.25888893008232 0.68195784091949 434 | vn -0.48422768712044 0.25888898968697 0.83576315641403 435 | vn -0.25141668319702 0.25888898968697 0.93261253833771 436 | vn -0.00147191027645 0.25888895988464 0.96590596437454 437 | vn 0.24857313930988 0.25888898968697 0.93337446451187 438 | vn 0.4816782772541 0.25888893008232 0.83723509311676 439 | vn 0.68195784091949 0.25888893008232 0.68403947353363 440 | vn 0.83576315641403 0.25888898968697 0.48422768712044 441 | vn 0.93261253833771 0.25888898968697 0.25141668319702 442 | vn 0.86595875024796 0.5001072883606 0.00285227154382 443 | vn 0.8371901512146 0.50010734796524 -0.22137153148651 444 | vn 0.75136840343475 0.5001072883606 -0.43050920963287 445 | vn 0.614342212677 0.50010722875595 -0.61030840873718 446 | vn 0.43544945120811 0.50010722875595 -0.74851620197296 447 | vn 0.22688169777393 0.50010722875595 -0.83571374416351 448 | vn 0.0028522729408 0.5001072883606 -0.86595875024796 449 | vn -0.22137153148651 0.50010734796524 -0.8371901512146 450 | vn -0.43050920963287 0.5001072883606 -0.75136840343475 451 | vn -0.61030840873718 0.50010722875595 -0.614342212677 452 | vn -0.74851620197296 0.50010722875595 -0.43544945120811 453 | vn -0.83571374416351 0.50010722875595 -0.22688169777393 454 | vn -0.86595875024796 0.5001072883606 -0.00285227131099 455 | vn -0.8371901512146 0.50010734796524 0.22137153148651 456 | vn -0.75136840343475 0.5001072883606 0.43050920963287 457 | vn -0.614342212677 0.50010722875595 0.61030840873718 458 | vn -0.43544945120811 0.50010722875595 0.74851620197296 459 | vn -0.22688169777393 0.50010722875595 0.83571374416351 460 | vn -0.0028522729408 0.5001072883606 0.86595875024796 461 | vn 0.22137153148651 0.50010734796524 0.8371901512146 462 | vn 0.43050920963287 0.5001072883606 0.75136840343475 463 | vn 0.61030840873718 0.50010722875595 0.614342212677 464 | vn 0.74851620197296 0.50010722875595 0.43544945120811 465 | vn 0.83571374416351 0.50010722875595 0.22688169777393 466 | vn 0.70699751377106 0.70720446109772 0.00405076052994 467 | vn 0.68395555019379 0.70720446109772 -0.17907172441483 468 | vn 0.61430311203003 0.70720452070236 -0.34999063611031 469 | vn 0.50278699398041 0.70720452070236 -0.49705839157104 470 | vn 0.35700678825378 0.70720446109772 -0.61025238037109 471 | vn 0.18689711391926 0.70720446109772 -0.68185871839523 472 | vn 0.00405076052994 0.70720446109772 -0.70699751377106 473 | vn -0.17907172441483 0.70720446109772 -0.68395555019379 474 | vn -0.34999063611031 0.70720452070236 -0.61430311203003 475 | vn -0.49705839157104 0.70720452070236 -0.50278699398041 476 | vn -0.61025238037109 0.70720446109772 -0.35700678825378 477 | vn -0.68185871839523 0.70720446109772 -0.18689711391926 478 | vn -0.70699751377106 0.70720446109772 -0.00405076192692 479 | vn -0.68395555019379 0.70720446109772 0.17907172441483 480 | vn -0.61430311203003 0.70720452070236 0.34999063611031 481 | vn -0.50278699398041 0.70720452070236 0.49705839157104 482 | vn -0.35700678825378 0.70720446109772 0.61025238037109 483 | vn -0.18689711391926 0.70720446109772 0.68185871839523 484 | vn -0.00405076052994 0.70720446109772 0.70699751377106 485 | vn 0.17907172441483 0.70720446109772 0.68395555019379 486 | vn 0.34999063611031 0.70720452070236 0.61430311203003 487 | vn 0.49705839157104 0.70720452070236 0.50278699398041 488 | vn 0.61025238037109 0.70720446109772 0.35700678825378 489 | vn 0.68185871839523 0.70720446109772 0.18689711391926 490 | vn 0.49988353252411 0.86607831716537 0.00498214690015 491 | vn 0.48413994908333 0.86607825756073 -0.12456703186035 492 | vn 0.43540292978287 0.86607825756073 -0.24562712013721 493 | vn 0.35699394345284 0.86607831716537 -0.34994810819626 494 | vn 0.2542564868927 0.86607825756073 -0.43042078614235 495 | vn 0.13419178128242 0.86607825756073 -0.48156103491783 496 | vn 0.00498214643449 0.86607825756073 -0.49988362193108 497 | vn -0.12456703186035 0.86607825756073 -0.48413994908333 498 | vn -0.24562712013721 0.86607825756073 -0.43540292978287 499 | vn -0.34994810819626 0.86607831716537 -0.35699394345284 500 | vn -0.43042078614235 0.86607825756073 -0.2542564868927 501 | vn -0.48156100511551 0.86607825756073 -0.13419178128242 502 | vn -0.49988362193108 0.86607825756073 -0.00498214643449 503 | vn -0.48413994908333 0.86607825756073 0.12456703186035 504 | vn -0.43540292978287 0.86607825756073 0.24562712013721 505 | vn -0.35699394345284 0.86607831716537 0.34994810819626 506 | vn -0.2542564868927 0.86607825756073 0.43042078614235 507 | vn -0.13419178128242 0.86607825756073 0.48156103491783 508 | vn -0.00498214643449 0.86607825756073 0.49988362193108 509 | vn 0.12456703186035 0.86607825756073 0.48413994908333 510 | vn 0.24562712013721 0.86607825756073 0.43540292978287 511 | vn 0.34994810819626 0.86607831716537 0.35699394345284 512 | vn 0.43042078614235 0.86607825756073 0.2542564868927 513 | vn 0.48156100511551 0.86607825756073 0.13419178128242 514 | vn 0.28407144546509 0.95874935388565 0.01015461795032 515 | vn 0.27702015638351 0.95874935388565 -0.06371451169252 516 | vn 0.2510903775692 0.95874935388565 -0.13324159383774 517 | vn 0.2080492079258 0.95874935388565 -0.19368846714497 518 | vn 0.15082989633083 0.95874935388565 -0.24093574285507 519 | vn 0.08333174139261 0.95874935388565 -0.27176371216774 520 | vn 0.01015461795032 0.95874935388565 -0.28407144546509 521 | vn -0.06371451169252 0.95874935388565 -0.27702015638351 522 | vn -0.13324159383774 0.95874935388565 -0.2510903775692 523 | vn -0.19368846714497 0.95874935388565 -0.2080492079258 524 | vn -0.24093574285507 0.95874935388565 -0.15082989633083 525 | vn -0.27176371216774 0.95874935388565 -0.08333174139261 526 | vn -0.28407144546509 0.95874935388565 -0.01015461795032 527 | vn -0.27702015638351 0.95874935388565 0.06371451169252 528 | vn -0.2510903775692 0.95874935388565 0.13324159383774 529 | vn -0.2080492079258 0.95874935388565 0.19368846714497 530 | vn -0.15082989633083 0.95874935388565 0.24093574285507 531 | vn -0.08333174139261 0.95874935388565 0.27176371216774 532 | vn -0.01015461795032 0.95874935388565 0.28407144546509 533 | vn 0.06371451169252 0.95874935388565 0.27702015638351 534 | vn 0.13324159383774 0.95874935388565 0.2510903775692 535 | vn 0.19368846714497 0.95874935388565 0.2080492079258 536 | vn 0.24093574285507 0.95874935388565 0.15082989633083 537 | vn 0.27176371216774 0.95874935388565 0.08333174139261 538 | vn -0.00000000031317 -1 -0.00000000062633 539 | vn 0 1 0 540 | # 267 normals 541 | 542 | vt 0 0.08333337306976 0 543 | vt 0 0.16666674613953 0 544 | vt 0.04166666790843 0.16666674613953 0 545 | vt 0.04166666790843 0.08333337306976 0 546 | vt 0.08333333581686 0.16666674613953 0 547 | vt 0.08333333581686 0.08333337306976 0 548 | vt 0.125 0.16666674613953 0 549 | vt 0.125 0.08333337306976 0 550 | vt 0.16666667163372 0.16666674613953 0 551 | vt 0.16666667163372 0.08333337306976 0 552 | vt 0.20833332836628 0.16666674613953 0 553 | vt 0.20833332836628 0.08333337306976 0 554 | vt 0.25 0.16666674613953 0 555 | vt 0.25 0.08333337306976 0 556 | vt 0.29166665673256 0.16666674613953 0 557 | vt 0.29166665673256 0.08333337306976 0 558 | vt 0.33333334326744 0.16666674613953 0 559 | vt 0.33333334326744 0.08333337306976 0 560 | vt 0.375 0.16666674613953 0 561 | vt 0.375 0.08333337306976 0 562 | vt 0.41666665673256 0.16666674613953 0 563 | vt 0.41666665673256 0.08333337306976 0 564 | vt 0.45833334326744 0.16666674613953 0 565 | vt 0.45833334326744 0.08333337306976 0 566 | vt 0.5 0.16666674613953 0 567 | vt 0.5 0.08333337306976 0 568 | vt 0.54166668653488 0.16666674613953 0 569 | vt 0.54166668653488 0.08333337306976 0 570 | vt 0.58333331346512 0.16666674613953 0 571 | vt 0.58333331346512 0.08333337306976 0 572 | vt 0.625 0.16666674613953 0 573 | vt 0.625 0.08333337306976 0 574 | vt 0.66666668653488 0.16666674613953 0 575 | vt 0.66666668653488 0.08333337306976 0 576 | vt 0.70833331346512 0.16666674613953 0 577 | vt 0.70833331346512 0.08333337306976 0 578 | vt 0.75 0.16666674613953 0 579 | vt 0.75 0.08333337306976 0 580 | vt 0.79166668653488 0.16666674613953 0 581 | vt 0.79166668653488 0.08333337306976 0 582 | vt 0.83333331346512 0.16666674613953 0 583 | vt 0.83333331346512 0.08333337306976 0 584 | vt 0.875 0.16666674613953 0 585 | vt 0.875 0.08333337306976 0 586 | vt 0.91666668653488 0.16666674613953 0 587 | vt 0.91666668653488 0.08333337306976 0 588 | vt 0.95833331346512 0.16666674613953 0 589 | vt 0.95833331346512 0.08333337306976 0 590 | vt 1 0.16666674613953 0 591 | vt 1 0.08333337306976 0 592 | vt 0 0.25 0 593 | vt 0.04166666790843 0.25 0 594 | vt 0.08333333581686 0.25 0 595 | vt 0.125 0.25 0 596 | vt 0.16666667163372 0.25 0 597 | vt 0.20833332836628 0.25 0 598 | vt 0.25 0.25 0 599 | vt 0.29166665673256 0.25 0 600 | vt 0.33333334326744 0.25 0 601 | vt 0.375 0.25 0 602 | vt 0.41666665673256 0.25 0 603 | vt 0.45833334326744 0.25 0 604 | vt 0.5 0.25 0 605 | vt 0.54166668653488 0.25 0 606 | vt 0.58333331346512 0.25 0 607 | vt 0.625 0.25 0 608 | vt 0.66666668653488 0.25 0 609 | vt 0.70833331346512 0.25 0 610 | vt 0.75 0.25 0 611 | vt 0.79166668653488 0.25 0 612 | vt 0.83333331346512 0.25 0 613 | vt 0.875 0.25 0 614 | vt 0.91666668653488 0.25 0 615 | vt 0.95833331346512 0.25 0 616 | vt 1 0.25 0 617 | vt 0 0.33333337306976 0 618 | vt 0.04166666790843 0.33333337306976 0 619 | vt 0.08333333581686 0.33333337306976 0 620 | vt 0.125 0.33333337306976 0 621 | vt 0.16666667163372 0.33333337306976 0 622 | vt 0.20833332836628 0.33333337306976 0 623 | vt 0.25 0.33333337306976 0 624 | vt 0.29166665673256 0.33333337306976 0 625 | vt 0.33333334326744 0.33333337306976 0 626 | vt 0.375 0.33333337306976 0 627 | vt 0.41666665673256 0.33333337306976 0 628 | vt 0.45833334326744 0.33333337306976 0 629 | vt 0.5 0.33333337306976 0 630 | vt 0.54166668653488 0.33333337306976 0 631 | vt 0.58333331346512 0.33333337306976 0 632 | vt 0.625 0.33333337306976 0 633 | vt 0.66666668653488 0.33333337306976 0 634 | vt 0.70833331346512 0.33333337306976 0 635 | vt 0.75 0.33333337306976 0 636 | vt 0.79166668653488 0.33333337306976 0 637 | vt 0.83333331346512 0.33333337306976 0 638 | vt 0.875 0.33333337306976 0 639 | vt 0.91666668653488 0.33333337306976 0 640 | vt 0.95833331346512 0.33333337306976 0 641 | vt 1 0.33333337306976 0 642 | vt 0 0.41666674613953 0 643 | vt 0.04166666790843 0.41666674613953 0 644 | vt 0.08333333581686 0.41666674613953 0 645 | vt 0.125 0.41666674613953 0 646 | vt 0.16666667163372 0.41666674613953 0 647 | vt 0.20833332836628 0.41666674613953 0 648 | vt 0.25 0.41666674613953 0 649 | vt 0.29166665673256 0.41666674613953 0 650 | vt 0.33333334326744 0.41666674613953 0 651 | vt 0.375 0.41666674613953 0 652 | vt 0.41666665673256 0.41666674613953 0 653 | vt 0.45833334326744 0.41666674613953 0 654 | vt 0.5 0.41666674613953 0 655 | vt 0.54166668653488 0.41666674613953 0 656 | vt 0.58333331346512 0.41666674613953 0 657 | vt 0.625 0.41666674613953 0 658 | vt 0.66666668653488 0.41666674613953 0 659 | vt 0.70833331346512 0.41666674613953 0 660 | vt 0.75 0.41666674613953 0 661 | vt 0.79166668653488 0.41666674613953 0 662 | vt 0.83333331346512 0.41666674613953 0 663 | vt 0.875 0.41666674613953 0 664 | vt 0.91666668653488 0.41666674613953 0 665 | vt 0.95833331346512 0.41666674613953 0 666 | vt 1 0.41666674613953 0 667 | vt 0 0.5 0 668 | vt 0.04166666790843 0.5 0 669 | vt 0.08333333581686 0.5 0 670 | vt 0.125 0.5 0 671 | vt 0.16666667163372 0.5 0 672 | vt 0.20833332836628 0.5 0 673 | vt 0.25 0.5 0 674 | vt 0.29166665673256 0.5 0 675 | vt 0.33333334326744 0.5 0 676 | vt 0.375 0.5 0 677 | vt 0.41666665673256 0.5 0 678 | vt 0.45833334326744 0.5 0 679 | vt 0.5 0.5 0 680 | vt 0.54166668653488 0.5 0 681 | vt 0.58333331346512 0.5 0 682 | vt 0.625 0.5 0 683 | vt 0.66666668653488 0.5 0 684 | vt 0.70833331346512 0.5 0 685 | vt 0.75 0.5 0 686 | vt 0.79166668653488 0.5 0 687 | vt 0.83333331346512 0.5 0 688 | vt 0.875 0.5 0 689 | vt 0.91666668653488 0.5 0 690 | vt 0.95833331346512 0.5 0 691 | vt 1 0.5 0 692 | vt 0 0.58333337306976 0 693 | vt 0.04166666790843 0.58333337306976 0 694 | vt 0.08333333581686 0.58333337306976 0 695 | vt 0.125 0.58333337306976 0 696 | vt 0.16666667163372 0.58333337306976 0 697 | vt 0.20833332836628 0.58333337306976 0 698 | vt 0.25 0.58333337306976 0 699 | vt 0.29166665673256 0.58333337306976 0 700 | vt 0.33333334326744 0.58333337306976 0 701 | vt 0.375 0.58333337306976 0 702 | vt 0.41666665673256 0.58333337306976 0 703 | vt 0.45833334326744 0.58333337306976 0 704 | vt 0.5 0.58333337306976 0 705 | vt 0.54166668653488 0.58333337306976 0 706 | vt 0.58333331346512 0.58333337306976 0 707 | vt 0.625 0.58333337306976 0 708 | vt 0.66666668653488 0.58333337306976 0 709 | vt 0.70833331346512 0.58333337306976 0 710 | vt 0.75 0.58333337306976 0 711 | vt 0.79166668653488 0.58333337306976 0 712 | vt 0.83333331346512 0.58333337306976 0 713 | vt 0.875 0.58333337306976 0 714 | vt 0.91666668653488 0.58333337306976 0 715 | vt 0.95833331346512 0.58333337306976 0 716 | vt 1 0.58333337306976 0 717 | vt 0 0.66666668653488 0 718 | vt 0.04166666790843 0.66666668653488 0 719 | vt 0.08333333581686 0.66666668653488 0 720 | vt 0.125 0.66666668653488 0 721 | vt 0.16666667163372 0.66666668653488 0 722 | vt 0.20833332836628 0.66666668653488 0 723 | vt 0.25 0.66666668653488 0 724 | vt 0.29166665673256 0.66666668653488 0 725 | vt 0.33333334326744 0.66666668653488 0 726 | vt 0.375 0.66666668653488 0 727 | vt 0.41666665673256 0.66666668653488 0 728 | vt 0.45833334326744 0.66666668653488 0 729 | vt 0.5 0.66666668653488 0 730 | vt 0.54166668653488 0.66666668653488 0 731 | vt 0.58333331346512 0.66666668653488 0 732 | vt 0.625 0.66666668653488 0 733 | vt 0.66666668653488 0.66666668653488 0 734 | vt 0.70833331346512 0.66666668653488 0 735 | vt 0.75 0.66666668653488 0 736 | vt 0.79166668653488 0.66666668653488 0 737 | vt 0.83333331346512 0.66666668653488 0 738 | vt 0.875 0.66666668653488 0 739 | vt 0.91666668653488 0.66666668653488 0 740 | vt 0.95833331346512 0.66666668653488 0 741 | vt 1 0.66666668653488 0 742 | vt 0 0.75 0 743 | vt 0.04166666790843 0.75 0 744 | vt 0.08333333581686 0.75 0 745 | vt 0.125 0.75 0 746 | vt 0.16666667163372 0.75 0 747 | vt 0.20833332836628 0.75 0 748 | vt 0.25 0.75 0 749 | vt 0.29166665673256 0.75 0 750 | vt 0.33333334326744 0.75 0 751 | vt 0.375 0.75 0 752 | vt 0.41666665673256 0.75 0 753 | vt 0.45833334326744 0.75 0 754 | vt 0.5 0.75 0 755 | vt 0.54166668653488 0.75 0 756 | vt 0.58333331346512 0.75 0 757 | vt 0.625 0.75 0 758 | vt 0.66666668653488 0.75 0 759 | vt 0.70833331346512 0.75 0 760 | vt 0.75 0.75 0 761 | vt 0.79166668653488 0.75 0 762 | vt 0.83333331346512 0.75 0 763 | vt 0.875 0.75 0 764 | vt 0.91666668653488 0.75 0 765 | vt 0.95833331346512 0.75 0 766 | vt 1 0.75 0 767 | vt 0 0.83333337306976 0 768 | vt 0.04166666790843 0.83333337306976 0 769 | vt 0.08333333581686 0.83333337306976 0 770 | vt 0.125 0.83333337306976 0 771 | vt 0.16666667163372 0.83333337306976 0 772 | vt 0.20833332836628 0.83333337306976 0 773 | vt 0.25 0.83333337306976 0 774 | vt 0.29166665673256 0.83333337306976 0 775 | vt 0.33333334326744 0.83333337306976 0 776 | vt 0.375 0.83333337306976 0 777 | vt 0.41666665673256 0.83333337306976 0 778 | vt 0.45833334326744 0.83333337306976 0 779 | vt 0.5 0.83333337306976 0 780 | vt 0.54166668653488 0.83333337306976 0 781 | vt 0.58333331346512 0.83333337306976 0 782 | vt 0.625 0.83333337306976 0 783 | vt 0.66666668653488 0.83333337306976 0 784 | vt 0.70833331346512 0.83333337306976 0 785 | vt 0.75 0.83333337306976 0 786 | vt 0.79166668653488 0.83333337306976 0 787 | vt 0.83333331346512 0.83333337306976 0 788 | vt 0.875 0.83333337306976 0 789 | vt 0.91666668653488 0.83333337306976 0 790 | vt 0.95833331346512 0.83333337306976 0 791 | vt 1 0.83333337306976 0 792 | vt 0 0.91666668653488 0 793 | vt 0.04166666790843 0.91666668653488 0 794 | vt 0.08333333581686 0.91666668653488 0 795 | vt 0.125 0.91666668653488 0 796 | vt 0.16666667163372 0.91666668653488 0 797 | vt 0.20833332836628 0.91666668653488 0 798 | vt 0.25 0.91666668653488 0 799 | vt 0.29166665673256 0.91666668653488 0 800 | vt 0.33333334326744 0.91666668653488 0 801 | vt 0.375 0.91666668653488 0 802 | vt 0.41666665673256 0.91666668653488 0 803 | vt 0.45833334326744 0.91666668653488 0 804 | vt 0.5 0.91666668653488 0 805 | vt 0.54166668653488 0.91666668653488 0 806 | vt 0.58333331346512 0.91666668653488 0 807 | vt 0.625 0.91666668653488 0 808 | vt 0.66666668653488 0.91666668653488 0 809 | vt 0.70833331346512 0.91666668653488 0 810 | vt 0.75 0.91666668653488 0 811 | vt 0.79166668653488 0.91666668653488 0 812 | vt 0.83333331346512 0.91666668653488 0 813 | vt 0.875 0.91666668653488 0 814 | vt 0.91666668653488 0.91666668653488 0 815 | vt 0.95833331346512 0.91666668653488 0 816 | vt 1 0.91666668653488 0 817 | vt 0 0 0 818 | vt 0 1 0 819 | vt 0.04166666790843 0 0 820 | vt 0.04166666790843 1 0 821 | vt 0.08333333581686 0 0 822 | vt 0.08333333581686 1 0 823 | vt 0.125 0 0 824 | vt 0.125 1 0 825 | vt 0.16666667163372 0 0 826 | vt 0.16666667163372 1 0 827 | vt 0.20833332836628 0 0 828 | vt 0.20833332836628 1 0 829 | vt 0.25 0 0 830 | vt 0.25 1 0 831 | vt 0.29166665673256 0 0 832 | vt 0.29166665673256 1 0 833 | vt 0.33333334326744 0 0 834 | vt 0.33333334326744 1 0 835 | vt 0.375 0 0 836 | vt 0.375 1 0 837 | vt 0.41666665673256 0 0 838 | vt 0.41666665673256 1 0 839 | vt 0.45833334326744 0 0 840 | vt 0.45833334326744 1 0 841 | vt 0.5 0 0 842 | vt 0.5 1 0 843 | vt 0.54166668653488 0 0 844 | vt 0.54166668653488 1 0 845 | vt 0.58333331346512 0 0 846 | vt 0.58333331346512 1 0 847 | vt 0.625 0 0 848 | vt 0.625 1 0 849 | vt 0.66666668653488 0 0 850 | vt 0.66666668653488 1 0 851 | vt 0.70833331346512 0 0 852 | vt 0.70833331346512 1 0 853 | vt 0.75 0 0 854 | vt 0.75 1 0 855 | vt 0.79166668653488 0 0 856 | vt 0.79166668653488 1 0 857 | vt 0.83333331346512 0 0 858 | vt 0.83333331346512 1 0 859 | vt 0.875 0 0 860 | vt 0.875 1 0 861 | vt 0.91666668653488 0 0 862 | vt 0.91666668653488 1 0 863 | vt 0.95833331346512 0 0 864 | vt 0.95833331346512 1 0 865 | # 323 texture coordinates 866 | 867 | o Sphere 868 | usemtl Mat 869 | f 28/3/3 27/2/2 3/1/1 870 | f 29/5/6 28/3/3 4/4/5 871 | f 30/7/8 29/5/6 5/6/7 872 | f 31/9/10 30/7/8 6/8/9 873 | f 32/11/12 31/9/10 7/10/11 874 | f 33/13/14 32/11/12 8/12/13 875 | f 34/15/16 33/13/14 9/14/15 876 | f 35/17/18 34/15/16 10/16/17 877 | f 36/19/20 35/17/18 11/18/19 878 | f 37/21/22 36/19/20 12/20/21 879 | f 38/23/24 37/21/22 13/22/23 880 | f 39/25/26 38/23/24 14/24/25 881 | f 40/27/28 39/25/26 15/26/27 882 | f 41/29/30 40/27/28 16/28/29 883 | f 42/31/32 41/29/30 17/30/31 884 | f 43/33/34 42/31/32 18/32/33 885 | f 44/35/36 43/33/34 19/34/35 886 | f 45/37/38 44/35/36 20/36/37 887 | f 46/39/40 45/37/38 21/38/39 888 | f 47/41/42 46/39/40 22/40/41 889 | f 48/43/44 47/41/42 23/42/43 890 | f 49/45/46 48/43/44 24/44/45 891 | f 50/47/48 49/45/46 25/46/47 892 | f 27/49/2 50/47/48 26/48/49 893 | f 52/52/51 51/51/50 27/2/2 894 | f 53/53/52 52/52/51 28/3/3 895 | f 54/54/53 53/53/52 29/5/6 896 | f 55/55/54 54/54/53 30/7/8 897 | f 56/56/55 55/55/54 31/9/10 898 | f 57/57/56 56/56/55 32/11/12 899 | f 58/58/57 57/57/56 33/13/14 900 | f 59/59/58 58/58/57 34/15/16 901 | f 60/60/59 59/59/58 35/17/18 902 | f 61/61/60 60/60/59 36/19/20 903 | f 62/62/61 61/61/60 37/21/22 904 | f 63/63/62 62/62/61 38/23/24 905 | f 64/64/63 63/63/62 39/25/26 906 | f 65/65/64 64/64/63 40/27/28 907 | f 66/66/65 65/65/64 41/29/30 908 | f 67/67/66 66/66/65 42/31/32 909 | f 68/68/67 67/67/66 43/33/34 910 | f 69/69/68 68/68/67 44/35/36 911 | f 70/70/69 69/69/68 45/37/38 912 | f 71/71/70 70/70/69 46/39/40 913 | f 72/72/71 71/71/70 47/41/42 914 | f 73/73/72 72/72/71 48/43/44 915 | f 74/74/73 73/73/72 49/45/46 916 | f 51/75/50 74/74/73 50/47/48 917 | f 76/77/75 75/76/74 51/51/50 918 | f 77/78/76 76/77/75 52/52/51 919 | f 78/79/77 77/78/76 53/53/52 920 | f 79/80/78 78/79/77 54/54/53 921 | f 80/81/79 79/80/78 55/55/54 922 | f 81/82/80 80/81/79 56/56/55 923 | f 82/83/81 81/82/80 57/57/56 924 | f 83/84/82 82/83/81 58/58/57 925 | f 84/85/83 83/84/82 59/59/58 926 | f 85/86/84 84/85/83 60/60/59 927 | f 86/87/85 85/86/84 61/61/60 928 | f 87/88/86 86/87/85 62/62/61 929 | f 88/89/87 87/88/86 63/63/62 930 | f 89/90/88 88/89/87 64/64/63 931 | f 90/91/89 89/90/88 65/65/64 932 | f 91/92/90 90/91/89 66/66/65 933 | f 92/93/91 91/92/90 67/67/66 934 | f 93/94/92 92/93/91 68/68/67 935 | f 94/95/93 93/94/92 69/69/68 936 | f 95/96/94 94/95/93 70/70/69 937 | f 96/97/95 95/96/94 71/71/70 938 | f 97/98/96 96/97/95 72/72/71 939 | f 98/99/97 97/98/96 73/73/72 940 | f 75/100/74 98/99/97 74/74/73 941 | f 100/102/99 99/101/98 75/76/74 942 | f 101/103/100 100/102/99 76/77/75 943 | f 102/104/101 101/103/100 77/78/76 944 | f 103/105/102 102/104/101 78/79/77 945 | f 104/106/103 103/105/102 79/80/78 946 | f 105/107/104 104/106/103 80/81/79 947 | f 106/108/105 105/107/104 81/82/80 948 | f 107/109/106 106/108/105 82/83/81 949 | f 108/110/107 107/109/106 83/84/82 950 | f 109/111/108 108/110/107 84/85/83 951 | f 110/112/109 109/111/108 85/86/84 952 | f 111/113/110 110/112/109 86/87/85 953 | f 112/114/111 111/113/110 87/88/86 954 | f 113/115/112 112/114/111 88/89/87 955 | f 114/116/113 113/115/112 89/90/88 956 | f 115/117/114 114/116/113 90/91/89 957 | f 116/118/115 115/117/114 91/92/90 958 | f 117/119/116 116/118/115 92/93/91 959 | f 118/120/117 117/119/116 93/94/92 960 | f 119/121/118 118/120/117 94/95/93 961 | f 120/122/119 119/121/118 95/96/94 962 | f 121/123/120 120/122/119 96/97/95 963 | f 122/124/121 121/123/120 97/98/96 964 | f 99/125/98 122/124/121 98/99/97 965 | f 124/127/123 123/126/122 99/101/98 966 | f 125/128/124 124/127/123 100/102/99 967 | f 126/129/125 125/128/124 101/103/100 968 | f 127/130/126 126/129/125 102/104/101 969 | f 128/131/127 127/130/126 103/105/102 970 | f 129/132/128 128/131/127 104/106/103 971 | f 130/133/129 129/132/128 105/107/104 972 | f 131/134/130 130/133/129 106/108/105 973 | f 132/135/131 131/134/130 107/109/106 974 | f 133/136/132 132/135/131 108/110/107 975 | f 134/137/133 133/136/132 109/111/108 976 | f 135/138/134 134/137/133 110/112/109 977 | f 136/139/135 135/138/134 111/113/110 978 | f 137/140/136 136/139/135 112/114/111 979 | f 138/141/137 137/140/136 113/115/112 980 | f 139/142/138 138/141/137 114/116/113 981 | f 140/143/139 139/142/138 115/117/114 982 | f 141/144/140 140/143/139 116/118/115 983 | f 142/145/141 141/144/140 117/119/116 984 | f 143/146/142 142/145/141 118/120/117 985 | f 144/147/143 143/146/142 119/121/118 986 | f 145/148/144 144/147/143 120/122/119 987 | f 146/149/145 145/148/144 121/123/120 988 | f 123/150/122 146/149/145 122/124/121 989 | f 148/152/147 147/151/146 123/126/122 990 | f 149/153/148 148/152/147 124/127/123 991 | f 150/154/149 149/153/148 125/128/124 992 | f 151/155/150 150/154/149 126/129/125 993 | f 152/156/151 151/155/150 127/130/126 994 | f 153/157/152 152/156/151 128/131/127 995 | f 154/158/153 153/157/152 129/132/128 996 | f 155/159/154 154/158/153 130/133/129 997 | f 156/160/155 155/159/154 131/134/130 998 | f 157/161/156 156/160/155 132/135/131 999 | f 158/162/157 157/161/156 133/136/132 1000 | f 159/163/158 158/162/157 134/137/133 1001 | f 160/164/159 159/163/158 135/138/134 1002 | f 161/165/160 160/164/159 136/139/135 1003 | f 162/166/161 161/165/160 137/140/136 1004 | f 163/167/162 162/166/161 138/141/137 1005 | f 164/168/163 163/167/162 139/142/138 1006 | f 165/169/164 164/168/163 140/143/139 1007 | f 166/170/165 165/169/164 141/144/140 1008 | f 167/171/166 166/170/165 142/145/141 1009 | f 168/172/167 167/171/166 143/146/142 1010 | f 169/173/168 168/172/167 144/147/143 1011 | f 170/174/169 169/173/168 145/148/144 1012 | f 147/175/146 170/174/169 146/149/145 1013 | f 172/177/171 171/176/170 147/151/146 1014 | f 173/178/172 172/177/171 148/152/147 1015 | f 174/179/173 173/178/172 149/153/148 1016 | f 175/180/174 174/179/173 150/154/149 1017 | f 176/181/175 175/180/174 151/155/150 1018 | f 177/182/176 176/181/175 152/156/151 1019 | f 178/183/177 177/182/176 153/157/152 1020 | f 179/184/178 178/183/177 154/158/153 1021 | f 180/185/179 179/184/178 155/159/154 1022 | f 181/186/180 180/185/179 156/160/155 1023 | f 182/187/181 181/186/180 157/161/156 1024 | f 183/188/182 182/187/181 158/162/157 1025 | f 184/189/183 183/188/182 159/163/158 1026 | f 185/190/184 184/189/183 160/164/159 1027 | f 186/191/185 185/190/184 161/165/160 1028 | f 187/192/186 186/191/185 162/166/161 1029 | f 188/193/187 187/192/186 163/167/162 1030 | f 189/194/188 188/193/187 164/168/163 1031 | f 190/195/189 189/194/188 165/169/164 1032 | f 191/196/190 190/195/189 166/170/165 1033 | f 192/197/191 191/196/190 167/171/166 1034 | f 193/198/192 192/197/191 168/172/167 1035 | f 194/199/193 193/198/192 169/173/168 1036 | f 171/200/170 194/199/193 170/174/169 1037 | f 196/202/195 195/201/194 171/176/170 1038 | f 197/203/196 196/202/195 172/177/171 1039 | f 198/204/197 197/203/196 173/178/172 1040 | f 199/205/198 198/204/197 174/179/173 1041 | f 200/206/199 199/205/198 175/180/174 1042 | f 201/207/200 200/206/199 176/181/175 1043 | f 202/208/201 201/207/200 177/182/176 1044 | f 203/209/202 202/208/201 178/183/177 1045 | f 204/210/203 203/209/202 179/184/178 1046 | f 205/211/204 204/210/203 180/185/179 1047 | f 206/212/205 205/211/204 181/186/180 1048 | f 207/213/206 206/212/205 182/187/181 1049 | f 208/214/207 207/213/206 183/188/182 1050 | f 209/215/208 208/214/207 184/189/183 1051 | f 210/216/209 209/215/208 185/190/184 1052 | f 211/217/210 210/216/209 186/191/185 1053 | f 212/218/211 211/217/210 187/192/186 1054 | f 213/219/212 212/218/211 188/193/187 1055 | f 214/220/213 213/219/212 189/194/188 1056 | f 215/221/214 214/220/213 190/195/189 1057 | f 216/222/215 215/221/214 191/196/190 1058 | f 217/223/216 216/222/215 192/197/191 1059 | f 218/224/217 217/223/216 193/198/192 1060 | f 195/225/194 218/224/217 194/199/193 1061 | f 220/227/219 219/226/218 195/201/194 1062 | f 221/228/220 220/227/219 196/202/195 1063 | f 222/229/221 221/228/220 197/203/196 1064 | f 223/230/222 222/229/221 198/204/197 1065 | f 224/231/223 223/230/222 199/205/198 1066 | f 225/232/224 224/231/223 200/206/199 1067 | f 226/233/225 225/232/224 201/207/200 1068 | f 227/234/226 226/233/225 202/208/201 1069 | f 228/235/227 227/234/226 203/209/202 1070 | f 229/236/228 228/235/227 204/210/203 1071 | f 230/237/229 229/236/228 205/211/204 1072 | f 231/238/230 230/237/229 206/212/205 1073 | f 232/239/231 231/238/230 207/213/206 1074 | f 233/240/232 232/239/231 208/214/207 1075 | f 234/241/233 233/240/232 209/215/208 1076 | f 235/242/234 234/241/233 210/216/209 1077 | f 236/243/235 235/242/234 211/217/210 1078 | f 237/244/236 236/243/235 212/218/211 1079 | f 238/245/237 237/244/236 213/219/212 1080 | f 239/246/238 238/245/237 214/220/213 1081 | f 240/247/239 239/246/238 215/221/214 1082 | f 241/248/240 240/247/239 216/222/215 1083 | f 242/249/241 241/248/240 217/223/216 1084 | f 219/250/218 242/249/241 218/224/217 1085 | f 244/252/243 243/251/242 219/226/218 1086 | f 245/253/244 244/252/243 220/227/219 1087 | f 246/254/245 245/253/244 221/228/220 1088 | f 247/255/246 246/254/245 222/229/221 1089 | f 248/256/247 247/255/246 223/230/222 1090 | f 249/257/248 248/256/247 224/231/223 1091 | f 250/258/249 249/257/248 225/232/224 1092 | f 251/259/250 250/258/249 226/233/225 1093 | f 252/260/251 251/259/250 227/234/226 1094 | f 253/261/252 252/260/251 228/235/227 1095 | f 254/262/253 253/261/252 229/236/228 1096 | f 255/263/254 254/262/253 230/237/229 1097 | f 256/264/255 255/263/254 231/238/230 1098 | f 257/265/256 256/264/255 232/239/231 1099 | f 258/266/257 257/265/256 233/240/232 1100 | f 259/267/258 258/266/257 234/241/233 1101 | f 260/268/259 259/267/258 235/242/234 1102 | f 261/269/260 260/268/259 236/243/235 1103 | f 262/270/261 261/269/260 237/244/236 1104 | f 263/271/262 262/270/261 238/245/237 1105 | f 264/272/263 263/271/262 239/246/238 1106 | f 265/273/264 264/272/263 240/247/239 1107 | f 266/274/265 265/273/264 241/248/240 1108 | f 243/275/242 266/274/265 242/249/241 1109 | f 4/4/5 3/1/1 1/276/266 1110 | f 2/277/267 243/251/242 244/252/243 1111 | f 5/6/7 4/4/5 1/278/266 1112 | f 2/279/267 244/252/243 245/253/244 1113 | f 6/8/9 5/6/7 1/280/266 1114 | f 2/281/267 245/253/244 246/254/245 1115 | f 7/10/11 6/8/9 1/282/266 1116 | f 2/283/267 246/254/245 247/255/246 1117 | f 8/12/13 7/10/11 1/284/266 1118 | f 2/285/267 247/255/246 248/256/247 1119 | f 9/14/15 8/12/13 1/286/266 1120 | f 2/287/267 248/256/247 249/257/248 1121 | f 10/16/17 9/14/15 1/288/266 1122 | f 2/289/267 249/257/248 250/258/249 1123 | f 11/18/19 10/16/17 1/290/266 1124 | f 2/291/267 250/258/249 251/259/250 1125 | f 12/20/21 11/18/19 1/292/266 1126 | f 2/293/267 251/259/250 252/260/251 1127 | f 13/22/23 12/20/21 1/294/266 1128 | f 2/295/267 252/260/251 253/261/252 1129 | f 14/24/25 13/22/23 1/296/266 1130 | f 2/297/267 253/261/252 254/262/253 1131 | f 15/26/27 14/24/25 1/298/266 1132 | f 2/299/267 254/262/253 255/263/254 1133 | f 16/28/29 15/26/27 1/300/266 1134 | f 2/301/267 255/263/254 256/264/255 1135 | f 17/30/31 16/28/29 1/302/266 1136 | f 2/303/267 256/264/255 257/265/256 1137 | f 18/32/33 17/30/31 1/304/266 1138 | f 2/305/267 257/265/256 258/266/257 1139 | f 19/34/35 18/32/33 1/306/266 1140 | f 2/307/267 258/266/257 259/267/258 1141 | f 20/36/37 19/34/35 1/308/266 1142 | f 2/309/267 259/267/258 260/268/259 1143 | f 21/38/39 20/36/37 1/310/266 1144 | f 2/311/267 260/268/259 261/269/260 1145 | f 22/40/41 21/38/39 1/312/266 1146 | f 2/313/267 261/269/260 262/270/261 1147 | f 23/42/43 22/40/41 1/314/266 1148 | f 2/315/267 262/270/261 263/271/262 1149 | f 24/44/45 23/42/43 1/316/266 1150 | f 2/317/267 263/271/262 264/272/263 1151 | f 25/46/47 24/44/45 1/318/266 1152 | f 2/319/267 264/272/263 265/273/264 1153 | f 26/48/49 25/46/47 1/320/266 1154 | f 2/321/267 265/273/264 266/274/265 1155 | f 3/50/1 26/48/49 1/322/266 1156 | f 2/323/267 266/274/265 243/275/242 1157 | f 4/4/5 28/3/3 3/1/1 1158 | f 5/6/7 29/5/6 4/4/5 1159 | f 6/8/9 30/7/8 5/6/7 1160 | f 7/10/11 31/9/10 6/8/9 1161 | f 8/12/13 32/11/12 7/10/11 1162 | f 9/14/15 33/13/14 8/12/13 1163 | f 10/16/17 34/15/16 9/14/15 1164 | f 11/18/19 35/17/18 10/16/17 1165 | f 12/20/21 36/19/20 11/18/19 1166 | f 13/22/23 37/21/22 12/20/21 1167 | f 14/24/25 38/23/24 13/22/23 1168 | f 15/26/27 39/25/26 14/24/25 1169 | f 16/28/29 40/27/28 15/26/27 1170 | f 17/30/31 41/29/30 16/28/29 1171 | f 18/32/33 42/31/32 17/30/31 1172 | f 19/34/35 43/33/34 18/32/33 1173 | f 20/36/37 44/35/36 19/34/35 1174 | f 21/38/39 45/37/38 20/36/37 1175 | f 22/40/41 46/39/40 21/38/39 1176 | f 23/42/43 47/41/42 22/40/41 1177 | f 24/44/45 48/43/44 23/42/43 1178 | f 25/46/47 49/45/46 24/44/45 1179 | f 26/48/49 50/47/48 25/46/47 1180 | f 3/50/1 27/49/2 26/48/49 1181 | f 28/3/3 52/52/51 27/2/2 1182 | f 29/5/6 53/53/52 28/3/3 1183 | f 30/7/8 54/54/53 29/5/6 1184 | f 31/9/10 55/55/54 30/7/8 1185 | f 32/11/12 56/56/55 31/9/10 1186 | f 33/13/14 57/57/56 32/11/12 1187 | f 34/15/16 58/58/57 33/13/14 1188 | f 35/17/18 59/59/58 34/15/16 1189 | f 36/19/20 60/60/59 35/17/18 1190 | f 37/21/22 61/61/60 36/19/20 1191 | f 38/23/24 62/62/61 37/21/22 1192 | f 39/25/26 63/63/62 38/23/24 1193 | f 40/27/28 64/64/63 39/25/26 1194 | f 41/29/30 65/65/64 40/27/28 1195 | f 42/31/32 66/66/65 41/29/30 1196 | f 43/33/34 67/67/66 42/31/32 1197 | f 44/35/36 68/68/67 43/33/34 1198 | f 45/37/38 69/69/68 44/35/36 1199 | f 46/39/40 70/70/69 45/37/38 1200 | f 47/41/42 71/71/70 46/39/40 1201 | f 48/43/44 72/72/71 47/41/42 1202 | f 49/45/46 73/73/72 48/43/44 1203 | f 50/47/48 74/74/73 49/45/46 1204 | f 27/49/2 51/75/50 50/47/48 1205 | f 52/52/51 76/77/75 51/51/50 1206 | f 53/53/52 77/78/76 52/52/51 1207 | f 54/54/53 78/79/77 53/53/52 1208 | f 55/55/54 79/80/78 54/54/53 1209 | f 56/56/55 80/81/79 55/55/54 1210 | f 57/57/56 81/82/80 56/56/55 1211 | f 58/58/57 82/83/81 57/57/56 1212 | f 59/59/58 83/84/82 58/58/57 1213 | f 60/60/59 84/85/83 59/59/58 1214 | f 61/61/60 85/86/84 60/60/59 1215 | f 62/62/61 86/87/85 61/61/60 1216 | f 63/63/62 87/88/86 62/62/61 1217 | f 64/64/63 88/89/87 63/63/62 1218 | f 65/65/64 89/90/88 64/64/63 1219 | f 66/66/65 90/91/89 65/65/64 1220 | f 67/67/66 91/92/90 66/66/65 1221 | f 68/68/67 92/93/91 67/67/66 1222 | f 69/69/68 93/94/92 68/68/67 1223 | f 70/70/69 94/95/93 69/69/68 1224 | f 71/71/70 95/96/94 70/70/69 1225 | f 72/72/71 96/97/95 71/71/70 1226 | f 73/73/72 97/98/96 72/72/71 1227 | f 74/74/73 98/99/97 73/73/72 1228 | f 51/75/50 75/100/74 74/74/73 1229 | f 76/77/75 100/102/99 75/76/74 1230 | f 77/78/76 101/103/100 76/77/75 1231 | f 78/79/77 102/104/101 77/78/76 1232 | f 79/80/78 103/105/102 78/79/77 1233 | f 80/81/79 104/106/103 79/80/78 1234 | f 81/82/80 105/107/104 80/81/79 1235 | f 82/83/81 106/108/105 81/82/80 1236 | f 83/84/82 107/109/106 82/83/81 1237 | f 84/85/83 108/110/107 83/84/82 1238 | f 85/86/84 109/111/108 84/85/83 1239 | f 86/87/85 110/112/109 85/86/84 1240 | f 87/88/86 111/113/110 86/87/85 1241 | f 88/89/87 112/114/111 87/88/86 1242 | f 89/90/88 113/115/112 88/89/87 1243 | f 90/91/89 114/116/113 89/90/88 1244 | f 91/92/90 115/117/114 90/91/89 1245 | f 92/93/91 116/118/115 91/92/90 1246 | f 93/94/92 117/119/116 92/93/91 1247 | f 94/95/93 118/120/117 93/94/92 1248 | f 95/96/94 119/121/118 94/95/93 1249 | f 96/97/95 120/122/119 95/96/94 1250 | f 97/98/96 121/123/120 96/97/95 1251 | f 98/99/97 122/124/121 97/98/96 1252 | f 75/100/74 99/125/98 98/99/97 1253 | f 100/102/99 124/127/123 99/101/98 1254 | f 101/103/100 125/128/124 100/102/99 1255 | f 102/104/101 126/129/125 101/103/100 1256 | f 103/105/102 127/130/126 102/104/101 1257 | f 104/106/103 128/131/127 103/105/102 1258 | f 105/107/104 129/132/128 104/106/103 1259 | f 106/108/105 130/133/129 105/107/104 1260 | f 107/109/106 131/134/130 106/108/105 1261 | f 108/110/107 132/135/131 107/109/106 1262 | f 109/111/108 133/136/132 108/110/107 1263 | f 110/112/109 134/137/133 109/111/108 1264 | f 111/113/110 135/138/134 110/112/109 1265 | f 112/114/111 136/139/135 111/113/110 1266 | f 113/115/112 137/140/136 112/114/111 1267 | f 114/116/113 138/141/137 113/115/112 1268 | f 115/117/114 139/142/138 114/116/113 1269 | f 116/118/115 140/143/139 115/117/114 1270 | f 117/119/116 141/144/140 116/118/115 1271 | f 118/120/117 142/145/141 117/119/116 1272 | f 119/121/118 143/146/142 118/120/117 1273 | f 120/122/119 144/147/143 119/121/118 1274 | f 121/123/120 145/148/144 120/122/119 1275 | f 122/124/121 146/149/145 121/123/120 1276 | f 99/125/98 123/150/122 122/124/121 1277 | f 124/127/123 148/152/147 123/126/122 1278 | f 125/128/124 149/153/148 124/127/123 1279 | f 126/129/125 150/154/149 125/128/124 1280 | f 127/130/126 151/155/150 126/129/125 1281 | f 128/131/127 152/156/151 127/130/126 1282 | f 129/132/128 153/157/152 128/131/127 1283 | f 130/133/129 154/158/153 129/132/128 1284 | f 131/134/130 155/159/154 130/133/129 1285 | f 132/135/131 156/160/155 131/134/130 1286 | f 133/136/132 157/161/156 132/135/131 1287 | f 134/137/133 158/162/157 133/136/132 1288 | f 135/138/134 159/163/158 134/137/133 1289 | f 136/139/135 160/164/159 135/138/134 1290 | f 137/140/136 161/165/160 136/139/135 1291 | f 138/141/137 162/166/161 137/140/136 1292 | f 139/142/138 163/167/162 138/141/137 1293 | f 140/143/139 164/168/163 139/142/138 1294 | f 141/144/140 165/169/164 140/143/139 1295 | f 142/145/141 166/170/165 141/144/140 1296 | f 143/146/142 167/171/166 142/145/141 1297 | f 144/147/143 168/172/167 143/146/142 1298 | f 145/148/144 169/173/168 144/147/143 1299 | f 146/149/145 170/174/169 145/148/144 1300 | f 123/150/122 147/175/146 146/149/145 1301 | f 148/152/147 172/177/171 147/151/146 1302 | f 149/153/148 173/178/172 148/152/147 1303 | f 150/154/149 174/179/173 149/153/148 1304 | f 151/155/150 175/180/174 150/154/149 1305 | f 152/156/151 176/181/175 151/155/150 1306 | f 153/157/152 177/182/176 152/156/151 1307 | f 154/158/153 178/183/177 153/157/152 1308 | f 155/159/154 179/184/178 154/158/153 1309 | f 156/160/155 180/185/179 155/159/154 1310 | f 157/161/156 181/186/180 156/160/155 1311 | f 158/162/157 182/187/181 157/161/156 1312 | f 159/163/158 183/188/182 158/162/157 1313 | f 160/164/159 184/189/183 159/163/158 1314 | f 161/165/160 185/190/184 160/164/159 1315 | f 162/166/161 186/191/185 161/165/160 1316 | f 163/167/162 187/192/186 162/166/161 1317 | f 164/168/163 188/193/187 163/167/162 1318 | f 165/169/164 189/194/188 164/168/163 1319 | f 166/170/165 190/195/189 165/169/164 1320 | f 167/171/166 191/196/190 166/170/165 1321 | f 168/172/167 192/197/191 167/171/166 1322 | f 169/173/168 193/198/192 168/172/167 1323 | f 170/174/169 194/199/193 169/173/168 1324 | f 147/175/146 171/200/170 170/174/169 1325 | f 172/177/171 196/202/195 171/176/170 1326 | f 173/178/172 197/203/196 172/177/171 1327 | f 174/179/173 198/204/197 173/178/172 1328 | f 175/180/174 199/205/198 174/179/173 1329 | f 176/181/175 200/206/199 175/180/174 1330 | f 177/182/176 201/207/200 176/181/175 1331 | f 178/183/177 202/208/201 177/182/176 1332 | f 179/184/178 203/209/202 178/183/177 1333 | f 180/185/179 204/210/203 179/184/178 1334 | f 181/186/180 205/211/204 180/185/179 1335 | f 182/187/181 206/212/205 181/186/180 1336 | f 183/188/182 207/213/206 182/187/181 1337 | f 184/189/183 208/214/207 183/188/182 1338 | f 185/190/184 209/215/208 184/189/183 1339 | f 186/191/185 210/216/209 185/190/184 1340 | f 187/192/186 211/217/210 186/191/185 1341 | f 188/193/187 212/218/211 187/192/186 1342 | f 189/194/188 213/219/212 188/193/187 1343 | f 190/195/189 214/220/213 189/194/188 1344 | f 191/196/190 215/221/214 190/195/189 1345 | f 192/197/191 216/222/215 191/196/190 1346 | f 193/198/192 217/223/216 192/197/191 1347 | f 194/199/193 218/224/217 193/198/192 1348 | f 171/200/170 195/225/194 194/199/193 1349 | f 196/202/195 220/227/219 195/201/194 1350 | f 197/203/196 221/228/220 196/202/195 1351 | f 198/204/197 222/229/221 197/203/196 1352 | f 199/205/198 223/230/222 198/204/197 1353 | f 200/206/199 224/231/223 199/205/198 1354 | f 201/207/200 225/232/224 200/206/199 1355 | f 202/208/201 226/233/225 201/207/200 1356 | f 203/209/202 227/234/226 202/208/201 1357 | f 204/210/203 228/235/227 203/209/202 1358 | f 205/211/204 229/236/228 204/210/203 1359 | f 206/212/205 230/237/229 205/211/204 1360 | f 207/213/206 231/238/230 206/212/205 1361 | f 208/214/207 232/239/231 207/213/206 1362 | f 209/215/208 233/240/232 208/214/207 1363 | f 210/216/209 234/241/233 209/215/208 1364 | f 211/217/210 235/242/234 210/216/209 1365 | f 212/218/211 236/243/235 211/217/210 1366 | f 213/219/212 237/244/236 212/218/211 1367 | f 214/220/213 238/245/237 213/219/212 1368 | f 215/221/214 239/246/238 214/220/213 1369 | f 216/222/215 240/247/239 215/221/214 1370 | f 217/223/216 241/248/240 216/222/215 1371 | f 218/224/217 242/249/241 217/223/216 1372 | f 195/225/194 219/250/218 218/224/217 1373 | f 220/227/219 244/252/243 219/226/218 1374 | f 221/228/220 245/253/244 220/227/219 1375 | f 222/229/221 246/254/245 221/228/220 1376 | f 223/230/222 247/255/246 222/229/221 1377 | f 224/231/223 248/256/247 223/230/222 1378 | f 225/232/224 249/257/248 224/231/223 1379 | f 226/233/225 250/258/249 225/232/224 1380 | f 227/234/226 251/259/250 226/233/225 1381 | f 228/235/227 252/260/251 227/234/226 1382 | f 229/236/228 253/261/252 228/235/227 1383 | f 230/237/229 254/262/253 229/236/228 1384 | f 231/238/230 255/263/254 230/237/229 1385 | f 232/239/231 256/264/255 231/238/230 1386 | f 233/240/232 257/265/256 232/239/231 1387 | f 234/241/233 258/266/257 233/240/232 1388 | f 235/242/234 259/267/258 234/241/233 1389 | f 236/243/235 260/268/259 235/242/234 1390 | f 237/244/236 261/269/260 236/243/235 1391 | f 238/245/237 262/270/261 237/244/236 1392 | f 239/246/238 263/271/262 238/245/237 1393 | f 240/247/239 264/272/263 239/246/238 1394 | f 241/248/240 265/273/264 240/247/239 1395 | f 242/249/241 266/274/265 241/248/240 1396 | f 219/250/218 243/275/242 242/249/241 1397 | -------------------------------------------------------------------------------- /src/lex.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::str; 3 | 4 | /// A parsing error, with location information. 5 | #[derive(Debug, PartialEq)] 6 | pub struct ParseError { 7 | /// The line of input the error is on. 8 | pub line_number: usize, 9 | /// The error message. 10 | pub message: String, 11 | } 12 | 13 | impl fmt::Display for ParseError { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 15 | write!(f, "parse error (L{}): {}", self.line_number, self.message) 16 | } 17 | } 18 | 19 | impl std::error::Error for ParseError {} 20 | 21 | #[inline] 22 | fn is_whitespace_except_newline(c: u8) -> bool { 23 | c == b' ' || c == b'\t' || c == b'\r' 24 | } 25 | 26 | #[inline] 27 | fn is_whitespace(c: u8) -> bool { 28 | is_whitespace_except_newline(c) || c == b'\n' 29 | } 30 | 31 | #[derive(Clone)] 32 | pub(crate) struct Lexer<'a> { 33 | bytes: &'a [u8], 34 | read_pos: usize, 35 | current_line_number: usize, 36 | } 37 | 38 | impl<'a> Lexer<'a> { 39 | pub(crate) fn new(input: &'a str) -> Lexer<'a> { 40 | Lexer { 41 | bytes: input.as_bytes(), 42 | read_pos: 0, 43 | current_line_number: 1, 44 | } 45 | } 46 | 47 | /// Advance the lexer by one character. 48 | fn advance(&mut self) { 49 | if let Some(b'\n') = self.peek() { 50 | self.current_line_number += 1; 51 | } 52 | self.read_pos += 1; 53 | } 54 | 55 | /// Looks at the next character the lexer is pointing to. 56 | fn peek(&self) -> Option<&u8> { 57 | self.bytes.get(self.read_pos) 58 | } 59 | 60 | /// Return the number of bytes read since the last lexer state provided as argument. 61 | /// 62 | /// Return [`None`] if the checkpoint parser is more advanced in the input than the current one — 63 | /// i.e. you have likely swapped the parsers, try calling the other way around! 64 | pub(crate) fn bytes_consumed(&self, checkpoint: &Self) -> Option { 65 | if self.read_pos < checkpoint.read_pos { 66 | None 67 | } else { 68 | Some(self.read_pos - checkpoint.read_pos) 69 | } 70 | } 71 | 72 | /// Advance past characters until the given condition is true. 73 | /// 74 | /// Returns whether or not any of the input was skipped. 75 | /// 76 | /// Postcondition: Either the end of the input was reached or 77 | /// `is_true` returns false for the currently peekable character. 78 | fn skip_while bool>(&mut self, is_true: F) -> bool { 79 | let mut was_anything_skipped = false; 80 | 81 | loop { 82 | match self.peek() { 83 | None => break, 84 | Some(&c) if !is_true(c) => break, 85 | _ => { 86 | self.advance(); 87 | was_anything_skipped = true; 88 | } 89 | } 90 | } 91 | 92 | debug_assert!(self.peek().map(|&c| !is_true(c)).unwrap_or(true)); 93 | 94 | was_anything_skipped 95 | } 96 | 97 | /// Advance past characters until the given condition is true. 98 | /// 99 | /// Returns whether or not any of the input was skipped. 100 | /// 101 | /// Postcondition: Either the end of the input was reached or 102 | /// `is_false` returns true for the currently peekable character. 103 | fn skip_unless bool>(&mut self, is_false: F) -> bool { 104 | self.skip_while(|c| !is_false(c)) 105 | } 106 | 107 | /// Advances past comments in the input, including their trailing newlines. 108 | /// 109 | /// Returns whether or not any of the input was skipped. 110 | fn skip_comment(&mut self) -> bool { 111 | match self.peek() { 112 | Some(b'#') => { 113 | // skip over the rest of the comment (except the newline) 114 | self.skip_unless(|c| c == b'\n'); 115 | true 116 | } 117 | _ => false, 118 | } 119 | } 120 | 121 | fn skip_whitespace_except_newline(&mut self) -> bool { 122 | self.skip_while(is_whitespace_except_newline) 123 | } 124 | 125 | /// Gets the next word in the input, as well as whether it's on 126 | /// a different line than the last word we got. 127 | fn next_word(&mut self) -> Option<&'a [u8]> { 128 | self.skip_whitespace_except_newline(); 129 | self.skip_comment(); 130 | 131 | let start_ptr = self.read_pos; 132 | 133 | match self.peek() { 134 | Some(b'\n') => { 135 | self.advance(); 136 | self.bytes.get(start_ptr..self.read_pos) // newline 137 | } 138 | Some(_) => { 139 | if self.skip_unless(|c| is_whitespace(c) || c == b'#') { 140 | self.bytes.get(start_ptr..self.read_pos) 141 | } else { 142 | None 143 | } 144 | } 145 | None => None, 146 | } 147 | } 148 | } 149 | 150 | #[derive(Clone)] 151 | pub(crate) struct PeekableLexer<'a> { 152 | inner: Lexer<'a>, 153 | peeked: Option>, 154 | } 155 | 156 | impl<'a> PeekableLexer<'a> { 157 | pub(crate) fn new(lexer: Lexer<'a>) -> Self { 158 | Self { 159 | inner: lexer, 160 | peeked: None, 161 | } 162 | } 163 | pub(crate) fn next_str(&mut self) -> Option<&'a str> { 164 | match self.peeked.take() { 165 | Some(v) => v, 166 | None => self 167 | .inner 168 | .next_word() 169 | .map(|buf| unsafe { str::from_utf8_unchecked(buf) }), 170 | } 171 | } 172 | 173 | pub(crate) fn peek_str(&mut self) -> Option<&'a str> { 174 | match self.peeked { 175 | Some(v) => v, 176 | None => { 177 | let peek = self 178 | .inner 179 | .next_word() 180 | .map(|buf| unsafe { str::from_utf8_unchecked(buf) }); 181 | 182 | self.peeked.replace(peek); 183 | peek 184 | } 185 | } 186 | } 187 | 188 | /// Return the number of bytes read since the last lexer state provided as argument. 189 | /// 190 | /// Return [`None`] if the checkpoint parser is more advanced in the input than the current one — 191 | /// i.e. you have likely swapped the parsers, try calling the other way around! 192 | pub(crate) fn bytes_consumed(&self, checkpoint: &Self) -> Option { 193 | self.inner.bytes_consumed(&checkpoint.inner) 194 | } 195 | } 196 | 197 | #[test] 198 | fn test_next_word() { 199 | let mut l = Lexer::new("hello wor\rld\n this# is\r\na \t test\n"); 200 | assert_eq!(l.next_word(), Some(&b"hello"[..])); 201 | assert_eq!(l.current_line_number, 1); 202 | assert_eq!(l.next_word(), Some(&b"wor"[..])); 203 | assert_eq!(l.current_line_number, 1); 204 | assert_eq!(l.next_word(), Some(&b"ld"[..])); 205 | assert_eq!(l.current_line_number, 1); 206 | assert_eq!(l.next_word(), Some(&b"\n"[..])); 207 | assert_eq!(l.current_line_number, 2); 208 | assert_eq!(l.next_word(), Some(&b"this"[..])); 209 | assert_eq!(l.current_line_number, 2); 210 | assert_eq!(l.next_word(), Some(&b"\n"[..])); 211 | assert_eq!(l.current_line_number, 3); 212 | assert_eq!(l.next_word(), Some(&b"a"[..])); 213 | assert_eq!(l.current_line_number, 3); 214 | assert_eq!(l.next_word(), Some(&b"test"[..])); 215 | assert_eq!(l.current_line_number, 3); 216 | assert_eq!(l.next_word(), Some(&b"\n"[..])); 217 | assert_eq!(l.current_line_number, 4); 218 | assert_eq!(l.next_word(), None); 219 | } 220 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Parsers for wavefront's `.obj` and `.mtl` file format for loading meshes. 2 | #![crate_type = "lib"] 3 | #![deny(warnings)] 4 | #![deny(missing_docs)] 5 | #![deny(unreachable_pub)] 6 | 7 | pub use lex::ParseError; 8 | 9 | mod lex; 10 | mod util; 11 | 12 | pub mod mtl; 13 | pub mod obj; 14 | -------------------------------------------------------------------------------- /src/mtl.rs: -------------------------------------------------------------------------------- 1 | //! A parser for Wavefront's `.mtl` file format, for storing information about 2 | //! the material of which a 3D mesh is composed. 3 | use std::borrow::ToOwned; 4 | use std::cmp::Ordering; 5 | use std::cmp::Ordering::{Equal, Greater, Less}; 6 | 7 | pub use lex::ParseError; 8 | use lex::{Lexer, PeekableLexer}; 9 | use util::OrderingExt; 10 | 11 | /// A set of materials in one `.mtl` file. 12 | #[derive(Clone, Debug, PartialEq)] 13 | #[allow(missing_docs)] 14 | pub struct MtlSet { 15 | pub materials: Vec, 16 | } 17 | 18 | /// A single material that can be applied to any face. They are generally 19 | /// applied by using the Phong shading model. 20 | #[derive(Clone, Debug)] 21 | #[allow(missing_docs)] 22 | pub struct Material { 23 | pub name: String, 24 | pub specular_coefficient: f64, 25 | pub color_ambient: Color, 26 | pub color_diffuse: Color, 27 | pub color_specular: Color, 28 | pub color_emissive: Option, 29 | pub optical_density: Option, 30 | pub alpha: f64, 31 | pub illumination: Illumination, 32 | pub ambient_map: Option, 33 | pub diffuse_map: Option, 34 | pub specular_map: Option, 35 | pub specular_exponent_map: Option, 36 | pub dissolve_map: Option, 37 | pub displacement_map: Option, 38 | pub decal_map: Option, 39 | pub bump_map: Option, 40 | } 41 | 42 | /// How a given material is supposed to be illuminated. 43 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] 44 | #[allow(missing_docs)] 45 | pub enum Illumination { 46 | Ambient, 47 | AmbientDiffuse, 48 | AmbientDiffuseSpecular, 49 | ReflectionRayTrace, 50 | } 51 | 52 | #[derive(Clone, Copy, Debug)] 53 | #[allow(missing_docs)] 54 | pub struct Color { 55 | pub r: f64, 56 | pub g: f64, 57 | pub b: f64, 58 | } 59 | 60 | fn fuzzy_cmp(a: f64, b: f64, delta: f64) -> Ordering { 61 | if (a - b).abs() <= delta { 62 | Equal 63 | } else if a < b { 64 | Less 65 | } else { 66 | Greater 67 | } 68 | } 69 | 70 | fn fuzzy_opt_cmp(a: Option, b: Option, delta: f64) -> Ordering { 71 | match (a, b) { 72 | (None, None) => Equal, 73 | (Some(_), None) => Greater, 74 | (None, Some(_)) => Less, 75 | (Some(a), Some(b)) => fuzzy_cmp(a, b, delta), 76 | } 77 | } 78 | 79 | impl PartialEq for Color { 80 | fn eq(&self, other: &Color) -> bool { 81 | self.partial_cmp(other).unwrap() == Equal 82 | } 83 | } 84 | 85 | impl PartialOrd for Color { 86 | fn partial_cmp(&self, other: &Color) -> Option { 87 | Some( 88 | fuzzy_cmp(self.r, other.r, 0.00001) 89 | .lexico(|| fuzzy_cmp(self.g, other.g, 0.00001)) 90 | .lexico(|| fuzzy_cmp(self.b, other.b, 0.00001)), 91 | ) 92 | } 93 | } 94 | 95 | impl PartialEq for Material { 96 | fn eq(&self, other: &Material) -> bool { 97 | self.partial_cmp(other).unwrap() == Equal 98 | } 99 | } 100 | 101 | impl PartialOrd for Material { 102 | fn partial_cmp(&self, other: &Material) -> Option { 103 | Some( 104 | self 105 | .name 106 | .cmp(&other.name) 107 | .lexico(|| { 108 | fuzzy_cmp( 109 | self.specular_coefficient, 110 | other.specular_coefficient, 111 | 0.00001, 112 | ) 113 | }) 114 | .lexico(|| { 115 | self 116 | .color_ambient 117 | .partial_cmp(&other.color_ambient) 118 | .unwrap() 119 | }) 120 | .lexico(|| { 121 | self 122 | .color_diffuse 123 | .partial_cmp(&other.color_diffuse) 124 | .unwrap() 125 | }) 126 | .lexico(|| { 127 | self 128 | .color_specular 129 | .partial_cmp(&other.color_specular) 130 | .unwrap() 131 | }) 132 | .lexico(|| fuzzy_opt_cmp(self.optical_density, other.optical_density, 0.00001)) 133 | .lexico(|| fuzzy_cmp(self.alpha, other.alpha, 0.00001)) 134 | .lexico(|| self.illumination.cmp(&other.illumination)) 135 | .lexico(|| self.ambient_map.cmp(&other.ambient_map)) 136 | .lexico(|| self.diffuse_map.cmp(&other.diffuse_map)) 137 | .lexico(|| self.specular_map.cmp(&other.specular_map)) 138 | .lexico(|| self.specular_exponent_map.cmp(&other.specular_exponent_map)) 139 | .lexico(|| self.dissolve_map.cmp(&other.dissolve_map)) 140 | .lexico(|| self.displacement_map.cmp(&other.displacement_map)) 141 | .lexico(|| self.decal_map.cmp(&other.decal_map)) 142 | .lexico(|| self.bump_map.cmp(&other.bump_map)), 143 | ) 144 | } 145 | } 146 | 147 | struct Parser<'a> { 148 | line_number: usize, 149 | lexer: PeekableLexer<'a>, 150 | } 151 | 152 | impl<'a> Parser<'a> { 153 | fn new(input: &'a str) -> Parser<'a> { 154 | Parser { 155 | line_number: 1, 156 | lexer: PeekableLexer::new(Lexer::new(input)), 157 | } 158 | } 159 | 160 | fn error_raw(&self, msg: String) -> ParseError { 161 | ParseError { 162 | line_number: self.line_number, 163 | message: msg, 164 | } 165 | } 166 | 167 | fn error(&self, msg: String) -> Result { 168 | Err(self.error_raw(msg)) 169 | } 170 | 171 | fn next(&mut self) -> Option<&'a str> { 172 | let ret = self.lexer.next_str(); 173 | if let Some("\n") = ret { 174 | self.line_number += 1; 175 | } 176 | ret 177 | } 178 | 179 | fn advance(&mut self) { 180 | self.next(); 181 | } 182 | 183 | fn peek(&mut self) -> Option<&'a str> { 184 | self.lexer.peek_str() 185 | } 186 | 187 | /// Possibly skips over some newlines. 188 | fn zero_or_more_newlines(&mut self) { 189 | while let Some("\n") = self.peek() { 190 | self.advance(); 191 | } 192 | } 193 | /// Skips over some newlines, failing if it didn't manage to skip any. 194 | fn one_or_more_newlines(&mut self) -> Result<(), ParseError> { 195 | match self.peek() { 196 | None => {} // allow eof 197 | Some("\n") => {} 198 | Some(s) => return self.error(format!("Expected newline but got {}", s)), 199 | } 200 | 201 | self.zero_or_more_newlines(); 202 | 203 | Ok(()) 204 | } 205 | 206 | fn parse_newmtl(&mut self) -> Result<&'a str, ParseError> { 207 | match self.next() { 208 | None => return self.error("Expected `newmtl` but got end of input.".to_owned()), 209 | Some("newmtl") => {} 210 | Some(s) => return self.error(format!("Expected `newmtl` but got {}.", s)), 211 | } 212 | 213 | match self.next() { 214 | None => return self.error("Expected material name but got end of input.".to_owned()), 215 | Some(s) => Ok(s), 216 | } 217 | } 218 | 219 | fn parse_f64(&mut self) -> Result { 220 | match self.next() { 221 | None => self.error("Expected f64 but got end of input.".to_owned()), 222 | Some(s) => s 223 | .parse() 224 | .map_err(|_| self.error_raw(format!("Expected f64 but got {}.", s))), 225 | } 226 | } 227 | 228 | fn parse_usize(&mut self) -> Result { 229 | match self.next() { 230 | None => self.error("Expected usize but got end of input.".to_owned()), 231 | Some(s) => s 232 | .parse() 233 | .map_err(|_| self.error_raw(format!("Expected usize but got {}.", s))), 234 | } 235 | } 236 | 237 | fn parse_tag(&mut self, tag: &str) -> Result<(), ParseError> { 238 | match self.next() { 239 | None => self.error(format!("Expected `{}` but got end of input.", tag)), 240 | Some(s) if s != tag => self.error(format!("Expected `{}` but got {}.", tag, s)), 241 | _ => Ok(()), 242 | } 243 | } 244 | 245 | fn parse_color(&mut self) -> Result { 246 | let r = self.parse_f64()?; 247 | let g = self.parse_f64()?; 248 | let b = self.parse_f64()?; 249 | 250 | Ok(Color { r, g, b }) 251 | } 252 | 253 | fn parse_specular_coefficeint(&mut self) -> Result { 254 | self.parse_tag("Ns")?; 255 | self.parse_f64() 256 | } 257 | 258 | fn parse_ambient_color(&mut self) -> Result { 259 | self.parse_tag("Ka")?; 260 | self.parse_color() 261 | } 262 | 263 | fn parse_diffuse_color(&mut self) -> Result { 264 | self.parse_tag("Kd")?; 265 | self.parse_color() 266 | } 267 | 268 | fn parse_specular_color(&mut self) -> Result { 269 | self.parse_tag("Ks")?; 270 | self.parse_color() 271 | } 272 | 273 | fn parse_emissive_color(&mut self) -> Result, ParseError> { 274 | if self.peek() != Some("Ke") { 275 | return Ok(None); 276 | } 277 | self.parse_tag("Ke")?; 278 | self.parse_color().map(|c| Some(c)) 279 | } 280 | 281 | fn parse_optical_density(&mut self) -> Result, ParseError> { 282 | match self.peek() { 283 | Some("Ni") => {} 284 | _ => return Ok(None), 285 | } 286 | 287 | self.parse_tag("Ni")?; 288 | let optical_density = self.parse_f64()?; 289 | Ok(Some(optical_density)) 290 | } 291 | 292 | fn parse_dissolve(&mut self) -> Result { 293 | self.parse_tag("d")?; 294 | self.parse_f64() 295 | } 296 | 297 | fn parse_illumination(&mut self) -> Result { 298 | self.parse_tag("illum")?; 299 | match self.parse_usize()? { 300 | 0 => Ok(Illumination::Ambient), 301 | 1 => Ok(Illumination::AmbientDiffuse), 302 | 2 => Ok(Illumination::AmbientDiffuseSpecular), 303 | 3 => Ok(Illumination::ReflectionRayTrace), 304 | n => self.error(format!("Unknown illumination model: {}.", n)), 305 | } 306 | } 307 | 308 | fn parse_map(&mut self, name: &'static str) -> Result, ParseError> { 309 | match self.peek() { 310 | Some(s) => { 311 | if s != name { 312 | return Ok(None); 313 | } 314 | } 315 | _ => return Ok(None), 316 | } 317 | 318 | self.parse_tag(name)?; 319 | match self.next() { 320 | None => self.error("Expected texture path but got end of input.".to_owned()), 321 | Some(s) => Ok(Some(s)), 322 | } 323 | } 324 | 325 | fn parse_material(&mut self) -> Result { 326 | let name = self.parse_newmtl()?; 327 | self.one_or_more_newlines()?; 328 | let spec_coeff = self.parse_specular_coefficeint()?; 329 | self.one_or_more_newlines()?; 330 | let amb = self.parse_ambient_color()?; 331 | self.one_or_more_newlines()?; 332 | let diff = self.parse_diffuse_color()?; 333 | self.one_or_more_newlines()?; 334 | let spec = self.parse_specular_color()?; 335 | self.one_or_more_newlines()?; 336 | let emit = self.parse_emissive_color()?; 337 | if emit.is_some() { 338 | self.one_or_more_newlines()?; 339 | } 340 | let optical_density = self.parse_optical_density()?; 341 | if optical_density.is_some() { 342 | self.one_or_more_newlines()?; 343 | } 344 | let dissolve = self.parse_dissolve()?; 345 | self.one_or_more_newlines()?; 346 | let illum = self.parse_illumination()?; 347 | self.one_or_more_newlines()?; 348 | 349 | // Parse maps 350 | // Color textures 351 | let ambient_map = self.parse_map("map_Ka")?; 352 | if ambient_map.is_some() { 353 | self.one_or_more_newlines()?; 354 | } 355 | let diffuse_map = self.parse_map("map_Kd")?; 356 | if diffuse_map.is_some() { 357 | self.one_or_more_newlines()?; 358 | } 359 | let specular_map = self.parse_map("map_Ks")?; 360 | if specular_map.is_some() { 361 | self.one_or_more_newlines()?; 362 | } 363 | 364 | // Scalar textures 365 | let spec_exp_map = self.parse_map("map_Ns")?; 366 | if spec_exp_map.is_some() { 367 | self.one_or_more_newlines()?; 368 | } 369 | let dissolve_map = self.parse_map("map_d")?; 370 | if dissolve_map.is_some() { 371 | self.one_or_more_newlines()?; 372 | } 373 | let disp_map = self.parse_map("disp")?; 374 | if disp_map.is_some() { 375 | self.one_or_more_newlines()?; 376 | } 377 | 378 | // Decal (roughness) texture 379 | let decal = self.parse_map("decal")?; 380 | if decal.is_some() { 381 | self.one_or_more_newlines()?; 382 | } 383 | 384 | // Bump texture 385 | let mut bump = self.parse_map("map_bump")?; 386 | if bump.is_none() { 387 | // Some implementations use map_bump instead 388 | bump = self.parse_map("map_bump")?; 389 | } 390 | if bump.is_some() { 391 | self.one_or_more_newlines()?; 392 | } 393 | 394 | Ok(Material { 395 | name: name.to_owned(), 396 | specular_coefficient: spec_coeff, 397 | color_ambient: amb, 398 | color_diffuse: diff, 399 | color_specular: spec, 400 | color_emissive: emit, 401 | optical_density, 402 | alpha: dissolve, 403 | illumination: illum, 404 | ambient_map: ambient_map.map(|s| s.to_owned()), 405 | diffuse_map: diffuse_map.map(|s| s.to_owned()), 406 | specular_map: specular_map.map(|s| s.to_owned()), 407 | specular_exponent_map: spec_exp_map.map(|s| s.to_owned()), 408 | dissolve_map: dissolve_map.map(|s| s.to_owned()), 409 | displacement_map: disp_map.map(|s| s.to_owned()), 410 | decal_map: decal.map(|s| s.to_owned()), 411 | bump_map: bump.map(|s| s.to_owned()), 412 | }) 413 | } 414 | 415 | fn parse_mtlset(&mut self) -> Result { 416 | self.zero_or_more_newlines(); 417 | 418 | let mut ret = Vec::new(); 419 | 420 | loop { 421 | match self.peek() { 422 | Some("newmtl") => { 423 | ret.push(self.parse_material()?); 424 | } 425 | _ => break, 426 | } 427 | } 428 | 429 | match self.peek() { 430 | None => {} 431 | Some(s) => return self.error(format!("Expected end of input but got {}.", s)), 432 | } 433 | 434 | Ok(MtlSet { materials: ret }) 435 | } 436 | } 437 | 438 | /// Parses a wavefront `.mtl` file, returning either the successfully parsed 439 | /// file, or an error. Support in this parser for the full file format is 440 | /// best-effort and realistically I will only end up supporting the subset 441 | /// of the file format which falls under the "shit I see exported from blender" 442 | /// category. 443 | pub fn parse>(input: S) -> Result { 444 | Parser::new(input.as_ref()).parse_mtlset() 445 | } 446 | 447 | #[test] 448 | fn test_parse() { 449 | use self::Illumination::AmbientDiffuseSpecular; 450 | 451 | let test_case = r#" 452 | # Blender MTL File: 'None' 453 | # Material Count: 2 454 | 455 | # name 456 | newmtl Material 457 | # Phong specular coefficient 458 | Ns 96.078431 459 | # ambient color (weighted) 460 | Ka 0.000000 0.000000 0.000000 461 | # diffuse color (weighted) 462 | Kd 0.640000 0.640000 0.640000 463 | # dissolve factor (weighted) 464 | Ks 0.500000 0.500000 0.500000 465 | # emissive color (weighted) 466 | Ke 0.100000 0.100000 0.100000 467 | # optical density (refraction) 468 | Ni 1.000000 469 | # alpha 470 | d 1.000000 471 | # illumination: 0=ambient, 1=ambient+diffuse, 2=ambient+diffuse+specular 472 | illum 2 473 | 474 | newmtl None 475 | Ns 0 476 | # ambient 477 | Ka 0.000000 0.000000 0.000000 478 | # diffuse 479 | Kd 0.8 0.8 0.8 480 | # specular 481 | Ks 0.8 0.8 0.8 482 | d 1 483 | illum 2"#; 484 | 485 | let expected = Ok(MtlSet { 486 | materials: vec![ 487 | Material { 488 | name: "Material".to_owned(), 489 | specular_coefficient: 96.078431, 490 | color_ambient: Color { 491 | r: 0.0, 492 | g: 0.0, 493 | b: 0.0, 494 | }, 495 | color_diffuse: Color { 496 | r: 0.64, 497 | g: 0.64, 498 | b: 0.64, 499 | }, 500 | color_specular: Color { 501 | r: 0.5, 502 | g: 0.5, 503 | b: 0.5, 504 | }, 505 | color_emissive: Some(Color { 506 | r: 0.1, 507 | g: 0.1, 508 | b: 0.1, 509 | }), 510 | optical_density: Some(1.0), 511 | alpha: 1.0, 512 | illumination: AmbientDiffuseSpecular, 513 | ambient_map: None, 514 | diffuse_map: None, 515 | specular_map: None, 516 | specular_exponent_map: None, 517 | dissolve_map: None, 518 | displacement_map: None, 519 | decal_map: None, 520 | bump_map: None, 521 | }, 522 | Material { 523 | name: "None".to_owned(), 524 | specular_coefficient: 0.0, 525 | color_ambient: Color { 526 | r: 0.0, 527 | g: 0.0, 528 | b: 0.0, 529 | }, 530 | color_diffuse: Color { 531 | r: 0.8, 532 | g: 0.8, 533 | b: 0.8, 534 | }, 535 | color_specular: Color { 536 | r: 0.8, 537 | g: 0.8, 538 | b: 0.8, 539 | }, 540 | color_emissive: None, 541 | optical_density: None, 542 | alpha: 1.0, 543 | illumination: AmbientDiffuseSpecular, 544 | ambient_map: None, 545 | diffuse_map: None, 546 | specular_map: None, 547 | specular_exponent_map: None, 548 | dissolve_map: None, 549 | displacement_map: None, 550 | decal_map: None, 551 | bump_map: None, 552 | }, 553 | ], 554 | }); 555 | 556 | assert_eq!(parse(test_case), expected); 557 | } 558 | 559 | #[test] 560 | fn test_cube() { 561 | use self::Illumination::AmbientDiffuseSpecular; 562 | 563 | let test_case = r#" 564 | # Blender MTL File: 'cube.blend' 565 | # Material Count: 1 566 | 567 | newmtl Material 568 | Ns 96.078431 569 | Ka 0.000000 0.000000 0.000000 570 | Kd 0.640000 0.640000 0.640000 571 | Ks 0.500000 0.500000 0.500000 572 | Ke 0.000000 0.000000 0.000000 573 | Ni 1.000000 574 | d 1.000000 575 | illum 2 576 | map_Ka cube-ambient.png 577 | map_Kd cube-diff.png 578 | map_Ks cube-spec-color.png 579 | map_Ns cube-spec-exp.mps 580 | map_d cube-alpha.mps 581 | disp cube-disp.mps 582 | decal cube-roughness.mps 583 | map_bump cube-bump.mpb 584 | "#; 585 | 586 | let expected = Ok(MtlSet { 587 | materials: vec![Material { 588 | name: "Material".to_owned(), 589 | specular_coefficient: 96.078431, 590 | color_ambient: Color { 591 | r: 0.0, 592 | g: 0.0, 593 | b: 0.0, 594 | }, 595 | color_diffuse: Color { 596 | r: 0.64, 597 | g: 0.64, 598 | b: 0.64, 599 | }, 600 | color_specular: Color { 601 | r: 0.5, 602 | g: 0.5, 603 | b: 0.5, 604 | }, 605 | color_emissive: Some(Color { 606 | r: 0.0, 607 | g: 0.0, 608 | b: 0.0, 609 | }), 610 | optical_density: Some(1.0), 611 | alpha: 1.0, 612 | illumination: AmbientDiffuseSpecular, 613 | ambient_map: Some("cube-ambient.png".to_owned()), 614 | diffuse_map: Some("cube-diff.png".to_owned()), 615 | specular_map: Some("cube-spec-color.png".to_owned()), 616 | specular_exponent_map: Some("cube-spec-exp.mps".to_owned()), 617 | dissolve_map: Some("cube-alpha.mps".to_owned()), 618 | displacement_map: Some("cube-disp.mps".to_owned()), 619 | decal_map: Some("cube-roughness.mps".to_owned()), 620 | bump_map: Some("cube-bump.mpb".to_owned()), 621 | }], 622 | }); 623 | 624 | assert_eq!(parse(test_case), expected); 625 | } 626 | 627 | #[test] 628 | fn test_missing_maps() { 629 | use self::Illumination::AmbientDiffuseSpecular; 630 | 631 | let test_case = r#" 632 | newmtl Material 633 | Ns 96.078431 634 | Ka 0.000000 0.000000 0.000000 635 | Kd 0.640000 0.640000 0.640000 636 | Ks 0.500000 0.500000 0.500000 637 | Ke 0.000000 0.000000 0.000000 638 | Ni 1.000000 639 | d 1.000000 640 | illum 2 641 | map_Ka ambient.png 642 | map_Ns spec-exp.mps 643 | map_d alpha.mps 644 | map_bump bump.mpb 645 | "#; 646 | 647 | let expected = Ok(MtlSet { 648 | materials: vec![Material { 649 | name: "Material".to_owned(), 650 | specular_coefficient: 96.078431, 651 | color_ambient: Color { 652 | r: 0.0, 653 | g: 0.0, 654 | b: 0.0, 655 | }, 656 | color_diffuse: Color { 657 | r: 0.64, 658 | g: 0.64, 659 | b: 0.64, 660 | }, 661 | color_specular: Color { 662 | r: 0.5, 663 | g: 0.5, 664 | b: 0.5, 665 | }, 666 | color_emissive: Some(Color { 667 | r: 0.0, 668 | g: 0.0, 669 | b: 0.0, 670 | }), 671 | optical_density: Some(1.0), 672 | alpha: 1.0, 673 | illumination: AmbientDiffuseSpecular, 674 | ambient_map: Some("ambient.png".to_owned()), 675 | diffuse_map: None, 676 | specular_map: None, 677 | specular_exponent_map: Some("spec-exp.mps".to_owned()), 678 | dissolve_map: Some("alpha.mps".to_owned()), 679 | displacement_map: None, 680 | decal_map: None, 681 | bump_map: Some("bump.mpb".to_owned()), 682 | }], 683 | }); 684 | 685 | assert_eq!(parse(test_case), expected); 686 | } 687 | -------------------------------------------------------------------------------- /src/obj.rs: -------------------------------------------------------------------------------- 1 | //! A parser for Wavefront's `.obj` file format for storing 3D meshes. 2 | use lex::PeekableLexer; 3 | use std::borrow::ToOwned; 4 | use std::cmp::Ordering; 5 | use std::cmp::Ordering::{Equal, Greater, Less}; 6 | use std::mem; 7 | 8 | use lex::{Lexer, ParseError}; 9 | use util::OrderingExt; 10 | 11 | /// A set of objects, as listed in an `.obj` file. 12 | #[derive(Clone, Debug, PartialEq)] 13 | pub struct ObjSet { 14 | /// Which material library to use. 15 | pub material_library: Option, 16 | /// The set of objects. 17 | pub objects: Vec, 18 | } 19 | 20 | /// A mesh object. 21 | #[derive(Clone, Debug, PartialEq)] 22 | pub struct Object { 23 | /// A human-readable name for this object. 24 | pub name: String, 25 | /// The set of vertices this object is composed of. These are referenced 26 | /// by index in `shapes` contained within each element of `geometry`. 27 | pub vertices: Vec, 28 | /// The set of texture vertices referenced by this object. The actual 29 | /// vertices are indexed by the second element in a `VTNIndex`. 30 | pub tex_vertices: Vec, 31 | /// The set of normals referenced by this object. This are are referenced 32 | /// by the third element in a `VTNIndex`. 33 | pub normals: Vec, 34 | /// A set of shapes (with materials applied to them) of which this object is 35 | /// composed. 36 | pub geometry: Vec, 37 | } 38 | 39 | /// A set of shapes, all using the given material. 40 | #[derive(Clone, Debug, PartialEq)] 41 | pub struct Geometry { 42 | /// A reference to the material to apply to this geometry. 43 | pub material_name: Option, 44 | /// The shapes of which this geometry is composed. 45 | pub shapes: Vec, 46 | } 47 | 48 | /// A shape gathers a primitive and groups. 49 | /// 50 | /// Each shape is associated with 0 or many groups. Those are text identifiers 51 | /// used to gather geometry elements into different groups. 52 | #[derive(Clone, Debug, PartialEq)] 53 | pub struct Shape { 54 | /// The primitive of the shape. 55 | pub primitive: Primitive, 56 | /// Associated groups. No associated group means the shape uses the default 57 | /// group. 58 | pub groups: Vec, 59 | /// Associated smoothing groups. No associated smoothing group means the shape should be rendered 60 | /// flat. 61 | pub smoothing_groups: Vec, 62 | } 63 | 64 | /// Name of a group. 65 | pub type GroupName = String; 66 | 67 | /// The various primitives supported by this library. 68 | /// 69 | /// Convex polygons more complicated than a triangle are automatically 70 | /// converted into triangles. 71 | #[derive(Clone, Copy, Debug, Hash, PartialEq)] 72 | pub enum Primitive { 73 | /// A point specified by its position. 74 | Point(VTNIndex), 75 | /// A line specified by its endpoints. 76 | Line(VTNIndex, VTNIndex), 77 | /// A triangle specified by its three vertices. 78 | Triangle(VTNIndex, VTNIndex, VTNIndex), 79 | } 80 | 81 | /// A single 3-dimensional point on the corner of an object. 82 | #[allow(missing_docs)] 83 | #[derive(Clone, Copy, Debug)] 84 | pub struct Vertex { 85 | pub x: f64, 86 | pub y: f64, 87 | pub z: f64, 88 | } 89 | 90 | /// A single 3-dimensional normal 91 | pub type Normal = Vertex; 92 | 93 | /// A single 3-dimensional point on a texture. "Texure Vertex". 94 | #[allow(missing_docs)] 95 | #[derive(Clone, Copy, Debug)] 96 | pub struct TVertex { 97 | pub u: f64, 98 | pub v: f64, 99 | pub w: f64, 100 | } 101 | 102 | fn fuzzy_cmp(a: f64, b: f64, delta: f64) -> Ordering { 103 | if (a - b).abs() <= delta { 104 | Equal 105 | } else if a < b { 106 | Less 107 | } else { 108 | Greater 109 | } 110 | } 111 | 112 | // TODO(cgaebel): Can we implement Eq here? 113 | impl PartialEq for Vertex { 114 | fn eq(&self, other: &Vertex) -> bool { 115 | self.partial_cmp(other).unwrap() == Equal 116 | } 117 | } 118 | 119 | impl PartialOrd for Vertex { 120 | fn partial_cmp(&self, other: &Vertex) -> Option { 121 | Some( 122 | fuzzy_cmp(self.x, other.x, 0.00001) 123 | .lexico(|| fuzzy_cmp(self.y, other.y, 0.00001)) 124 | .lexico(|| fuzzy_cmp(self.z, other.z, 0.00001)), 125 | ) 126 | } 127 | } 128 | 129 | impl PartialEq for TVertex { 130 | fn eq(&self, other: &TVertex) -> bool { 131 | self.partial_cmp(other).unwrap() == Equal 132 | } 133 | } 134 | 135 | impl PartialOrd for TVertex { 136 | fn partial_cmp(&self, other: &TVertex) -> Option { 137 | Some( 138 | fuzzy_cmp(self.u, other.u, 0.00001) 139 | .lexico(|| fuzzy_cmp(self.v, other.v, 0.00001)) 140 | .lexico(|| fuzzy_cmp(self.w, other.w, 0.00001)), 141 | ) 142 | } 143 | } 144 | 145 | /// An index into the `vertices` array of an object, representing a vertex in 146 | /// the mesh. After parsing, this is guaranteed to be a valid index into the 147 | /// array, so unchecked indexing may be used. 148 | pub type VertexIndex = usize; 149 | 150 | /// An index into the `texture vertex` array of an object. 151 | /// 152 | /// Unchecked indexing may be used, because the values are guaranteed to be in 153 | /// range by the parser. 154 | pub type TextureIndex = usize; 155 | 156 | /// An index into the `normals` array of an object. 157 | /// 158 | /// Unchecked indexing may be used, because the values are guaranteed to be in 159 | /// range by the parser. 160 | pub type NormalIndex = usize; 161 | 162 | /// An index into the vertex array, with an optional index into the texture 163 | /// array. This is used to define the corners of shapes which may or may not 164 | /// be textured. 165 | pub type VTNIndex = (VertexIndex, Option, Option); 166 | 167 | /// Blender exports primitives as a list of the vertices representing their corners. 168 | /// This function turns that into a set of OpenGL-usable shapes - i.e. points, 169 | /// lines, or triangles. 170 | fn to_triangles(xs: &[VTNIndex]) -> Vec { 171 | match xs.len() { 172 | 0 => return vec![], 173 | 1 => return vec![Primitive::Point(xs[0])], 174 | 2 => return vec![Primitive::Line(xs[0], xs[1])], 175 | _ => {} 176 | } 177 | 178 | let last_elem = *xs.last().unwrap(); 179 | 180 | xs[..xs.len() - 1] 181 | .iter() 182 | .zip(xs[1..xs.len() - 1].iter()) 183 | .map(|(&x, &y)| Primitive::Triangle(last_elem, x, y)) 184 | .collect() 185 | } 186 | 187 | #[test] 188 | fn test_to_triangles() { 189 | use self::Primitive::{Line, Point, Triangle}; 190 | 191 | assert_eq!(to_triangles(&[]), vec!()); 192 | 193 | assert_eq!( 194 | to_triangles(&[(3, None, None)]), 195 | vec!(Point((3, None, None))) 196 | ); 197 | 198 | assert_eq!( 199 | to_triangles(&[(1, None, None), (2, None, None)]), 200 | vec!(Line((1, None, None), (2, None, None))) 201 | ); 202 | 203 | assert_eq!( 204 | to_triangles(&[(1, None, None), (2, None, None), (3, None, None)]), 205 | vec!(Triangle((3, None, None), (1, None, None), (2, None, None))) 206 | ); 207 | 208 | assert_eq!( 209 | to_triangles(&[ 210 | (1, None, None), 211 | (2, None, None), 212 | (3, None, None), 213 | (4, None, None) 214 | ]), 215 | vec!( 216 | Triangle((4, None, None), (1, None, None), (2, None, None)), 217 | Triangle((4, None, None), (2, None, None), (3, None, None)) 218 | ) 219 | ); 220 | 221 | assert_eq!( 222 | to_triangles(&[ 223 | (1, None, None), 224 | (2, None, None), 225 | (3, None, None), 226 | (4, None, None), 227 | (5, None, None) 228 | ]), 229 | vec!( 230 | Triangle((5, None, None), (1, None, None), (2, None, None)), 231 | Triangle((5, None, None), (2, None, None), (3, None, None)), 232 | Triangle((5, None, None), (3, None, None), (4, None, None)) 233 | ) 234 | ); 235 | } 236 | 237 | #[derive(Clone)] 238 | struct Parser<'a> { 239 | line_number: usize, 240 | lexer: PeekableLexer<'a>, 241 | } 242 | 243 | impl<'a> Parser<'a> { 244 | fn new(input: &'a str) -> Parser<'a> { 245 | Parser { 246 | line_number: 1, 247 | lexer: PeekableLexer::new(Lexer::new(input)), 248 | } 249 | } 250 | 251 | fn error_raw(&self, msg: String) -> ParseError { 252 | ParseError { 253 | line_number: self.line_number, 254 | message: msg, 255 | } 256 | } 257 | 258 | fn error(&self, msg: E) -> Result 259 | where 260 | E: Into, 261 | { 262 | Err(self.error_raw(msg.into())) 263 | } 264 | 265 | fn next(&mut self) -> Option<&'a str> { 266 | let ret = self.lexer.next_str(); 267 | if let Some("\n") = ret { 268 | self.line_number += 1; 269 | } 270 | ret 271 | } 272 | 273 | fn advance(&mut self) { 274 | self.next(); 275 | } 276 | 277 | fn peek(&mut self) -> Option<&'a str> { 278 | self.lexer.peek_str() 279 | } 280 | 281 | /// Take a parser function and try to parse with it. If the parser fails, `None` is returned and 282 | /// no input is consumed. If the parser succeeds, the input is consumed and the parser result 283 | /// is returned. 284 | /// 285 | /// Be careful while using this function, especially in recursive parsing as it might end up with 286 | /// non-linear parsing. 287 | fn try(&mut self, parse: P) -> Option 288 | where 289 | P: FnOnce(&mut Self) -> Result, 290 | { 291 | let mut tried = self.clone(); 292 | 293 | match parse(&mut tried) { 294 | Ok(r) => { 295 | *self = tried; 296 | Some(r) 297 | } 298 | Err(_) => None, 299 | } 300 | } 301 | 302 | /// Return the number of bytes read since the last lexer state provided as argument. 303 | /// 304 | /// Return [`None`] if the checkpoint parser is more advanced in the input than the current one — 305 | /// i.e. you have likely swapped the parsers, try calling the other way around! 306 | fn bytes_consumed(&self, checkpoint: &Self) -> Option { 307 | self.lexer.bytes_consumed(&checkpoint.lexer) 308 | } 309 | 310 | /// Possibly skips over some newlines. 311 | fn zero_or_more_newlines(&mut self) { 312 | while let Some("\n") = self.peek() { 313 | self.advance() 314 | } 315 | } 316 | 317 | /// Parse just a constant string. 318 | fn parse_tag(&mut self, tag: &str) -> Result<(), ParseError> { 319 | match self.next() { 320 | None => self.error(format!("Expected `{}` but got end of input.", tag)), 321 | Some(s) if s != tag => self.error(format!("Expected `{}` but got {}.", tag, s)), 322 | _ => Ok(()), 323 | } 324 | } 325 | 326 | fn parse_tag_or_eof(&mut self, tag: &str) -> Result<(), ParseError> { 327 | match self.next() { 328 | Some(s) if s != tag => self.error(format!("Expected `{}` or EOF but got {}.", tag, s)), 329 | _ => Ok(()), 330 | } 331 | } 332 | 333 | /// Skips over some newlines, failing if it didn't manage to skip any. 334 | fn one_or_more_newlines(&mut self) -> Result<(), ParseError> { 335 | self.parse_tag_or_eof("\n")?; 336 | self.zero_or_more_newlines(); 337 | Ok(()) 338 | } 339 | 340 | fn parse_str(&mut self) -> Result<&'a str, ParseError> { 341 | match self.next() { 342 | None => self.error(format!("Expected string but got end of input.")), 343 | Some("\n") => self.error(format!("Expected string but got `end of line`.")), 344 | Some(got) => Ok(got), 345 | } 346 | } 347 | 348 | fn parse_material_library(&mut self) -> Result, ParseError> { 349 | match self.peek() { 350 | Some("mtllib") => {} 351 | _ => return Ok(None), 352 | } 353 | self.advance(); 354 | self.parse_str().map(Some) 355 | } 356 | 357 | fn parse_object_name(&mut self) -> Result<&'a str, ParseError> { 358 | match self.peek() { 359 | Some("o") => { 360 | self.parse_tag("o")?; 361 | let name = self.parse_str(); 362 | self.one_or_more_newlines()?; 363 | name 364 | } 365 | _ => Ok(""), 366 | } 367 | } 368 | 369 | // TODO(cgaebel): Should this be returning `num::rational::BigRational` instead? 370 | // I can't think of a good reason to do this except to make testing easier. 371 | fn parse_double(&mut self) -> Result { 372 | let s = self.parse_str()?; 373 | s.parse() 374 | .map_err(|_| self.error_raw(format!("Expected f64 but got {}.", s))) 375 | } 376 | 377 | fn parse_vertex(&mut self) -> Result { 378 | self.parse_tag("v")?; 379 | 380 | let x = self.parse_double()?; 381 | let y = self.parse_double()?; 382 | let z = self.parse_double()?; 383 | 384 | Ok(Vertex { x, y, z }) 385 | } 386 | 387 | fn parse_tex_vertex(&mut self) -> Result { 388 | self.parse_tag("vt")?; 389 | let u = self.parse_double()?; 390 | 391 | match self.try(Self::parse_double) { 392 | Some(v) => { 393 | let w = self.try(Self::parse_double).unwrap_or(0.); 394 | Ok(TVertex { u, v, w }) 395 | } 396 | None => Ok(TVertex { u, v: 0., w: 0. }), 397 | } 398 | } 399 | 400 | fn parse_normal(&mut self) -> Result { 401 | self.parse_tag("vn")?; 402 | 403 | let x = self.parse_double()?; 404 | let y = self.parse_double()?; 405 | let z = self.parse_double()?; 406 | 407 | Ok(Normal { x, y, z }) 408 | } 409 | 410 | fn parse_usemtl(&mut self) -> Result<&'a str, ParseError> { 411 | self.parse_tag("usemtl")?; 412 | self.parse_str() 413 | } 414 | 415 | #[inline] 416 | fn parse_isize_from(&self, s: &str) -> Result { 417 | s.parse() 418 | .map_err(|_| self.error_raw(format!("Expected isize but got {}.", s))) 419 | } 420 | 421 | fn parse_u32(&mut self) -> Result { 422 | let s = self.parse_str()?; 423 | s.parse() 424 | .map_err(|_| self.error_raw(format!("Expected u32 but got {}.", s))) 425 | } 426 | 427 | fn parse_vtindex( 428 | &mut self, 429 | valid_vtx: (usize, usize), 430 | valid_tx: (usize, usize), 431 | valid_nx: (usize, usize), 432 | ) -> Result { 433 | match self.next() { 434 | None => return self.error("Expected vertex index but got end of input.".to_owned()), 435 | Some(s) => { 436 | let process_split = 437 | |split: &str, valid_range: (usize, usize)| -> Result, ParseError> { 438 | if split.len() > 0 { 439 | Ok(Some(self.check_valid_index( 440 | valid_range, 441 | self.parse_isize_from(split)?, 442 | )?)) 443 | } else { 444 | Ok(None) 445 | } 446 | }; 447 | 448 | let mut splits_iter = s.split('/'); 449 | let split1 = splits_iter 450 | .next() 451 | .and_then(|s| process_split(&s, valid_vtx).transpose()) 452 | .transpose()?; 453 | let split2 = splits_iter 454 | .next() 455 | .and_then(|s| process_split(&s, valid_tx).transpose()) 456 | .transpose()?; 457 | let split3 = splits_iter 458 | .next() 459 | .and_then(|s| process_split(&s, valid_nx).transpose()) 460 | .transpose()?; 461 | 462 | if split1.is_none() || splits_iter.next().is_some() { 463 | self.error(format!("Expected at least 1 and at most 3 vertex indexes.")) 464 | } else { 465 | Ok((split1.unwrap(), split2, split3)) 466 | } 467 | } 468 | } 469 | } 470 | 471 | /// `valid_values` is a range of valid bounds for the actual value. 472 | #[inline(always)] 473 | fn check_valid_index( 474 | &self, 475 | valid_values: (usize, usize), 476 | actual_value: isize, 477 | ) -> Result { 478 | let (min, max) = valid_values; 479 | 480 | let mut x = actual_value; 481 | 482 | // Handle negative vertex indexes. 483 | if x < 0 { 484 | x = max as isize - x; 485 | } 486 | 487 | if x >= min as isize && x < max as isize { 488 | debug_assert!(x > 0); 489 | Ok((x - min as isize) as usize) 490 | } else { 491 | self.error(format!( 492 | "Expected index in the range [{}, {}), but got {}.", 493 | min, max, actual_value 494 | )) 495 | } 496 | } 497 | 498 | fn parse_face( 499 | &mut self, 500 | valid_vtx: (usize, usize), 501 | valid_tx: (usize, usize), 502 | valid_nx: (usize, usize), 503 | current_groups: &Vec, 504 | current_smoothing_groups: &Vec, 505 | ) -> Result, ParseError> { 506 | match self.next() { 507 | Some("f") => {} 508 | Some("l") => {} 509 | None => return self.error("Expected `f` or `l` but got end of input.".to_owned()), 510 | Some(s) => return self.error(format!("Expected `f` or `l` but got {}.", s)), 511 | } 512 | 513 | let mut corner_list = Vec::new(); 514 | 515 | corner_list.push(self.parse_vtindex(valid_vtx, valid_tx, valid_nx)?); 516 | 517 | loop { 518 | match self.peek() { 519 | None => break, 520 | Some("\n") => break, 521 | Some(_) => corner_list.push(self.parse_vtindex(valid_vtx, valid_tx, valid_nx)?), 522 | } 523 | } 524 | 525 | Ok( 526 | to_triangles(&corner_list) 527 | .into_iter() 528 | .map(|prim| Shape { 529 | primitive: prim, 530 | groups: current_groups.clone(), 531 | smoothing_groups: current_smoothing_groups.clone(), 532 | }) 533 | .collect(), 534 | ) 535 | } 536 | 537 | fn parse_geometries( 538 | &mut self, 539 | valid_vtx: (usize, usize), 540 | valid_tx: (usize, usize), 541 | valid_nx: (usize, usize), 542 | ) -> Result, ParseError> { 543 | let mut result = Vec::new(); 544 | let mut shapes = Vec::new(); 545 | 546 | let mut current_material = None; 547 | let mut current_groups = Vec::new(); 548 | let mut current_smoothing_groups = Vec::new(); 549 | 550 | loop { 551 | match self.peek() { 552 | Some("usemtl") => { 553 | let old_material = mem::replace(&mut current_material, Some(self.parse_usemtl()?)); 554 | 555 | result.push(Geometry { 556 | material_name: old_material.map(|s| s.to_owned()), 557 | shapes: mem::replace(&mut shapes, Vec::new()), 558 | }); 559 | } 560 | Some("s") => { 561 | self.advance(); 562 | current_smoothing_groups = self.parse_smoothing_groups()?; 563 | } 564 | Some("f") | Some("l") => { 565 | shapes.extend( 566 | self 567 | .parse_face( 568 | valid_vtx, 569 | valid_tx, 570 | valid_nx, 571 | ¤t_groups, 572 | ¤t_smoothing_groups, 573 | )? 574 | .into_iter(), 575 | ); 576 | } 577 | Some("g") => { 578 | self.advance(); 579 | let names = self.parse_groups()?; 580 | current_groups = names; 581 | } 582 | _ => break, 583 | } 584 | 585 | self.one_or_more_newlines()?; 586 | } 587 | 588 | result.push(Geometry { 589 | material_name: current_material.map(|s| s.to_owned()), 590 | shapes, 591 | }); 592 | 593 | Ok( 594 | result 595 | .into_iter() 596 | .filter(|ref x| !x.shapes.is_empty()) 597 | .collect(), 598 | ) 599 | } 600 | 601 | fn parse_object( 602 | &mut self, 603 | min_vertex_index: &mut usize, 604 | max_vertex_index: &mut usize, 605 | min_tex_index: &mut usize, 606 | max_tex_index: &mut usize, 607 | min_normal_index: &mut usize, 608 | max_normal_index: &mut usize, 609 | ) -> Result { 610 | let name = self.parse_object_name()?; 611 | 612 | let mut vertices = Vec::new(); 613 | let mut normals = Vec::new(); 614 | let mut tex_vertices = Vec::new(); 615 | 616 | // read vertices, normals and texture coordinates 617 | loop { 618 | if let Some(v) = self.try(Self::parse_vertex) { 619 | vertices.push(v); 620 | } else if let Some(vn) = self.try(Self::parse_normal) { 621 | normals.push(vn); 622 | } else if let Some(vt) = self.try(Self::parse_tex_vertex) { 623 | tex_vertices.push(vt); 624 | } else { 625 | break; 626 | } 627 | 628 | self.one_or_more_newlines()?; 629 | } 630 | 631 | *max_vertex_index += vertices.len(); 632 | *max_tex_index += tex_vertices.len(); 633 | *max_normal_index += normals.len(); 634 | 635 | let geometry = self.parse_geometries( 636 | (*min_vertex_index, *max_vertex_index), 637 | (*min_tex_index, *max_tex_index), 638 | (*min_normal_index, *max_normal_index), 639 | )?; 640 | 641 | *min_vertex_index += vertices.len(); 642 | *min_tex_index += tex_vertices.len(); 643 | *min_normal_index += normals.len(); 644 | 645 | Ok(Object { 646 | name: name.to_owned(), 647 | vertices, 648 | tex_vertices, 649 | normals, 650 | geometry, 651 | }) 652 | } 653 | 654 | fn parse_objects(&mut self) -> Result, ParseError> { 655 | let mut result = Vec::new(); 656 | 657 | let mut min_vertex_index = 1; 658 | let mut max_vertex_index = 1; 659 | let mut min_tex_index = 1; 660 | let mut max_tex_index = 1; 661 | let mut min_normal_index = 1; 662 | let mut max_normal_index = 1; 663 | 664 | loop { 665 | match self.peek() { 666 | Some(_) => { 667 | // create a checkpoint parser so that we can check if we consumed bytes 668 | let checkpoint = self.clone(); 669 | 670 | result.push(self.parse_object( 671 | &mut min_vertex_index, 672 | &mut max_vertex_index, 673 | &mut min_tex_index, 674 | &mut max_tex_index, 675 | &mut min_normal_index, 676 | &mut max_normal_index, 677 | )?); 678 | 679 | if self.bytes_consumed(&checkpoint).unwrap_or(0) == 0 { 680 | return self.error("cannot parse corrupted data"); 681 | } 682 | } 683 | None => break, 684 | } 685 | 686 | self.zero_or_more_newlines(); 687 | } 688 | 689 | Ok(result) 690 | } 691 | 692 | fn parse_objset(&mut self) -> Result { 693 | self.zero_or_more_newlines(); 694 | 695 | let material_library = self.parse_material_library()?; 696 | 697 | if material_library.is_some() { 698 | self.one_or_more_newlines()?; 699 | } 700 | 701 | let objects = self.parse_objects()?; 702 | 703 | self.zero_or_more_newlines(); 704 | 705 | if let Some(s) = self.peek() { 706 | return self.error(format!("Expected end of input but got {}.", s)); 707 | } 708 | 709 | Ok(ObjSet { 710 | material_library: material_library.map(|s| s.to_owned()), 711 | objects, 712 | }) 713 | } 714 | 715 | fn parse_groups(&mut self) -> Result, ParseError> { 716 | let mut groups = Vec::new(); 717 | 718 | loop { 719 | // ends the list of group names 720 | // g without any name is valid and means default group 721 | if let Some("\n") = self.peek() { 722 | break; 723 | } 724 | 725 | let name = self.parse_str()?; 726 | groups.push(name.to_owned()); 727 | } 728 | 729 | Ok(groups) 730 | } 731 | 732 | fn parse_smoothing_groups(&mut self) -> Result, ParseError> { 733 | let mut groups = Vec::new(); 734 | 735 | if self.try(|p| p.parse_tag("off")).is_none() { 736 | loop { 737 | let group = self.parse_u32()?; 738 | groups.push(group); 739 | 740 | if let Some("\n") = self.peek() { 741 | break; 742 | } 743 | } 744 | } 745 | 746 | Ok(groups) 747 | } 748 | } 749 | 750 | #[test] 751 | fn test_parse() { 752 | use self::Primitive::{Line, Triangle}; 753 | 754 | let test_case = r#" 755 | # Blender v2.69 (sub 0) OBJ File: '' 756 | # www.blender.org 757 | mtllib untitled.mtl 758 | o Cube.001 759 | v -1.000000 -1.000000 1.000000 760 | v -1.000000 -1.000000 -1.000000 761 | v 1.000000 -1.000000 -1.000000 762 | v 1.000000 -1.000000 1.000000 763 | v -1.000000 1.000000 1.000000 764 | v -1.000000 1.000000 -1.000000 765 | v 1.000000 1.000000 -1.000000 766 | v 1.000000 1.000000 1.000000 767 | usemtl None 768 | s off 769 | f 5 6 2 1 770 | f 6 7 3 2 771 | f 7 8 4 3 772 | f 8 5 1 4 773 | f 1 2 3 4 774 | f 8 7 6 5 775 | o Circle 776 | v 0.000000 0.000000 -1.000000 777 | v -0.195090 0.000000 -0.980785 778 | v -0.382683 0.000000 -0.923880 779 | v -0.555570 0.000000 -0.831470 780 | v -0.707107 0.000000 -0.707107 781 | v -0.831470 0.000000 -0.555570 782 | v -0.923880 0.000000 -0.382683 783 | v -0.980785 0.000000 -0.195090 784 | v -1.000000 0.000000 -0.000000 785 | v -0.980785 0.000000 0.195090 786 | v -0.923880 0.000000 0.382683 787 | v -0.831470 0.000000 0.555570 788 | v -0.707107 0.000000 0.707107 789 | v -0.555570 0.000000 0.831470 790 | v -0.382683 0.000000 0.923880 791 | v -0.195090 0.000000 0.980785 792 | v 0.000000 0.000000 1.000000 793 | v 0.195091 0.000000 0.980785 794 | v 0.382684 0.000000 0.923879 795 | v 0.555571 0.000000 0.831469 796 | v 0.707107 0.000000 0.707106 797 | v 0.831470 0.000000 0.555570 798 | v 0.923880 0.000000 0.382683 799 | v 0.980785 0.000000 0.195089 800 | v 1.000000 0.000000 -0.000001 801 | v 0.980785 0.000000 -0.195091 802 | v 0.923879 0.000000 -0.382684 803 | v 0.831469 0.000000 -0.555571 804 | v 0.707106 0.000000 -0.707108 805 | v 0.555569 0.000000 -0.831470 806 | v 0.382682 0.000000 -0.923880 807 | v 0.195089 0.000000 -0.980786 808 | l 10 9 809 | l 11 10 810 | l 12 11 811 | l 13 12 812 | l 14 13 813 | l 15 14 814 | l 16 15 815 | l 17 16 816 | l 18 17 817 | l 19 18 818 | l 20 19 819 | l 21 20 820 | l 22 21 821 | l 23 22 822 | l 24 23 823 | l 25 24 824 | l 26 25 825 | l 27 26 826 | l 28 27 827 | l 29 28 828 | l 30 29 829 | l 31 30 830 | l 32 31 831 | l 33 32 832 | l 34 33 833 | l 35 34 834 | l 36 35 835 | l 37 36 836 | l 38 37 837 | l 39 38 838 | l 40 39 839 | l 9 40 840 | o Cube 841 | v 1.000000 -1.000000 -1.000000 842 | v 1.000000 -1.000000 1.000000 843 | v -1.000000 -1.000000 1.000000 844 | v -1.000000 -1.000000 -1.000000 845 | v 1.000000 1.000000 -0.999999 846 | v 0.999999 1.000000 1.000001 847 | v -1.000000 1.000000 1.000000 848 | v -1.000000 1.000000 -1.000000 849 | usemtl Material 850 | s off 851 | f 41 42 43 44 852 | f 45 48 47 46 853 | f 41 45 46 42 854 | f 42 46 47 43 855 | f 43 47 48 44 856 | f 45 41 44 48"#; 857 | 858 | let expected = Ok(ObjSet { 859 | material_library: Some("untitled.mtl".to_owned()), 860 | objects: vec![ 861 | Object { 862 | name: "Cube.001".to_owned(), 863 | vertices: vec![ 864 | (-1, -1, 1), 865 | (-1, -1, -1), 866 | (1, -1, -1), 867 | (1, -1, 1), 868 | (-1, 1, 1), 869 | (-1, 1, -1), 870 | (1, 1, -1), 871 | (1, 1, 1), 872 | ] 873 | .into_iter() 874 | .map(|(x, y, z)| Vertex { 875 | x: x as f64, 876 | y: y as f64, 877 | z: z as f64, 878 | }) 879 | .collect(), 880 | tex_vertices: vec![], 881 | normals: vec![], 882 | geometry: vec![Geometry { 883 | material_name: Some("None".to_owned()), 884 | shapes: vec![ 885 | (0, 4, 5), 886 | (0, 5, 1), 887 | (1, 5, 6), 888 | (1, 6, 2), 889 | (2, 6, 7), 890 | (2, 7, 3), 891 | (3, 7, 4), 892 | (3, 4, 0), 893 | (3, 0, 1), 894 | (3, 1, 2), 895 | (4, 7, 6), 896 | (4, 6, 5), 897 | ] 898 | .into_iter() 899 | .map(|(x, y, z)| Shape { 900 | primitive: Triangle((x, None, None), (y, None, None), (z, None, None)), 901 | groups: vec![], 902 | smoothing_groups: vec![], 903 | }) 904 | .collect(), 905 | }], 906 | }, 907 | Object { 908 | name: "Circle".to_owned(), 909 | vertices: vec![ 910 | (0.0, 0.0, -1.0), 911 | (-0.19509, 0.0, -0.980785), 912 | (-0.382683, 0.0, -0.92388), 913 | (-0.55557, 0.0, -0.83147), 914 | (-0.707107, 0.0, -0.707107), 915 | (-0.83147, 0.0, -0.55557), 916 | (-0.92388, 0.0, -0.382683), 917 | (-0.980785, 0.0, -0.19509), 918 | (-1.0, 0.0, 0.0), 919 | (-0.980785, 0.0, 0.19509), 920 | (-0.92388, 0.0, 0.382683), 921 | (-0.83147, 0.0, 0.55557), 922 | (-0.707107, 0.0, 0.707107), 923 | (-0.55557, 0.0, 0.83147), 924 | (-0.382683, 0.0, 0.92388), 925 | (-0.19509, 0.0, 0.980785), 926 | (0.0, 0.0, 1.0), 927 | (0.195091, 0.0, 0.980785), 928 | (0.382684, 0.0, 0.923879), 929 | (0.555571, 0.0, 0.831469), 930 | (0.707107, 0.0, 0.707106), 931 | (0.83147, 0.0, 0.55557), 932 | (0.92388, 0.0, 0.382683), 933 | (0.980785, 0.0, 0.195089), 934 | (1.0, 0.0, -0.000001), 935 | (0.980785, 0.0, -0.195091), 936 | (0.923879, 0.0, -0.382684), 937 | (0.831469, 0.0, -0.555571), 938 | (0.707106, 0.0, -0.707108), 939 | (0.555569, 0.0, -0.83147), 940 | (0.382682, 0.0, -0.92388), 941 | (0.195089, 0.0, -0.980786), 942 | ] 943 | .into_iter() 944 | .map(|(x, y, z)| Vertex { x, y, z }) 945 | .collect(), 946 | tex_vertices: vec![], 947 | normals: vec![], 948 | geometry: vec![Geometry { 949 | material_name: None, 950 | shapes: vec![ 951 | (1, 0), 952 | (2, 1), 953 | (3, 2), 954 | (4, 3), 955 | (5, 4), 956 | (6, 5), 957 | (7, 6), 958 | (8, 7), 959 | (9, 8), 960 | (10, 9), 961 | (11, 10), 962 | (12, 11), 963 | (13, 12), 964 | (14, 13), 965 | (15, 14), 966 | (16, 15), 967 | (17, 16), 968 | (18, 17), 969 | (19, 18), 970 | (20, 19), 971 | (21, 20), 972 | (22, 21), 973 | (23, 22), 974 | (24, 23), 975 | (25, 24), 976 | (26, 25), 977 | (27, 26), 978 | (28, 27), 979 | (29, 28), 980 | (30, 29), 981 | (31, 30), 982 | (0, 31), 983 | ] 984 | .into_iter() 985 | .map(|(x, y)| Shape { 986 | primitive: Line((x, None, None), (y, None, None)), 987 | groups: vec![], 988 | smoothing_groups: vec![], 989 | }) 990 | .collect(), 991 | }], 992 | }, 993 | Object { 994 | name: "Cube".to_owned(), 995 | vertices: vec![ 996 | (1.0, -1.0, -1.0), 997 | (1.0, -1.0, 1.0), 998 | (-1.0, -1.0, 1.0), 999 | (-1.0, -1.0, -1.0), 1000 | (1.0, 1.0, -0.999999), 1001 | (0.999999, 1.0, 1.000001), 1002 | (-1.0, 1.0, 1.0), 1003 | (-1.0, 1.0, -1.0), 1004 | ] 1005 | .into_iter() 1006 | .map(|(x, y, z)| Vertex { x, y, z }) 1007 | .collect(), 1008 | tex_vertices: vec![], 1009 | normals: vec![], 1010 | geometry: vec![Geometry { 1011 | material_name: Some("Material".to_owned()), 1012 | shapes: vec![ 1013 | (3, 0, 1), 1014 | (3, 1, 2), 1015 | (5, 4, 7), 1016 | (5, 7, 6), 1017 | (1, 0, 4), 1018 | (1, 4, 5), 1019 | (2, 1, 5), 1020 | (2, 5, 6), 1021 | (3, 2, 6), 1022 | (3, 6, 7), 1023 | (7, 4, 0), 1024 | (7, 0, 3), 1025 | ] 1026 | .into_iter() 1027 | .map(|(x, y, z)| Shape { 1028 | primitive: Triangle((x, None, None), (y, None, None), (z, None, None)), 1029 | groups: vec![], 1030 | smoothing_groups: vec![], 1031 | }) 1032 | .collect(), 1033 | }], 1034 | }, 1035 | ], 1036 | }); 1037 | 1038 | assert_eq!(parse(test_case), expected); 1039 | } 1040 | 1041 | #[test] 1042 | fn test_cube() { 1043 | use self::Primitive::Triangle; 1044 | 1045 | let test_case = r#" 1046 | # Blender v2.71 (sub 0) OBJ File: 'cube.blend' 1047 | # www.blender.org 1048 | mtllib cube.mtl 1049 | o Cube 1050 | v 1.000000 -1.000000 -1.000000 1051 | v 1.000000 -1.000000 1.000000 1052 | v -1.000000 -1.000000 1.000000 1053 | v -1.000000 -1.000000 -1.000000 1054 | v 1.000000 1.000000 -0.999999 1055 | v 0.999999 1.000000 1.000001 1056 | v -1.000000 1.000000 1.000000 1057 | v -1.000000 1.000000 -1.000000 1058 | vt 1.004952 0.498633 1059 | vt 0.754996 0.498236 1060 | vt 0.755393 0.248279 1061 | vt 1.005349 0.248677 1062 | vt 0.255083 0.497442 1063 | vt 0.255480 0.247485 1064 | vt 0.505437 0.247882 1065 | vt 0.505039 0.497839 1066 | vt 0.754598 0.748193 1067 | vt 0.504642 0.747795 1068 | vt 0.505834 -0.002074 1069 | vt 0.755790 -0.001677 1070 | vt 0.005127 0.497044 1071 | vt 0.005524 0.247088 1072 | usemtl Material 1073 | s off 1074 | f 1/1 2/2 3/3 4/4 1075 | f 5/5 8/6 7/7 6/8 1076 | f 1/9 5/10 6/8 2/2 1077 | f 2/2 6/8 7/7 3/3 1078 | f 3/3 7/7 8/11 4/12 1079 | f 5/5 1/13 4/14 8/6"#; 1080 | 1081 | let expected = Ok(ObjSet { 1082 | material_library: Some("cube.mtl".to_owned()), 1083 | objects: vec![Object { 1084 | name: "Cube".to_owned(), 1085 | vertices: vec![ 1086 | (1.0, -1.0, -1.0), 1087 | (1.0, -1.0, 1.0), 1088 | (-1.0, -1.0, 1.0), 1089 | (-1.0, -1.0, -1.0), 1090 | (1.0, 1.0, -1.0), 1091 | (1.0, 1.0, 1.0), 1092 | (-1.0, 1.0, 1.0), 1093 | (-1.0, 1.0, -1.0), 1094 | ] 1095 | .into_iter() 1096 | .map(|(x, y, z)| Vertex { 1097 | x: x as f64, 1098 | y: y as f64, 1099 | z: z as f64, 1100 | }) 1101 | .collect(), 1102 | tex_vertices: vec![ 1103 | (1.004952, 0.498633), 1104 | (0.754996, 0.498236), 1105 | (0.755393, 0.248279), 1106 | (1.005349, 0.248677), 1107 | (0.255083, 0.497442), 1108 | (0.25548, 0.247485), 1109 | (0.505437, 0.247882), 1110 | (0.505039, 0.497839), 1111 | (0.754598, 0.748193), 1112 | (0.504642, 0.747795), 1113 | (0.505834, -0.002074), 1114 | (0.75579, -0.001677), 1115 | (0.005127, 0.497044), 1116 | (0.005524, 0.247088), 1117 | ] 1118 | .into_iter() 1119 | .map(|(u, v)| TVertex { u, v, w: 0. }) 1120 | .collect(), 1121 | normals: vec![], 1122 | geometry: vec![Geometry { 1123 | material_name: Some("Material".to_owned()), 1124 | shapes: vec![ 1125 | (3, 3, 0, 0, 1, 1), 1126 | (3, 3, 1, 1, 2, 2), 1127 | (5, 7, 4, 4, 7, 5), 1128 | (5, 7, 7, 5, 6, 6), 1129 | (1, 1, 0, 8, 4, 9), 1130 | (1, 1, 4, 9, 5, 7), 1131 | (2, 2, 1, 1, 5, 7), 1132 | (2, 2, 5, 7, 6, 6), 1133 | (3, 11, 2, 2, 6, 6), 1134 | (3, 11, 6, 6, 7, 10), 1135 | (7, 5, 4, 4, 0, 12), 1136 | (7, 5, 0, 12, 3, 13), 1137 | ] 1138 | .into_iter() 1139 | .map(|(vx, tx, vy, ty, vz, tz)| Shape { 1140 | primitive: Triangle( 1141 | (vx, Some(tx), None), 1142 | (vy, Some(ty), None), 1143 | (vz, Some(tz), None), 1144 | ), 1145 | groups: vec![], 1146 | smoothing_groups: vec![], 1147 | }) 1148 | .collect(), 1149 | }], 1150 | }], 1151 | }); 1152 | 1153 | assert_eq!(parse(test_case), expected); 1154 | } 1155 | 1156 | #[test] 1157 | fn test_cube_anonymous_object() { 1158 | use self::Primitive::Triangle; 1159 | 1160 | let test_case = r#" 1161 | # Blender v2.71 (sub 0) OBJ File: 'cube.blend' 1162 | # www.blender.org 1163 | mtllib cube.mtl 1164 | v 1.000000 -1.000000 -1.000000 1165 | v 1.000000 -1.000000 1.000000 1166 | v -1.000000 -1.000000 1.000000 1167 | v -1.000000 -1.000000 -1.000000 1168 | v 1.000000 1.000000 -0.999999 1169 | v 0.999999 1.000000 1.000001 1170 | v -1.000000 1.000000 1.000000 1171 | v -1.000000 1.000000 -1.000000 1172 | vt 1.004952 0.498633 1173 | vt 0.754996 0.498236 1174 | vt 0.755393 0.248279 1175 | vt 1.005349 0.248677 1176 | vt 0.255083 0.497442 1177 | vt 0.255480 0.247485 1178 | vt 0.505437 0.247882 1179 | vt 0.505039 0.497839 1180 | vt 0.754598 0.748193 1181 | vt 0.504642 0.747795 1182 | vt 0.505834 -0.002074 1183 | vt 0.755790 -0.001677 1184 | vt 0.005127 0.497044 1185 | vt 0.005524 0.247088 1186 | usemtl Material 1187 | s off 1188 | f 1/1 2/2 3/3 4/4 1189 | f 5/5 8/6 7/7 6/8 1190 | f 1/9 5/10 6/8 2/2 1191 | f 2/2 6/8 7/7 3/3 1192 | f 3/3 7/7 8/11 4/12 1193 | f 5/5 1/13 4/14 8/6"#; 1194 | 1195 | let expected = Ok(ObjSet { 1196 | material_library: Some("cube.mtl".to_owned()), 1197 | objects: vec![Object { 1198 | name: String::new(), 1199 | vertices: vec![ 1200 | (1.0, -1.0, -1.0), 1201 | (1.0, -1.0, 1.0), 1202 | (-1.0, -1.0, 1.0), 1203 | (-1.0, -1.0, -1.0), 1204 | (1.0, 1.0, -1.0), 1205 | (1.0, 1.0, 1.0), 1206 | (-1.0, 1.0, 1.0), 1207 | (-1.0, 1.0, -1.0), 1208 | ] 1209 | .into_iter() 1210 | .map(|(x, y, z)| Vertex { 1211 | x: x as f64, 1212 | y: y as f64, 1213 | z: z as f64, 1214 | }) 1215 | .collect(), 1216 | tex_vertices: vec![ 1217 | (1.004952, 0.498633), 1218 | (0.754996, 0.498236), 1219 | (0.755393, 0.248279), 1220 | (1.005349, 0.248677), 1221 | (0.255083, 0.497442), 1222 | (0.25548, 0.247485), 1223 | (0.505437, 0.247882), 1224 | (0.505039, 0.497839), 1225 | (0.754598, 0.748193), 1226 | (0.504642, 0.747795), 1227 | (0.505834, -0.002074), 1228 | (0.75579, -0.001677), 1229 | (0.005127, 0.497044), 1230 | (0.005524, 0.247088), 1231 | ] 1232 | .into_iter() 1233 | .map(|(u, v)| TVertex { u, v, w: 0. }) 1234 | .collect(), 1235 | normals: vec![], 1236 | geometry: vec![Geometry { 1237 | material_name: Some("Material".to_owned()), 1238 | shapes: vec![ 1239 | (3, 3, 0, 0, 1, 1), 1240 | (3, 3, 1, 1, 2, 2), 1241 | (5, 7, 4, 4, 7, 5), 1242 | (5, 7, 7, 5, 6, 6), 1243 | (1, 1, 0, 8, 4, 9), 1244 | (1, 1, 4, 9, 5, 7), 1245 | (2, 2, 1, 1, 5, 7), 1246 | (2, 2, 5, 7, 6, 6), 1247 | (3, 11, 2, 2, 6, 6), 1248 | (3, 11, 6, 6, 7, 10), 1249 | (7, 5, 4, 4, 0, 12), 1250 | (7, 5, 0, 12, 3, 13), 1251 | ] 1252 | .into_iter() 1253 | .map(|(vx, tx, vy, ty, vz, tz)| Shape { 1254 | primitive: Triangle( 1255 | (vx, Some(tx), None), 1256 | (vy, Some(ty), None), 1257 | (vz, Some(tz), None), 1258 | ), 1259 | groups: vec![], 1260 | smoothing_groups: vec![], 1261 | }) 1262 | .collect(), 1263 | }], 1264 | }], 1265 | }); 1266 | 1267 | assert_eq!(parse(test_case), expected); 1268 | } 1269 | 1270 | #[test] 1271 | fn test_cube_tex_vert_missing_vw() { 1272 | use self::Primitive::Triangle; 1273 | 1274 | let test_case = r#" 1275 | # Blender v2.71 (sub 0) OBJ File: 'cube.blend' 1276 | # www.blender.org 1277 | mtllib cube.mtl 1278 | o Cube 1279 | v 1.000000 -1.000000 -1.000000 1280 | v 1.000000 -1.000000 1.000000 1281 | v -1.000000 -1.000000 1.000000 1282 | v -1.000000 -1.000000 -1.000000 1283 | v 1.000000 1.000000 -0.999999 1284 | v 0.999999 1.000000 1.000001 1285 | v -1.000000 1.000000 1.000000 1286 | v -1.000000 1.000000 -1.000000 1287 | vt 1.004952 1288 | vt 0.754996 1289 | vt 0.755393 1290 | vt 1.005349 1291 | vt 0.255083 1292 | vt 0.255480 1293 | vt 0.505437 1294 | vt 0.505039 1295 | vt 0.754598 1296 | vt 0.504642 1297 | vt 0.505834 1298 | vt 0.755790 1299 | vt 0.005127 1300 | vt 0.005524 1301 | usemtl Material 1302 | s off 1303 | f 1/1 2/2 3/3 4/4 1304 | f 5/5 8/6 7/7 6/8 1305 | f 1/9 5/10 6/8 2/2 1306 | f 2/2 6/8 7/7 3/3 1307 | f 3/3 7/7 8/11 4/12 1308 | f 5/5 1/13 4/14 8/6"#; 1309 | 1310 | let expected = Ok(ObjSet { 1311 | material_library: Some("cube.mtl".to_owned()), 1312 | objects: vec![Object { 1313 | name: "Cube".to_owned(), 1314 | vertices: vec![ 1315 | (1.0, -1.0, -1.0), 1316 | (1.0, -1.0, 1.0), 1317 | (-1.0, -1.0, 1.0), 1318 | (-1.0, -1.0, -1.0), 1319 | (1.0, 1.0, -1.0), 1320 | (1.0, 1.0, 1.0), 1321 | (-1.0, 1.0, 1.0), 1322 | (-1.0, 1.0, -1.0), 1323 | ] 1324 | .into_iter() 1325 | .map(|(x, y, z)| Vertex { 1326 | x: x as f64, 1327 | y: y as f64, 1328 | z: z as f64, 1329 | }) 1330 | .collect(), 1331 | tex_vertices: vec![ 1332 | 1.004952, 0.754996, 0.755393, 1.005349, 0.255083, 0.25548, 0.505437, 0.505039, 0.754598, 1333 | 0.504642, 0.505834, 0.75579, 0.005127, 0.005524, 1334 | ] 1335 | .into_iter() 1336 | .map(|u| TVertex { u, v: 0., w: 0. }) 1337 | .collect(), 1338 | normals: vec![], 1339 | geometry: vec![Geometry { 1340 | material_name: Some("Material".to_owned()), 1341 | shapes: vec![ 1342 | (3, 3, 0, 0, 1, 1), 1343 | (3, 3, 1, 1, 2, 2), 1344 | (5, 7, 4, 4, 7, 5), 1345 | (5, 7, 7, 5, 6, 6), 1346 | (1, 1, 0, 8, 4, 9), 1347 | (1, 1, 4, 9, 5, 7), 1348 | (2, 2, 1, 1, 5, 7), 1349 | (2, 2, 5, 7, 6, 6), 1350 | (3, 11, 2, 2, 6, 6), 1351 | (3, 11, 6, 6, 7, 10), 1352 | (7, 5, 4, 4, 0, 12), 1353 | (7, 5, 0, 12, 3, 13), 1354 | ] 1355 | .into_iter() 1356 | .map(|(vx, tx, vy, ty, vz, tz)| Shape { 1357 | primitive: Triangle( 1358 | (vx, Some(tx), None), 1359 | (vy, Some(ty), None), 1360 | (vz, Some(tz), None), 1361 | ), 1362 | groups: vec![], 1363 | smoothing_groups: vec![], 1364 | }) 1365 | .collect(), 1366 | }], 1367 | }], 1368 | }); 1369 | 1370 | assert_eq!(parse(test_case), expected); 1371 | } 1372 | 1373 | #[test] 1374 | fn test_cube_3d_tex_vert() { 1375 | use self::Primitive::Triangle; 1376 | 1377 | let test_case = r#" 1378 | # Blender v2.71 (sub 0) OBJ File: 'cube.blend' 1379 | # www.blender.org 1380 | mtllib cube.mtl 1381 | o Cube 1382 | v 1.000000 -1.000000 -1.000000 1383 | v 1.000000 -1.000000 1.000000 1384 | v -1.000000 -1.000000 1.000000 1385 | v -1.000000 -1.000000 -1.000000 1386 | v 1.000000 1.000000 -0.999999 1387 | v 0.999999 1.000000 1.000001 1388 | v -1.000000 1.000000 1.000000 1389 | v -1.000000 1.000000 -1.000000 1390 | vt 1.004952 0.498633 1.0 1391 | vt 0.754996 0.498236 1.0 1392 | vt 0.755393 0.248279 1.0 1393 | vt 1.005349 0.248677 1.0 1394 | vt 0.255083 0.497442 1.0 1395 | vt 0.255480 0.247485 1.0 1396 | vt 0.505437 0.247882 1.0 1397 | vt 0.505039 0.497839 1.0 1398 | vt 0.754598 0.748193 1.0 1399 | vt 0.504642 0.747795 1.0 1400 | vt 0.505834 -0.002074 1.0 1401 | vt 0.755790 -0.001677 1.0 1402 | vt 0.005127 0.497044 1.0 1403 | vt 0.005524 0.247088 1.0 1404 | usemtl Material 1405 | s off 1406 | f 1/1 2/2 3/3 4/4 1407 | f 5/5 8/6 7/7 6/8 1408 | f 1/9 5/10 6/8 2/2 1409 | f 2/2 6/8 7/7 3/3 1410 | f 3/3 7/7 8/11 4/12 1411 | f 5/5 1/13 4/14 8/6 1412 | "#; 1413 | 1414 | let expected = Ok(ObjSet { 1415 | material_library: Some("cube.mtl".to_owned()), 1416 | objects: vec![Object { 1417 | name: "Cube".to_owned(), 1418 | vertices: vec![ 1419 | (1.0, -1.0, -1.0), 1420 | (1.0, -1.0, 1.0), 1421 | (-1.0, -1.0, 1.0), 1422 | (-1.0, -1.0, -1.0), 1423 | (1.0, 1.0, -1.0), 1424 | (1.0, 1.0, 1.0), 1425 | (-1.0, 1.0, 1.0), 1426 | (-1.0, 1.0, -1.0), 1427 | ] 1428 | .into_iter() 1429 | .map(|(x, y, z)| Vertex { 1430 | x: x as f64, 1431 | y: y as f64, 1432 | z: z as f64, 1433 | }) 1434 | .collect(), 1435 | tex_vertices: vec![ 1436 | (1.004952, 0.498633), 1437 | (0.754996, 0.498236), 1438 | (0.755393, 0.248279), 1439 | (1.005349, 0.248677), 1440 | (0.255083, 0.497442), 1441 | (0.25548, 0.247485), 1442 | (0.505437, 0.247882), 1443 | (0.505039, 0.497839), 1444 | (0.754598, 0.748193), 1445 | (0.504642, 0.747795), 1446 | (0.505834, -0.002074), 1447 | (0.75579, -0.001677), 1448 | (0.005127, 0.497044), 1449 | (0.005524, 0.247088), 1450 | ] 1451 | .into_iter() 1452 | .map(|(u, v)| TVertex { u, v, w: 1. }) 1453 | .collect(), 1454 | normals: vec![], 1455 | geometry: vec![Geometry { 1456 | material_name: Some("Material".to_owned()), 1457 | shapes: vec![ 1458 | (3, 3, 0, 0, 1, 1), 1459 | (3, 3, 1, 1, 2, 2), 1460 | (5, 7, 4, 4, 7, 5), 1461 | (5, 7, 7, 5, 6, 6), 1462 | (1, 1, 0, 8, 4, 9), 1463 | (1, 1, 4, 9, 5, 7), 1464 | (2, 2, 1, 1, 5, 7), 1465 | (2, 2, 5, 7, 6, 6), 1466 | (3, 11, 2, 2, 6, 6), 1467 | (3, 11, 6, 6, 7, 10), 1468 | (7, 5, 4, 4, 0, 12), 1469 | (7, 5, 0, 12, 3, 13), 1470 | ] 1471 | .into_iter() 1472 | .map(|(vx, tx, vy, ty, vz, tz)| Shape { 1473 | primitive: Triangle( 1474 | (vx, Some(tx), None), 1475 | (vy, Some(ty), None), 1476 | (vz, Some(tz), None), 1477 | ), 1478 | groups: vec![], 1479 | smoothing_groups: vec![], 1480 | }) 1481 | .collect(), 1482 | }], 1483 | }], 1484 | }); 1485 | 1486 | assert_eq!(parse(test_case), expected); 1487 | } 1488 | 1489 | #[test] 1490 | fn test_normals_no_tex() { 1491 | use self::Primitive::Triangle; 1492 | 1493 | let test_case = r#" 1494 | # Blender v2.70 (sub 4) OBJ File: '' 1495 | # www.blender.org 1496 | mtllib normal-cone.mtl 1497 | o Cone 1498 | v 0.000000 -1.000000 -1.000000 1499 | v 0.000000 1.000000 0.000000 1500 | v 0.195090 -1.000000 -0.980785 1501 | v 0.382683 -1.000000 -0.923880 1502 | v 0.555570 -1.000000 -0.831470 1503 | v 0.707107 -1.000000 -0.707107 1504 | v 0.831470 -1.000000 -0.555570 1505 | v 0.923880 -1.000000 -0.382683 1506 | v 0.980785 -1.000000 -0.195090 1507 | v 1.000000 -1.000000 -0.000000 1508 | v 0.980785 -1.000000 0.195090 1509 | v 0.923880 -1.000000 0.382683 1510 | v 0.831470 -1.000000 0.555570 1511 | v 0.707107 -1.000000 0.707107 1512 | v 0.555570 -1.000000 0.831470 1513 | v 0.382683 -1.000000 0.923880 1514 | v 0.195090 -1.000000 0.980785 1515 | v -0.000000 -1.000000 1.000000 1516 | v -0.195091 -1.000000 0.980785 1517 | v -0.382684 -1.000000 0.923879 1518 | v -0.555571 -1.000000 0.831469 1519 | v -0.707107 -1.000000 0.707106 1520 | v -0.831470 -1.000000 0.555570 1521 | v -0.923880 -1.000000 0.382683 1522 | v -0.980785 -1.000000 0.195089 1523 | v -1.000000 -1.000000 -0.000001 1524 | v -0.980785 -1.000000 -0.195091 1525 | v -0.923879 -1.000000 -0.382684 1526 | v -0.831469 -1.000000 -0.555571 1527 | v -0.707106 -1.000000 -0.707108 1528 | v -0.555569 -1.000000 -0.831470 1529 | v -0.382682 -1.000000 -0.923880 1530 | v -0.195089 -1.000000 -0.980786 1531 | vn -0.259887 0.445488 -0.856737 1532 | vn 0.087754 0.445488 -0.890977 1533 | vn -0.422035 0.445488 -0.789574 1534 | vn -0.567964 0.445488 -0.692068 1535 | vn -0.692066 0.445488 -0.567966 1536 | vn -0.789573 0.445488 -0.422037 1537 | vn -0.856737 0.445488 -0.259889 1538 | vn -0.890977 0.445488 -0.087754 1539 | vn -0.890977 0.445488 0.087753 1540 | vn -0.856737 0.445488 0.259887 1541 | vn -0.789574 0.445488 0.422035 1542 | vn -0.692067 0.445488 0.567964 1543 | vn -0.567965 0.445488 0.692066 1544 | vn -0.422036 0.445488 0.789573 1545 | vn -0.259889 0.445488 0.856737 1546 | vn -0.087754 0.445488 0.890977 1547 | vn 0.087753 0.445488 0.890977 1548 | vn 0.259888 0.445488 0.856737 1549 | vn 0.422036 0.445488 0.789573 1550 | vn 0.567965 0.445488 0.692067 1551 | vn 0.692067 0.445488 0.567965 1552 | vn 0.789573 0.445488 0.422035 1553 | vn 0.856737 0.445488 0.259888 1554 | vn 0.890977 0.445488 0.087753 1555 | vn 0.890977 0.445488 -0.087754 1556 | vn 0.856737 0.445488 -0.259888 1557 | vn 0.789573 0.445488 -0.422036 1558 | vn 0.692067 0.445488 -0.567965 1559 | vn 0.567965 0.445488 -0.692067 1560 | vn 0.422036 0.445488 -0.789573 1561 | vn -0.087753 0.445488 -0.890977 1562 | vn 0.259888 0.445488 -0.856737 1563 | vn 0.000000 -1.000000 -0.000000 1564 | usemtl Material.002 1565 | s off 1566 | f 32//1 2//1 33//1 1567 | f 1//2 2//2 3//2 1568 | f 31//3 2//3 32//3 1569 | f 30//4 2//4 31//4 1570 | f 29//5 2//5 30//5 1571 | f 28//6 2//6 29//6 1572 | f 27//7 2//7 28//7 1573 | f 26//8 2//8 27//8 1574 | f 25//9 2//9 26//9 1575 | f 24//10 2//10 25//10 1576 | f 23//11 2//11 24//11 1577 | f 22//12 2//12 23//12 1578 | f 21//13 2//13 22//13 1579 | f 20//14 2//14 21//14 1580 | f 19//15 2//15 20//15 1581 | f 18//16 2//16 19//16 1582 | f 17//17 2//17 18//17 1583 | f 16//18 2//18 17//18 1584 | f 15//19 2//19 16//19 1585 | f 14//20 2//20 15//20 1586 | f 13//21 2//21 14//21 1587 | f 12//22 2//22 13//22 1588 | f 11//23 2//23 12//23 1589 | f 10//24 2//24 11//24 1590 | f 9//25 2//25 10//25 1591 | f 8//26 2//26 9//26 1592 | f 7//27 2//27 8//27 1593 | f 6//28 2//28 7//28 1594 | f 5//29 2//29 6//29 1595 | f 4//30 2//30 5//30 1596 | f 33//31 2//31 1//31 1597 | f 3//32 2//32 4//32 1598 | "#; 1599 | 1600 | let expected = Ok(ObjSet { 1601 | material_library: Some("normal-cone.mtl".to_owned()), 1602 | objects: vec![Object { 1603 | name: "Cone".to_owned(), 1604 | vertices: vec![ 1605 | (0.000000, -1.000000, -1.000000), 1606 | (0.000000, 1.000000, 0.000000), 1607 | (0.195090, -1.000000, -0.980785), 1608 | (0.382683, -1.000000, -0.923880), 1609 | (0.555570, -1.000000, -0.831470), 1610 | (0.707107, -1.000000, -0.707107), 1611 | (0.831470, -1.000000, -0.555570), 1612 | (0.923880, -1.000000, -0.382683), 1613 | (0.980785, -1.000000, -0.195090), 1614 | (1.000000, -1.000000, -0.000000), 1615 | (0.980785, -1.000000, 0.195090), 1616 | (0.923880, -1.000000, 0.382683), 1617 | (0.831470, -1.000000, 0.555570), 1618 | (0.707107, -1.000000, 0.707107), 1619 | (0.555570, -1.000000, 0.831470), 1620 | (0.382683, -1.000000, 0.923880), 1621 | (0.195090, -1.000000, 0.980785), 1622 | (-0.000000, -1.000000, 1.000000), 1623 | (-0.195091, -1.000000, 0.980785), 1624 | (-0.382684, -1.000000, 0.923879), 1625 | (-0.555571, -1.000000, 0.831469), 1626 | (-0.707107, -1.000000, 0.707106), 1627 | (-0.831470, -1.000000, 0.555570), 1628 | (-0.923880, -1.000000, 0.382683), 1629 | (-0.980785, -1.000000, 0.195089), 1630 | (-1.000000, -1.000000, -0.000001), 1631 | (-0.980785, -1.000000, -0.195091), 1632 | (-0.923879, -1.000000, -0.382684), 1633 | (-0.831469, -1.000000, -0.555571), 1634 | (-0.707106, -1.000000, -0.707108), 1635 | (-0.555569, -1.000000, -0.831470), 1636 | (-0.382682, -1.000000, -0.923880), 1637 | (-0.195089, -1.000000, -0.980786), 1638 | ] 1639 | .into_iter() 1640 | .map(|(x, y, z)| Vertex { x, y, z }) 1641 | .collect(), 1642 | tex_vertices: vec![], 1643 | normals: vec![ 1644 | (-0.259887, 0.445488, -0.856737), 1645 | (0.087754, 0.445488, -0.890977), 1646 | (-0.422035, 0.445488, -0.789574), 1647 | (-0.567964, 0.445488, -0.692068), 1648 | (-0.692066, 0.445488, -0.567966), 1649 | (-0.789573, 0.445488, -0.422037), 1650 | (-0.856737, 0.445488, -0.259889), 1651 | (-0.890977, 0.445488, -0.087754), 1652 | (-0.890977, 0.445488, 0.087753), 1653 | (-0.856737, 0.445488, 0.259887), 1654 | (-0.789574, 0.445488, 0.422035), 1655 | (-0.692067, 0.445488, 0.567964), 1656 | (-0.567965, 0.445488, 0.692066), 1657 | (-0.422036, 0.445488, 0.789573), 1658 | (-0.259889, 0.445488, 0.856737), 1659 | (-0.087754, 0.445488, 0.890977), 1660 | (0.087753, 0.445488, 0.890977), 1661 | (0.259888, 0.445488, 0.856737), 1662 | (0.422036, 0.445488, 0.789573), 1663 | (0.567965, 0.445488, 0.692067), 1664 | (0.692067, 0.445488, 0.567965), 1665 | (0.789573, 0.445488, 0.422035), 1666 | (0.856737, 0.445488, 0.259888), 1667 | (0.890977, 0.445488, 0.087753), 1668 | (0.890977, 0.445488, -0.087754), 1669 | (0.856737, 0.445488, -0.259888), 1670 | (0.789573, 0.445488, -0.422036), 1671 | (0.692067, 0.445488, -0.567965), 1672 | (0.567965, 0.445488, -0.692067), 1673 | (0.422036, 0.445488, -0.789573), 1674 | (-0.087753, 0.445488, -0.890977), 1675 | (0.259888, 0.445488, -0.856737), 1676 | (0.000000, -1.000000, -0.000000), 1677 | ] 1678 | .into_iter() 1679 | .map(|(x, y, z)| Normal { x, y, z }) 1680 | .collect(), 1681 | geometry: vec![Geometry { 1682 | material_name: Some("Material.002".to_owned()), 1683 | shapes: vec![ 1684 | (32, 0, 31, 0, 1, 0), 1685 | (2, 1, 0, 1, 1, 1), 1686 | (31, 2, 30, 2, 1, 2), 1687 | (30, 3, 29, 3, 1, 3), 1688 | (29, 4, 28, 4, 1, 4), 1689 | (28, 5, 27, 5, 1, 5), 1690 | (27, 6, 26, 6, 1, 6), 1691 | (26, 7, 25, 7, 1, 7), 1692 | (25, 8, 24, 8, 1, 8), 1693 | (24, 9, 23, 9, 1, 9), 1694 | (23, 10, 22, 10, 1, 10), 1695 | (22, 11, 21, 11, 1, 11), 1696 | (21, 12, 20, 12, 1, 12), 1697 | (20, 13, 19, 13, 1, 13), 1698 | (19, 14, 18, 14, 1, 14), 1699 | (18, 15, 17, 15, 1, 15), 1700 | (17, 16, 16, 16, 1, 16), 1701 | (16, 17, 15, 17, 1, 17), 1702 | (15, 18, 14, 18, 1, 18), 1703 | (14, 19, 13, 19, 1, 19), 1704 | (13, 20, 12, 20, 1, 20), 1705 | (12, 21, 11, 21, 1, 21), 1706 | (11, 22, 10, 22, 1, 22), 1707 | (10, 23, 9, 23, 1, 23), 1708 | (9, 24, 8, 24, 1, 24), 1709 | (8, 25, 7, 25, 1, 25), 1710 | (7, 26, 6, 26, 1, 26), 1711 | (6, 27, 5, 27, 1, 27), 1712 | (5, 28, 4, 28, 1, 28), 1713 | (4, 29, 3, 29, 1, 29), 1714 | (0, 30, 32, 30, 1, 30), 1715 | (3, 31, 2, 31, 1, 31), 1716 | ] 1717 | .into_iter() 1718 | .map(|(vx, nx, vy, ny, vz, nz)| Shape { 1719 | primitive: Triangle( 1720 | (vx, None, Some(nx)), 1721 | (vy, None, Some(ny)), 1722 | (vz, None, Some(nz)), 1723 | ), 1724 | groups: vec![], 1725 | smoothing_groups: vec![], 1726 | }) 1727 | .collect(), 1728 | }], 1729 | }], 1730 | }); 1731 | assert_eq!(parse(test_case), expected); 1732 | } 1733 | 1734 | #[test] 1735 | fn test_smoothing_groups() { 1736 | use self::Primitive::Triangle; 1737 | 1738 | let test_case = r#" 1739 | # Blender v2.72 (sub 0) OBJ File: 'dome.blend' 1740 | # www.blender.org 1741 | mtllib dome.mtl 1742 | o Dome 1743 | v -0.382683 0.923880 0.000000 1744 | v -0.707107 0.707107 0.000000 1745 | v -0.923880 0.382683 0.000000 1746 | v -1.000000 -0.000000 0.000000 1747 | v -0.270598 0.923880 -0.270598 1748 | v -0.500000 0.707107 -0.500000 1749 | v -0.653282 0.382683 -0.653281 1750 | v -0.707107 -0.000000 -0.707107 1751 | v -0.000000 0.923880 -0.382683 1752 | v -0.000000 0.707107 -0.707107 1753 | v -0.000000 0.382683 -0.923879 1754 | v -0.000000 -0.000000 -1.000000 1755 | v -0.000000 1.000000 0.000000 1756 | v 0.270598 0.923880 -0.270598 1757 | v 0.500000 0.707107 -0.500000 1758 | v 0.653281 0.382683 -0.653281 1759 | v 0.707106 -0.000000 -0.707107 1760 | v 0.382683 0.923880 -0.000000 1761 | v 0.707106 0.707107 -0.000000 1762 | v 0.923879 0.382683 -0.000000 1763 | v 1.000000 -0.000000 -0.000000 1764 | v 0.270598 0.923880 0.270598 1765 | v 0.500000 0.707107 0.500000 1766 | v 0.653281 0.382683 0.653281 1767 | v 0.707106 -0.000000 0.707107 1768 | v -0.000000 0.923880 0.382683 1769 | v -0.000000 0.707107 0.707107 1770 | v -0.000000 0.382683 0.923879 1771 | v -0.000000 -0.000000 1.000000 1772 | v -0.270598 0.923880 0.270598 1773 | v -0.500000 0.707107 0.500000 1774 | v -0.653281 0.382683 0.653281 1775 | v -0.707107 -0.000000 0.707107 1776 | usemtl None 1777 | s 1 1778 | f 4 3 7 1779 | f 3 2 6 1780 | f 1 5 6 1781 | f 7 11 12 1782 | f 6 10 11 1783 | f 5 9 10 1784 | f 11 16 17 1785 | f 11 10 15 1786 | f 10 9 14 1787 | f 16 20 21 1788 | f 15 19 20 1789 | f 14 18 19 1790 | f 20 24 25 1791 | f 20 19 23 1792 | f 18 22 23 1793 | f 24 28 29 1794 | f 24 23 27 1795 | f 23 22 26 1796 | f 28 32 33 1797 | f 28 27 31 1798 | f 27 26 30 1799 | f 1 13 5 1800 | f 5 13 9 1801 | f 9 13 14 1802 | f 14 13 18 1803 | f 18 13 22 1804 | f 22 13 26 1805 | f 26 13 30 1806 | f 32 3 4 1807 | f 31 2 3 1808 | f 30 1 2 1809 | f 30 13 1 1810 | f 8 4 7 1811 | f 7 3 6 1812 | f 2 1 6 1813 | f 8 7 12 1814 | f 7 6 11 1815 | f 6 5 10 1816 | f 12 11 17 1817 | f 16 11 15 1818 | f 15 10 14 1819 | f 17 16 21 1820 | f 16 15 20 1821 | f 15 14 19 1822 | f 21 20 25 1823 | f 24 20 23 1824 | f 19 18 23 1825 | f 25 24 29 1826 | f 28 24 27 1827 | f 27 23 26 1828 | f 29 28 33 1829 | f 32 28 31 1830 | f 31 27 30 1831 | f 33 32 4 1832 | f 32 31 3 1833 | f 31 30 2 1834 | s 2 1835 | f 33 4 8 1836 | f 29 33 25 1837 | f 12 17 21 1838 | f 12 33 8 1839 | f 33 21 25 1840 | f 21 33 12 1841 | "#; 1842 | 1843 | let expected = Ok(ObjSet { 1844 | material_library: Some("dome.mtl".to_owned()), 1845 | objects: vec![Object { 1846 | name: "Dome".to_owned(), 1847 | vertices: vec![ 1848 | (-0.382683, 0.92388, 0.0), 1849 | (-0.707107, 0.707107, 0.0), 1850 | (-0.92388, 0.382683, 0.0), 1851 | (-1.0, 0.0, 0.0), 1852 | (-0.270598, 0.92388, -0.270598), 1853 | (-0.5, 0.707107, -0.5), 1854 | (-0.653282, 0.382683, -0.653281), 1855 | (-0.707107, 0.0, -0.707107), 1856 | (0.0, 0.92388, -0.382683), 1857 | (0.0, 0.707107, -0.707107), 1858 | (0.0, 0.382683, -0.923879), 1859 | (0.0, 0.0, -1.0), 1860 | (0.0, 1.0, 0.0), 1861 | (0.270598, 0.92388, -0.270598), 1862 | (0.5, 0.707107, -0.5), 1863 | (0.653281, 0.382683, -0.653281), 1864 | (0.707106, 0.0, -0.707107), 1865 | (0.382683, 0.92388, 0.0), 1866 | (0.707106, 0.707107, 0.0), 1867 | (0.923879, 0.382683, 0.0), 1868 | (1.0, 0.0, 0.0), 1869 | (0.270598, 0.92388, 0.270598), 1870 | (0.5, 0.707107, 0.5), 1871 | (0.653281, 0.382683, 0.653281), 1872 | (0.707106, 0.0, 0.707107), 1873 | (0.0, 0.92388, 0.382683), 1874 | (0.0, 0.707107, 0.707107), 1875 | (0.0, 0.382683, 0.923879), 1876 | (0.0, 0.0, 1.0), 1877 | (-0.270598, 0.92388, 0.270598), 1878 | (-0.5, 0.707107, 0.5), 1879 | (-0.653281, 0.382683, 0.653281), 1880 | (-0.707107, 0.0, 0.707107), 1881 | ] 1882 | .into_iter() 1883 | .map(|(x, y, z)| Vertex { x, y, z }) 1884 | .collect(), 1885 | tex_vertices: vec![], 1886 | normals: vec![], 1887 | geometry: vec![Geometry { 1888 | material_name: Some("None".to_owned()), 1889 | shapes: vec![ 1890 | (6, 3, 2, 1), 1891 | (5, 2, 1, 1), 1892 | (5, 0, 4, 1), 1893 | (11, 6, 10, 1), 1894 | (10, 5, 9, 1), 1895 | (9, 4, 8, 1), 1896 | (16, 10, 15, 1), 1897 | (14, 10, 9, 1), 1898 | (13, 9, 8, 1), 1899 | (20, 15, 19, 1), 1900 | (19, 14, 18, 1), 1901 | (18, 13, 17, 1), 1902 | (24, 19, 23, 1), 1903 | (22, 19, 18, 1), 1904 | (22, 17, 21, 1), 1905 | (28, 23, 27, 1), 1906 | (26, 23, 22, 1), 1907 | (25, 22, 21, 1), 1908 | (32, 27, 31, 1), 1909 | (30, 27, 26, 1), 1910 | (29, 26, 25, 1), 1911 | (4, 0, 12, 1), 1912 | (8, 4, 12, 1), 1913 | (13, 8, 12, 1), 1914 | (17, 13, 12, 1), 1915 | (21, 17, 12, 1), 1916 | (25, 21, 12, 1), 1917 | (29, 25, 12, 1), 1918 | (3, 31, 2, 1), 1919 | (2, 30, 1, 1), 1920 | (1, 29, 0, 1), 1921 | (0, 29, 12, 1), 1922 | (6, 7, 3, 1), 1923 | (5, 6, 2, 1), 1924 | (5, 1, 0, 1), 1925 | (11, 7, 6, 1), 1926 | (10, 6, 5, 1), 1927 | (9, 5, 4, 1), 1928 | (16, 11, 10, 1), 1929 | (14, 15, 10, 1), 1930 | (13, 14, 9, 1), 1931 | (20, 16, 15, 1), 1932 | (19, 15, 14, 1), 1933 | (18, 14, 13, 1), 1934 | (24, 20, 19, 1), 1935 | (22, 23, 19, 1), 1936 | (22, 18, 17, 1), 1937 | (28, 24, 23, 1), 1938 | (26, 27, 23, 1), 1939 | (25, 26, 22, 1), 1940 | (32, 28, 27, 1), 1941 | (30, 31, 27, 1), 1942 | (29, 30, 26, 1), 1943 | (3, 32, 31, 1), 1944 | (2, 31, 30, 1), 1945 | (1, 30, 29, 1), 1946 | (7, 32, 3, 2), 1947 | (24, 28, 32, 2), 1948 | (20, 11, 16, 2), 1949 | (7, 11, 32, 2), 1950 | (24, 32, 20, 2), 1951 | (11, 20, 32, 2), 1952 | ] 1953 | .into_iter() 1954 | .map(|(x, y, z, s)| Shape { 1955 | primitive: Triangle((x, None, None), (y, None, None), (z, None, None)), 1956 | groups: vec![], 1957 | smoothing_groups: vec![s], 1958 | }) 1959 | .collect(), 1960 | }], 1961 | }], 1962 | }); 1963 | 1964 | assert_eq!(parse(test_case), expected); 1965 | } 1966 | 1967 | #[test] 1968 | fn no_mtls() { 1969 | use self::Primitive::Triangle; 1970 | 1971 | let test_case = r#" 1972 | # Blender v2.71 (sub 0) OBJ File: 'cube.blend' 1973 | # www.blender.org 1974 | o Cube 1975 | v 1.000000 -1.000000 -1.000000 1976 | v 1.000000 -1.000000 1.000000 1977 | v -1.000000 -1.000000 1.000000 1978 | v -1.000000 -1.000000 -1.000000 1979 | v 1.000000 1.000000 -0.999999 1980 | v 0.999999 1.000000 1.000001 1981 | v -1.000000 1.000000 1.000000 1982 | v -1.000000 1.000000 -1.000000 1983 | vt 1.004952 0.498633 1984 | vt 0.754996 0.498236 1985 | vt 0.755393 0.248279 1986 | vt 1.005349 0.248677 1987 | vt 0.255083 0.497442 1988 | vt 0.255480 0.247485 1989 | vt 0.505437 0.247882 1990 | vt 0.505039 0.497839 1991 | vt 0.754598 0.748193 1992 | vt 0.504642 0.747795 1993 | vt 0.505834 -0.002074 1994 | vt 0.755790 -0.001677 1995 | vt 0.005127 0.497044 1996 | vt 0.005524 0.247088 1997 | s off 1998 | f 1/1 2/2 3/3 4/4 1999 | f 5/5 8/6 7/7 6/8 2000 | f 1/9 5/10 6/8 2/2 2001 | f 2/2 6/8 7/7 3/3 2002 | f 3/3 7/7 8/11 4/12 2003 | f 5/5 1/13 4/14 8/6 2004 | "#; 2005 | 2006 | let expected = Ok(ObjSet { 2007 | material_library: None, 2008 | objects: vec![Object { 2009 | name: "Cube".to_owned(), 2010 | vertices: vec![ 2011 | (1.0, -1.0, -1.0), 2012 | (1.0, -1.0, 1.0), 2013 | (-1.0, -1.0, 1.0), 2014 | (-1.0, -1.0, -1.0), 2015 | (1.0, 1.0, -1.0), 2016 | (1.0, 1.0, 1.0), 2017 | (-1.0, 1.0, 1.0), 2018 | (-1.0, 1.0, -1.0), 2019 | ] 2020 | .into_iter() 2021 | .map(|(x, y, z)| Vertex { x, y, z }) 2022 | .collect(), 2023 | tex_vertices: vec![ 2024 | (1.004952, 0.498633), 2025 | (0.754996, 0.498236), 2026 | (0.755393, 0.248279), 2027 | (1.005349, 0.248677), 2028 | (0.255083, 0.497442), 2029 | (0.25548, 0.247485), 2030 | (0.505437, 0.247882), 2031 | (0.505039, 0.497839), 2032 | (0.754598, 0.748193), 2033 | (0.504642, 0.747795), 2034 | (0.505834, -0.002074), 2035 | (0.75579, -0.001677), 2036 | (0.005127, 0.497044), 2037 | (0.005524, 0.247088), 2038 | ] 2039 | .into_iter() 2040 | .map(|(u, v)| TVertex { u, v, w: 0. }) 2041 | .collect(), 2042 | normals: vec![], 2043 | geometry: vec![Geometry { 2044 | material_name: None, 2045 | shapes: vec![ 2046 | (3, 3, 0, 0, 1, 1), 2047 | (3, 3, 1, 1, 2, 2), 2048 | (5, 7, 4, 4, 7, 5), 2049 | (5, 7, 7, 5, 6, 6), 2050 | (1, 1, 0, 8, 4, 9), 2051 | (1, 1, 4, 9, 5, 7), 2052 | (2, 2, 1, 1, 5, 7), 2053 | (2, 2, 5, 7, 6, 6), 2054 | (3, 11, 2, 2, 6, 6), 2055 | (3, 11, 6, 6, 7, 10), 2056 | (7, 5, 4, 4, 0, 12), 2057 | (7, 5, 0, 12, 3, 13), 2058 | ] 2059 | .into_iter() 2060 | .map(|(vx, tx, vy, ty, vz, tz)| Shape { 2061 | primitive: Triangle( 2062 | (vx, Some(tx), None), 2063 | (vy, Some(ty), None), 2064 | (vz, Some(tz), None), 2065 | ), 2066 | groups: vec![], 2067 | smoothing_groups: vec![], 2068 | }) 2069 | .collect(), 2070 | }], 2071 | }], 2072 | }); 2073 | 2074 | assert_eq!(parse(test_case), expected); 2075 | } 2076 | 2077 | #[test] 2078 | fn one_group() { 2079 | use self::Primitive::Triangle; 2080 | 2081 | let input = " 2082 | o Cube 2083 | v 1.000000 -1.000000 -1.000000\r 2084 | v 1.000000 -1.000000 1.000000 2085 | v -1.000000 -1.000000 1.000000 2086 | v -1.000000 -1.000000 -1.000000 2087 | v 1.000000 1.000000 -0.999999 2088 | v 0.999999 1.000000 1.000001 2089 | v -1.000000 1.000000 1.000000 2090 | v -1.000000 1.000000 -1.000000 2091 | vt 1.004952 0.498633 2092 | vt 0.754996 0.498236 2093 | vt 0.755393 0.248279 2094 | vt 1.005349 0.248677 2095 | vt 0.255083 0.497442 2096 | vt 0.255480 0.247485 2097 | vt 0.505437 0.247882 2098 | vt 0.505039 0.497839 2099 | vt 0.754598 0.748193 2100 | vt 0.504642 0.747795 2101 | vt 0.505834 -0.002074 2102 | vt 0.755790 -0.001677 2103 | vt 0.005127 0.497044 2104 | vt 0.005524 0.247088 2105 | g all 2106 | s off 2107 | f 1/1 2/2 3/3 4/4 2108 | f 5/5 8/6 7/7 6/8 2109 | f 1/9 5/10 6/8 2/2 2110 | f 2/2 6/8 7/7 3/3 2111 | f 3/3 7/7 8/11 4/12 2112 | f 5/5 1/13 4/14 8/6 2113 | "; 2114 | 2115 | let expected = ObjSet { 2116 | material_library: None, 2117 | objects: vec![Object { 2118 | name: "Cube".to_owned(), 2119 | vertices: vec![ 2120 | (1.0, -1.0, -1.0), 2121 | (1.0, -1.0, 1.0), 2122 | (-1.0, -1.0, 1.0), 2123 | (-1.0, -1.0, -1.0), 2124 | (1.0, 1.0, -1.0), 2125 | (1.0, 1.0, 1.0), 2126 | (-1.0, 1.0, 1.0), 2127 | (-1.0, 1.0, -1.0), 2128 | ] 2129 | .into_iter() 2130 | .map(|(x, y, z)| Vertex { x, y, z }) 2131 | .collect(), 2132 | tex_vertices: vec![ 2133 | (1.004952, 0.498633), 2134 | (0.754996, 0.498236), 2135 | (0.755393, 0.248279), 2136 | (1.005349, 0.248677), 2137 | (0.255083, 0.497442), 2138 | (0.25548, 0.247485), 2139 | (0.505437, 0.247882), 2140 | (0.505039, 0.497839), 2141 | (0.754598, 0.748193), 2142 | (0.504642, 0.747795), 2143 | (0.505834, -0.002074), 2144 | (0.75579, -0.001677), 2145 | (0.005127, 0.497044), 2146 | (0.005524, 0.247088), 2147 | ] 2148 | .into_iter() 2149 | .map(|(u, v)| TVertex { u, v, w: 0. }) 2150 | .collect(), 2151 | normals: vec![], 2152 | geometry: vec![Geometry { 2153 | material_name: None, 2154 | shapes: vec![ 2155 | (3, 3, 0, 0, 1, 1, "all"), 2156 | (3, 3, 1, 1, 2, 2, "all"), 2157 | (5, 7, 4, 4, 7, 5, "all"), 2158 | (5, 7, 7, 5, 6, 6, "all"), 2159 | (1, 1, 0, 8, 4, 9, "all"), 2160 | (1, 1, 4, 9, 5, 7, "all"), 2161 | (2, 2, 1, 1, 5, 7, "all"), 2162 | (2, 2, 5, 7, 6, 6, "all"), 2163 | (3, 11, 2, 2, 6, 6, "all"), 2164 | (3, 11, 6, 6, 7, 10, "all"), 2165 | (7, 5, 4, 4, 0, 12, "all"), 2166 | (7, 5, 0, 12, 3, 13, "all"), 2167 | ] 2168 | .into_iter() 2169 | .map(|(xv, xt, yv, yt, zv, zt, group)| Shape { 2170 | primitive: Triangle( 2171 | (xv, Some(xt), None), 2172 | (yv, Some(yt), None), 2173 | (zv, Some(zt), None), 2174 | ), 2175 | groups: vec![group.into()], 2176 | smoothing_groups: vec![], 2177 | }) 2178 | .collect(), 2179 | }], 2180 | }], 2181 | }; 2182 | 2183 | assert_eq!(parse(input), Ok(expected)); 2184 | } 2185 | 2186 | #[test] 2187 | fn issue_54() { 2188 | let input = include_str!("issue_54.obj"); 2189 | let _ = parse(input); 2190 | } 2191 | 2192 | #[test] 2193 | fn several_groups() { 2194 | use self::Primitive::Triangle; 2195 | 2196 | let input = r#" 2197 | o Cube 2198 | v 1.000000 -1.000000 -1.000000 2199 | v 1.000000 -1.000000 1.000000 2200 | v -1.000000 -1.000000 1.000000 2201 | v -1.000000 -1.000000 -1.000000 2202 | v 1.000000 1.000000 -0.999999 2203 | v 0.999999 1.000000 1.000001 2204 | v -1.000000 1.000000 1.000000 2205 | v -1.000000 1.000000 -1.000000 2206 | vt 1.004952 0.498633 2207 | vt 0.754996 0.498236 2208 | vt 0.755393 0.248279 2209 | vt 1.005349 0.248677 2210 | vt 0.255083 0.497442 2211 | vt 0.255480 0.247485 2212 | vt 0.505437 0.247882 2213 | vt 0.505039 0.497839 2214 | vt 0.754598 0.748193 2215 | vt 0.504642 0.747795 2216 | vt 0.505834 -0.002074 2217 | vt 0.755790 -0.001677 2218 | vt 0.005127 0.497044 2219 | vt 0.005524 0.247088 2220 | s off 2221 | g face one 2222 | f 1/1 2/2 3/3 4/4 2223 | g face two 2224 | f 5/5 8/6 7/7 6/8 2225 | g face three 2226 | f 1/9 5/10 6/8 2/2 2227 | g face four 2228 | f 2/2 6/8 7/7 3/3 2229 | g face five 2230 | f 3/3 7/7 8/11 4/12 2231 | g face six 2232 | f 5/5 1/13 4/14 8/6"#; 2233 | 2234 | let expected = ObjSet { 2235 | material_library: None, 2236 | objects: vec![Object { 2237 | name: "Cube".to_owned(), 2238 | vertices: vec![ 2239 | Vertex { 2240 | x: 1.0, 2241 | y: -1.0, 2242 | z: -1.0, 2243 | }, 2244 | Vertex { 2245 | x: 1.0, 2246 | y: -1.0, 2247 | z: 1.0, 2248 | }, 2249 | Vertex { 2250 | x: -1.0, 2251 | y: -1.0, 2252 | z: 1.0, 2253 | }, 2254 | Vertex { 2255 | x: -1.0, 2256 | y: -1.0, 2257 | z: -1.0, 2258 | }, 2259 | Vertex { 2260 | x: 1.0, 2261 | y: 1.0, 2262 | z: -1.0, 2263 | }, 2264 | Vertex { 2265 | x: 1.0, 2266 | y: 1.0, 2267 | z: 1.0, 2268 | }, 2269 | Vertex { 2270 | x: -1.0, 2271 | y: 1.0, 2272 | z: 1.0, 2273 | }, 2274 | Vertex { 2275 | x: -1.0, 2276 | y: 1.0, 2277 | z: -1.0, 2278 | }, 2279 | ], 2280 | tex_vertices: vec![ 2281 | TVertex { 2282 | u: 1.004952, 2283 | v: 0.498633, 2284 | w: 0., 2285 | }, 2286 | TVertex { 2287 | u: 0.754996, 2288 | v: 0.498236, 2289 | w: 0., 2290 | }, 2291 | TVertex { 2292 | u: 0.755393, 2293 | v: 0.248279, 2294 | w: 0., 2295 | }, 2296 | TVertex { 2297 | u: 1.005349, 2298 | v: 0.248677, 2299 | w: 0., 2300 | }, 2301 | TVertex { 2302 | u: 0.255083, 2303 | v: 0.497442, 2304 | w: 0., 2305 | }, 2306 | TVertex { 2307 | u: 0.25548, 2308 | v: 0.247485, 2309 | w: 0., 2310 | }, 2311 | TVertex { 2312 | u: 0.505437, 2313 | v: 0.247882, 2314 | w: 0., 2315 | }, 2316 | TVertex { 2317 | u: 0.505039, 2318 | v: 0.497839, 2319 | w: 0., 2320 | }, 2321 | TVertex { 2322 | u: 0.754598, 2323 | v: 0.748193, 2324 | w: 0., 2325 | }, 2326 | TVertex { 2327 | u: 0.504642, 2328 | v: 0.747795, 2329 | w: 0., 2330 | }, 2331 | TVertex { 2332 | u: 0.505834, 2333 | v: -0.002074, 2334 | w: 0., 2335 | }, 2336 | TVertex { 2337 | u: 0.75579, 2338 | v: -0.001677, 2339 | w: 0., 2340 | }, 2341 | TVertex { 2342 | u: 0.005127, 2343 | v: 0.497044, 2344 | w: 0., 2345 | }, 2346 | TVertex { 2347 | u: 0.005524, 2348 | v: 0.247088, 2349 | w: 0., 2350 | }, 2351 | ], 2352 | normals: vec![], 2353 | geometry: vec![Geometry { 2354 | material_name: None, 2355 | shapes: vec![ 2356 | (3, 3, 0, 0, 1, 1, vec!["face", "one"]), 2357 | (3, 3, 1, 1, 2, 2, vec!["face", "one"]), 2358 | (5, 7, 4, 4, 7, 5, vec!["face", "two"]), 2359 | (5, 7, 7, 5, 6, 6, vec!["face", "two"]), 2360 | (1, 1, 0, 8, 4, 9, vec!["face", "three"]), 2361 | (1, 1, 4, 9, 5, 7, vec!["face", "three"]), 2362 | (2, 2, 1, 1, 5, 7, vec!["face", "four"]), 2363 | (2, 2, 5, 7, 6, 6, vec!["face", "four"]), 2364 | (3, 11, 2, 2, 6, 6, vec!["face", "five"]), 2365 | (3, 11, 6, 6, 7, 10, vec!["face", "five"]), 2366 | (7, 5, 4, 4, 0, 12, vec!["face", "six"]), 2367 | (7, 5, 0, 12, 3, 13, vec!["face", "six"]), 2368 | ] 2369 | .into_iter() 2370 | .map(|(vx, tx, vy, ty, vz, tz, groups)| Shape { 2371 | primitive: Triangle( 2372 | (vx, Some(tx), None), 2373 | (vy, Some(ty), None), 2374 | (vz, Some(tz), None), 2375 | ), 2376 | groups: groups.into_iter().map(|s| s.into()).collect(), 2377 | smoothing_groups: vec![], 2378 | }) 2379 | .collect(), 2380 | }], 2381 | }], 2382 | }; 2383 | 2384 | assert_eq!(parse(input), Ok(expected)); 2385 | } 2386 | 2387 | /// Parses a wavefront `.obj` file, returning either the successfully parsed 2388 | /// file, or an error. Support in this parser for the full file format is 2389 | /// best-effort and realistically I will only end up supporting the subset 2390 | /// of the file format which falls under the "things I see exported from blender" 2391 | /// category. 2392 | pub fn parse>(input: S) -> Result { 2393 | Parser::new(input.as_ref()).parse_objset() 2394 | } 2395 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | /// Extensions to orderings. 4 | pub(crate) trait OrderingExt { 5 | /// Lexicographically chains comparisions. 6 | fn lexico Ordering>(self, f: F) -> Self; 7 | } 8 | 9 | impl OrderingExt for Ordering { 10 | fn lexico Ordering>(self, f: F) -> Ordering { 11 | match self { 12 | Ordering::Less | Ordering::Greater => self, 13 | Ordering::Equal => f(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/corrupted_data.rs: -------------------------------------------------------------------------------- 1 | extern crate proptest; 2 | extern crate wavefront_obj; 3 | 4 | use proptest::prelude::*; 5 | use wavefront_obj::obj; 6 | 7 | proptest! { 8 | #[test] 9 | fn detect_corrupted_data(s in "[:alphanum:]+") { 10 | let result = obj::parse(s); 11 | assert!(result.is_err()); 12 | } 13 | } 14 | --------------------------------------------------------------------------------