diff --git a/asteroids/View.cc b/asteroids/View.cc index 000e364..1ca0bb0 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -17,6 +17,8 @@ #include "AsteroidsEvents.h" #include "RocketEntity.h" +#include "OBJModel.h" + #include #include @@ -77,6 +79,8 @@ int View::OnInit (int argc, char* argv[]) { mShipPartsSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship_parts.png")); mShipPartsSprite.SetSubSpriteCount (10); + mAsteroidModel = LoadOBJModel (Engine::GetResourceFullPath("/data/models/asteroid/asteroid_model.obj")); + Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); Engine::RegisterListener (this, EventShipExplode); @@ -265,6 +269,20 @@ void View::Draw() { DrawWorld (); } + glPushMatrix(); + + glEnable (GL_LIGHTING); + glEnable (GL_LIGHT0); + + glRotatef (90, 1., 0., 0.); +// glScalef (10., 10., 10.); + DrawOBJModel (mAsteroidModel); + + glDisable (GL_LIGHT0); + glDisable (GL_LIGHTING); + + glPopMatrix(); + DrawUi (); // Perform post-Draw actions diff --git a/asteroids/View.h b/asteroids/View.h index 9a7e41b..bddd53b 100644 --- a/asteroids/View.h +++ b/asteroids/View.h @@ -139,7 +139,9 @@ class View : public Engine::ViewBase { Engine::Sprite mShipSprite; Engine::Sprite mShipThrustSprite; Engine::Sprite mShipPartsSprite; - + + Engine::OBJModelPtr mAsteroidModel; + float screen_left; float screen_right; float screen_top; diff --git a/data/models/asteroid/asteroid.blend b/data/models/asteroid/asteroid.blend new file mode 100644 index 0000000..b6a82ab Binary files /dev/null and b/data/models/asteroid/asteroid.blend differ diff --git a/data/models/asteroid/asteroid_model.mtl b/data/models/asteroid/asteroid_model.mtl new file mode 100644 index 0000000..6255d0f --- /dev/null +++ b/data/models/asteroid/asteroid_model.mtl @@ -0,0 +1,12 @@ +# Material Count: 1 +newmtl asteroid_asteroid_texture_full +Ns 37.254902 +Ka 0.069978 0.069978 0.069978 +Kd 0.640000 0.640000 0.640000 +Ks 0.600000 0.600000 0.600000 +Ni 1.000000 +d 1.000000 +illum 2 +#map_Kd asteroid_texture_full.png +map_Kd asteroid_texture_solid.png +map_bump asteroid_normals_uv.png diff --git a/data/models/asteroid/asteroid_model.obj b/data/models/asteroid/asteroid_model.obj new file mode 100755 index 0000000..e8b9bb4 --- /dev/null +++ b/data/models/asteroid/asteroid_model.obj @@ -0,0 +1,377 @@ +# Blender v2.55 (sub 0) OBJ File: 'asteroidfull.blend' +# www.blender.org +mtllib asteroid_model.mtl +o Asteroid_Icosphere +v -1.783176 3.157423 -2.187264 +v -0.623617 3.419646 -2.279537 +v -4.136988 -1.528819 1.175240 +v 1.622063 -3.012534 -1.107284 +v -2.764359 2.370088 -1.340167 +v 0.011242 3.019686 2.574161 +v -3.271742 2.084863 -0.140242 +v -1.924542 3.110817 -0.535404 +v -3.428460 0.084386 1.434556 +v -1.127657 -3.141940 1.362692 +v -3.526288 1.205355 -1.042213 +v -3.326865 -1.851120 -2.151031 +v 3.315345 2.078610 0.192092 +v 1.677497 2.826964 1.998395 +v -3.488970 -0.830072 -2.166265 +v 2.454325 -1.478905 -2.751490 +v 0.892076 -2.198434 3.836253 +v -0.529430 -1.099092 -4.090514 +v -1.530568 2.367935 2.067323 +v -0.082300 -2.695156 3.227212 +v -4.186931 -0.713751 0.795901 +v -3.847708 0.187925 -0.543515 +v 2.213019 2.683415 -2.315270 +v -3.271944 1.840718 0.932036 +v 0.112475 -1.622629 4.020147 +v -1.052165 1.693250 -4.113129 +v -1.423264 -0.272173 -4.052314 +v -2.335668 2.112422 -2.554989 +v 1.706754 -3.029854 0.171139 +v -2.704852 0.116766 -2.710793 +v 3.689623 1.837504 -1.209753 +v -1.948996 -3.023728 0.875353 +v 3.308379 -0.730487 -2.346540 +v -0.899223 -1.466655 3.453547 +v 3.176398 -0.164593 2.544505 +v 1.450483 3.221935 0.923732 +v -2.242297 -2.166627 2.063739 +v -1.472705 -1.333648 -3.420503 +v 3.871596 0.740806 1.546153 +v 2.533928 1.975883 1.930760 +v 0.492799 2.513122 -3.092595 +v -0.349212 3.148993 -0.029523 +v 3.267344 1.184023 2.241151 +v -4.353748 -1.612557 -0.495422 +v -1.930530 1.654793 2.510300 +v 0.573979 -0.844695 -4.044802 +v 0.528275 -3.143214 -1.789249 +v 3.692719 -1.577900 -1.071348 +v 1.168138 1.079678 3.139852 +v 3.753358 -0.623585 -0.108652 +v 3.379341 -1.003592 1.838246 +v -1.351585 2.874982 -3.067893 +v 1.766429 -1.157478 3.728364 +v 3.667178 1.594924 1.024512 +v -0.884260 1.836161 2.998022 +v -1.073378 -3.132030 -1.461949 +v -0.467529 3.159610 1.532913 +v 3.709228 0.144063 -1.087636 +v 3.605958 -1.445132 -1.878629 +v 2.660886 3.124851 -0.854361 +v -0.436671 0.126434 -4.552140 +v -2.266935 -2.809002 -1.819118 +v -2.916038 -0.990559 1.868705 +v 0.627561 3.215132 -1.810096 +v 2.894693 -2.446815 -1.574007 +v 0.482250 2.215932 3.057291 +v 2.681753 1.396219 -2.708434 +v 0.310227 -2.120141 -3.148356 +v 3.060341 -1.967882 0.490186 +v 1.224500 -2.950663 1.059580 +v 3.238048 2.395264 -1.831406 +v 2.981815 0.133216 -2.548208 +v 3.870637 0.807691 -0.134255 +v 0.576682 -3.072934 2.294799 +v -0.354720 0.674670 3.589938 +v 1.886084 -2.119545 3.151299 +v -3.599368 -2.470988 0.392379 +v 0.714642 0.489363 -4.151877 +vt 0.962265 0.728057 +vt 0.899626 0.745931 +vt 0.929662 0.684568 +vt 0.750291 0.839492 +vt 0.789824 0.780340 +vt 0.836636 0.898960 +vt 0.197601 0.657079 +vt 0.236349 0.516526 +vt 0.295964 0.556306 +vt 0.406744 0.084655 +vt 0.437681 0.165538 +vt 0.371831 0.150081 +vt 0.937341 0.802735 +vt 0.739755 0.637792 +vt 0.713864 0.558013 +vt 0.790163 0.605118 +vt 0.466004 0.054740 +vt 0.531386 0.156801 +vt 0.407272 0.036392 +vt 0.227384 0.053071 +vt 0.204144 0.126771 +vt 0.146542 0.125405 +vt 0.861072 0.549518 +vt 0.871220 0.610164 +vt 0.540887 0.052625 +vt 0.483144 0.379133 +vt 0.531410 0.373100 +vt 0.566293 0.430810 +vt 0.066866 0.123562 +vt 0.065190 0.224005 +vt 0.002406 0.127618 +vt 0.881327 0.854767 +vt 0.500670 0.000000 +vt 0.321590 0.676758 +vt 0.371831 0.718807 +vt 0.319555 0.758685 +vt 0.885719 0.501947 +vt 0.883607 0.391689 +vt 0.934425 0.485845 +vt 0.000000 0.251617 +vt 0.741391 0.942675 +vt 0.814392 0.974820 +vt 0.777879 1.000000 +vt 0.635861 0.539129 +vt 0.561478 0.627398 +vt 0.446596 0.581324 +vt 0.695576 0.850029 +vt 0.128100 0.635461 +vt 0.141764 0.513552 +vt 0.359817 0.484969 +vt 0.371774 0.529646 +vt 0.372111 0.218880 +vt 0.062065 0.038661 +vt 0.000031 0.066144 +vt 0.752155 0.392965 +vt 0.762496 0.520997 +vt 0.699231 0.418395 +vt 0.845722 0.651852 +vt 0.741723 0.694331 +vt 0.371831 0.596760 +vt 0.426111 0.638510 +vt 0.365584 0.589421 +vt 0.395927 0.271007 +vt 0.515240 0.285039 +vt 0.501419 0.319046 +vt 0.561706 0.232608 +vt 0.118925 0.447635 +vt 0.173827 0.444650 +vt 0.066425 0.272682 +vt 0.752460 0.638720 +vt 0.864116 0.319046 +vt 0.634457 0.598443 +vt 0.643782 0.659783 +vt 0.207013 0.293435 +vt 0.808222 0.487833 +vt 0.608847 0.000134 +vt 0.695638 0.677489 +vt 0.696948 0.736016 +vt 0.157426 0.707879 +vt 0.668226 0.056560 +vt 0.671934 0.003912 +vt 0.198776 0.481993 +vt 0.273634 0.449727 +vt 0.804622 0.216599 +vt 0.791229 0.301115 +vt 0.747758 0.249256 +vt 0.721640 0.137550 +vt 0.191523 0.354536 +vt 0.820792 0.282262 +vt 0.950867 0.369066 +vt 0.324779 0.115257 +vt 0.323095 0.062677 +vt 0.695576 0.356821 +vt 0.647322 0.164260 +vt 0.005872 0.513634 +vt 0.041462 0.548754 +vt 0.006022 0.612097 +vt 0.429869 0.368933 +vt 0.426298 0.477853 +vt 0.044569 0.356401 +vt 0.330651 0.269674 +vt 0.101029 0.677297 +vt 0.075503 0.599948 +vt 0.682265 0.212902 +vt 0.721592 0.099681 +vt 0.985494 0.451521 +vt 0.944144 0.577592 +vt 0.268275 0.230026 +vt 0.265275 0.163033 +vt 0.610146 0.272739 +vt 1.000000 0.368928 +vt 0.371831 0.160502 +vt 0.294827 0.352889 +vt 0.179156 0.406072 +vt 0.695576 0.633236 +vt 0.912297 0.560646 +vt 0.068301 0.408852 +vt 0.837648 0.638720 +vt 0.183490 0.418129 +vt 0.674900 0.548703 +vt 0.360881 0.086504 +vt 0.170343 0.000000 +vt 0.499177 0.455094 +vt 0.730293 0.321189 +vt 0.945692 0.869305 +vt 0.006033 0.668066 +vt 0.005872 0.612097 +vt 0.055275 0.612495 +vt 0.953257 0.914727 +vt 0.586418 0.354851 +vt 0.666332 0.438154 +vt 0.252839 0.408852 +vt 0.328739 0.431163 +vt 0.388098 0.659674 +vt 0.000000 0.475701 +vt 0.041462 0.408852 +vt 0.041403 0.513634 +vt 0.489351 0.319046 +vt 0.721592 0.319046 +vt 0.354845 0.326581 +vt 0.240882 0.751040 +usemtl asteroid_asteroid_normals_uv.p +s 1 +f 1/1 28/2 52/3 +f 15/4 30/5 22/6 +f 78/7 72/8 67/9 +f 1/10 8/11 5/12 +f 1/1 5/13 28/2 +f 48/14 69/15 50/16 +f 2/17 42/18 8/11 +f 8/11 1/10 2/17 +f 2/17 1/10 52/19 +f 69/20 29/21 70/22 +f 50/16 39/23 73/24 +f 2/17 64/25 42/18 +f 3/26 21/27 9/28 +f 74/29 10/30 20/31 +f 30/5 28/2 11/32 +f 41/33 64/25 2/17 +f 41/34 2/35 52/36 +f 43/37 49/38 40/39 +f 37/40 20/31 10/30 +f 44/41 21/42 3/43 +f 45/44 75/45 34/46 +f 12/47 30/5 15/4 +f 46/48 16/49 72/8 +f 72/8 31/50 71/51 +f 5/12 8/11 7/52 +f 45/44 34/46 9/28 +f 30/5 11/32 22/6 +f 76/53 74/29 17/54 +f 53/55 51/56 76/57 +f 26/58 28/2 30/5 +f 51/56 50/16 69/15 +f 46/48 72/8 78/7 +f 26/58 30/5 27/59 +f 10/30 74/29 70/22 +f 20/60 34/46 25/61 +f 67/9 23/62 41/34 +f 24/63 19/64 45/65 +f 53/55 49/38 43/37 +f 76/53 69/20 70/22 +f 24/63 7/52 8/11 +f 24/63 8/11 57/66 +f 24/63 57/66 19/64 +f 65/67 59/68 16/49 +f 32/69 37/40 10/30 +f 26/58 27/59 61/70 +f 53/55 75/71 49/38 +f 8/11 42/18 57/66 +f 75/45 55/72 66/73 +f 10/30 70/22 56/74 +f 35/75 43/37 39/23 +f 76/53 70/22 74/29 +f 23/76 64/25 41/33 +f 27/59 18/77 61/70 +f 12/47 38/78 30/5 +f 46/48 78/7 61/79 +f 60/80 64/25 23/76 +f 60/80 23/76 71/81 +f 33/82 59/68 58/83 +f 33/82 58/83 72/8 +f 72/8 71/51 67/9 +f 60/84 31/85 13/86 +f 35/75 39/23 51/56 +f 60/84 13/86 36/87 +f 62/88 32/69 56/74 +f 51/56 39/23 50/16 +f 60/84 71/89 31/85 +f 75/71 66/90 49/38 +f 65/91 29/21 69/20 +f 65/91 69/20 48/92 +f 53/55 76/57 17/93 +f 36/94 42/18 64/25 +f 36/94 64/25 60/80 +f 13/95 54/96 40/97 +f 77/98 3/26 37/99 +f 77/100 37/40 32/69 +f 68/101 62/88 56/74 +f 77/100 32/69 62/88 +f 18/102 68/103 46/48 +f 44/41 22/6 21/42 +f 14/104 36/94 13/105 +f 14/106 13/107 40/39 +f 78/7 67/9 41/34 +f 47/108 29/21 4/109 +f 14/104 6/110 36/94 +f 27/59 38/78 18/77 +f 47/108 56/74 70/22 +f 47/108 70/22 29/21 +f 66/90 6/111 14/106 +f 66/90 14/106 40/39 +f 66/90 40/39 49/38 +f 65/91 16/112 68/101 +f 36/94 6/110 57/66 +f 36/94 57/66 42/18 +f 45/44 55/72 75/45 +f 68/101 38/113 62/88 +f 65/91 68/101 47/108 +f 77/100 62/88 12/114 +f 66/73 55/72 6/115 +f 54/116 39/23 43/37 +f 16/49 59/68 33/82 +f 37/99 34/46 20/60 +f 77/100 12/114 44/117 +f 53/55 43/37 35/75 +f 48/14 50/16 58/118 +f 48/119 58/83 59/68 +f 6/115 55/72 19/120 +f 6/110 19/64 57/66 +f 54/116 43/37 40/39 +f 25/61 34/46 75/45 +f 62/88 38/113 12/114 +f 65/91 48/92 59/121 +f 76/53 51/122 69/20 +f 9/28 34/46 63/123 +f 19/120 55/72 45/44 +f 65/91 47/108 4/109 +f 63/123 34/46 37/99 +f 16/49 33/82 72/8 +f 47/108 68/101 56/74 +f 3/26 9/28 63/123 +f 53/55 35/75 51/56 +f 3/26 63/123 37/99 +f 74/29 20/31 17/54 +f 53/55 25/124 75/71 +f 11/32 28/2 5/13 +f 11/32 5/13 7/125 +f 73/126 54/127 13/128 +f 22/6 11/32 7/125 +f 22/6 7/125 24/129 +f 22/130 24/131 9/28 +f 50/132 73/133 58/83 +f 20/60 25/61 17/134 +f 44/41 15/4 22/6 +f 72/8 58/83 31/50 +f 65/91 4/109 29/21 +f 53/55 17/93 25/124 +f 31/50 58/83 73/133 +f 21/135 22/136 9/137 +f 10/30 56/74 32/69 +f 67/9 71/51 23/62 +f 77/98 44/138 3/26 +f 27/59 30/5 38/78 +f 31/85 73/139 13/86 +f 68/101 18/140 38/113 +f 24/131 45/44 9/28 +f 73/24 39/23 54/116 +f 26/141 61/79 78/7 +f 26/141 78/7 41/34 +f 44/41 12/47 15/4 +f 26/141 41/34 52/36 +f 46/48 68/103 16/49 +f 18/102 46/48 61/79 +f 26/58 52/3 28/2 diff --git a/data/models/asteroid/asteroid_normals_uv.png b/data/models/asteroid/asteroid_normals_uv.png new file mode 100755 index 0000000..4c37a41 Binary files /dev/null and b/data/models/asteroid/asteroid_normals_uv.png differ diff --git a/data/models/asteroid/asteroid_texture_solid.png b/data/models/asteroid/asteroid_texture_solid.png new file mode 100755 index 0000000..e9221ec Binary files /dev/null and b/data/models/asteroid/asteroid_texture_solid.png differ diff --git a/data/shaders/blinn_phong.glsl b/data/shaders/blinn_phong.glsl new file mode 100644 index 0000000..4862860 --- /dev/null +++ b/data/shaders/blinn_phong.glsl @@ -0,0 +1,63 @@ +Copyright (c) 2006-2007 dhpoware. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +------------------------------------------------------------------------------- + +Per-fragment Blinn-Phong shader for a single directional light source. + +[vert] + +#version 110 + +varying vec3 normal; + +void main() +{ + normal = normalize(gl_NormalMatrix * gl_Normal); + + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; +} + +[frag] + +#version 110 + +uniform sampler2D colorMap; +uniform float materialAlpha; + +varying vec3 normal; + +void main() +{ + vec3 n = normalize(normal); + + float nDotL = max(0.0, dot(n, gl_LightSource[0].position.xyz)); + float nDotH = max(0.0, dot(normal, vec3(gl_LightSource[0].halfVector))); + float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess); + + vec4 ambient = gl_FrontLightProduct[0].ambient; + vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL; + vec4 specular = gl_FrontLightProduct[0].specular * power; + vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular; + + gl_FragColor = color * texture2D(colorMap, gl_TexCoord[0].st); + gl_FragColor.a = materialAlpha; +} diff --git a/data/shaders/normal_mapping.glsl b/data/shaders/normal_mapping.glsl new file mode 100644 index 0000000..6e5dff9 --- /dev/null +++ b/data/shaders/normal_mapping.glsl @@ -0,0 +1,89 @@ +Copyright (c) 2006-2007 dhpoware. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +------------------------------------------------------------------------------- + +Tangent space normal mapping shader for a single directional light source. + +The tangent vector is passed to the vertex shader in gl_MultiTexCoord1. The +tangent vector is assumed to be a four component vector. The tangent vector's +w component indicates the handedness of the local tangent space at this vertex. +The handedness is used to calculate the bitangent vector. The reason for the +inclusion of the handedness component is to allow for triangles with mirrored +texture mappings. + +------------------------------------------------------------------------------- + +[vert] + +#version 110 + +varying vec3 lightDir; +varying vec3 halfVector; + +void main() +{ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; + + vec3 n = normalize(gl_NormalMatrix * gl_Normal); + vec3 t = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz); + vec3 b = cross(n, t) * gl_MultiTexCoord1.w; + + mat3 tbnMatrix = mat3(t.x, b.x, n.x, + t.y, b.y, n.y, + t.z, b.z, n.z); + + lightDir = gl_LightSource[0].position.xyz; + lightDir = tbnMatrix * lightDir; + + halfVector = gl_LightSource[0].halfVector.xyz; + halfVector = tbnMatrix * halfVector; +} + +[frag] + +#version 110 + +uniform sampler2D colorMap; +uniform sampler2D normalMap; +uniform float materialAlpha; + +varying vec3 lightDir; +varying vec3 halfVector; + +void main() +{ + vec3 n = normalize(texture2D(normalMap, gl_TexCoord[0].st).rgb * 2.0 - 1.0); + vec3 l = normalize(lightDir); + vec3 h = normalize(halfVector); + + float nDotL = max(0.0, dot(n, l)); + float nDotH = max(0.0, dot(n, h)); + float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess); + + vec4 ambient = gl_FrontLightProduct[0].ambient; + vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL; + vec4 specular = gl_FrontLightProduct[0].specular * power; + vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular; + + gl_FragColor = color * texture2D(colorMap, gl_TexCoord[0].st); + gl_FragColor.a = materialAlpha; +} diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 6dee331..6b8e242 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -34,6 +34,7 @@ SET ( ENGINE_SRCS VariablesCommands.cc SimpleConsoleOverlay.cc Sprite.cc + OBJModel.cc IMGUIControls.cc Engine.cc diff --git a/engine/OBJModel.cc b/engine/OBJModel.cc new file mode 100644 index 0000000..b63b5bc --- /dev/null +++ b/engine/OBJModel.cc @@ -0,0 +1,1294 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2007 dhpoware. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +// +// The methods normalize() and scale() are based on source code from +// http://www.mvps.org/directx/articles/scalemesh9.htm. +// +// The addVertex() method is based on source code from the Direct3D MeshFromOBJ +// sample found in the DirectX SDK. +// +// The generateTangents() method is based on public source code from +// http://www.terathon.com/code/tangent.php. +// +// The importGeometryFirstPass(), importGeometrySecondPass(), and +// importMaterials() methods are based on source code from Nate Robins' OpenGL +// Tutors programs (http://www.xmission.com/~nate/tutors.html). +// +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include "Engine.h" +#include "OBJModel.h" + +namespace Engine { + +namespace +{ + bool MeshCompFunc(const OBJModel::Mesh &lhs, const OBJModel::Mesh &rhs) + { + return lhs.pMaterial->alpha > rhs.pMaterial->alpha; + } +} + +OBJModel::OBJModel() +{ + m_hasPositions = false; + m_hasNormals = false; + m_hasTextureCoords = false; + m_hasTangents = false; + + m_numberOfVertexCoords = 0; + m_numberOfTextureCoords = 0; + m_numberOfNormals = 0; + m_numberOfTriangles = 0; + m_numberOfMaterials = 0; + m_numberOfMeshes = 0; + + m_center[0] = m_center[1] = m_center[2] = 0.0f; + m_width = m_height = m_length = m_radius = 0.0f; +} + +OBJModel::~OBJModel() +{ + destroy(); +} + +void OBJModel::bounds(float center[3], float &width, float &height, + float &length, float &radius) const +{ + float xMax = std::numeric_limits::min(); + float yMax = std::numeric_limits::min(); + float zMax = std::numeric_limits::min(); + + float xMin = std::numeric_limits::max(); + float yMin = std::numeric_limits::max(); + float zMin = std::numeric_limits::max(); + + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + + int numVerts = static_cast(m_vertexBuffer.size()); + + for (int i = 0; i < numVerts; ++i) + { + x = m_vertexBuffer[i].position[0]; + y = m_vertexBuffer[i].position[1]; + z = m_vertexBuffer[i].position[2]; + + if (x < xMin) + xMin = x; + + if (x > xMax) + xMax = x; + + if (y < yMin) + yMin = y; + + if (y > yMax) + yMax = y; + + if (z < zMin) + zMin = z; + + if (z > zMax) + zMax = z; + } + + center[0] = (xMin + xMax) / 2.0f; + center[1] = (yMin + yMax) / 2.0f; + center[2] = (zMin + zMax) / 2.0f; + + width = xMax - xMin; + height = yMax - yMin; + length = zMax - zMin; + + radius = std::max(std::max(width, height), length); +} + +void OBJModel::destroy() +{ + m_hasPositions = false; + m_hasTextureCoords = false; + m_hasNormals = false; + m_hasTangents = false; + + m_numberOfVertexCoords = 0; + m_numberOfTextureCoords = 0; + m_numberOfNormals = 0; + m_numberOfTriangles = 0; + m_numberOfMaterials = 0; + m_numberOfMeshes = 0; + + m_center[0] = m_center[1] = m_center[2] = 0.0f; + m_width = m_height = m_length = m_radius = 0.0f; + + m_directoryPath.clear(); + + m_meshes.clear(); + m_materials.clear(); + m_vertexBuffer.clear(); + m_indexBuffer.clear(); + m_attributeBuffer.clear(); + + m_vertexCoords.clear(); + m_textureCoords.clear(); + m_normals.clear(); + + m_materialCache.clear(); + m_vertexCache.clear(); +} + +bool OBJModel::import(const char *pszFilename, bool rebuildNormals) +{ + FILE *pFile = fopen(pszFilename, "r"); + + if (!pFile) + return false; + + // Extract the directory the OBJ file is in from the file name. + // This directory path will be used to load the OBJ's associated MTL file. + + m_directoryPath.clear(); + + boost::filesystem::path full_path (pszFilename); + m_directoryPath = full_path.parent_path().string(); + + LogDebug ("parent path for %s is %s", pszFilename, m_directoryPath.c_str()); + + // Import the OBJ file. + + importGeometryFirstPass(pFile); + rewind(pFile); + importGeometrySecondPass(pFile); + fclose(pFile); + + // Perform post import tasks. + + buildMeshes(); + bounds(m_center, m_width, m_height, m_length, m_radius); + + // Build vertex normals if required. + + if (rebuildNormals) + { + generateNormals(); + } + else + { + if (!hasNormals()) + generateNormals(); + } + + // Build tangents is required. + + for (int i = 0; i < m_numberOfMaterials; ++i) + { + if (!m_materials[i].bumpMapFilename.empty()) + { + generateTangents(); + break; + } + } + + return true; +} + +void OBJModel::normalize(float scaleTo, bool center) +{ + float width = 0.0f; + float height = 0.0f; + float length = 0.0f; + float radius = 0.0f; + float centerPos[3] = {0.0f}; + + bounds(centerPos, width, height, length, radius); + + float scalingFactor = scaleTo / radius; + float offset[3] = {0.0f}; + + if (center) + { + offset[0] = -centerPos[0]; + offset[1] = -centerPos[1]; + offset[2] = -centerPos[2]; + } + else + { + offset[0] = 0.0f; + offset[1] = 0.0f; + offset[2] = 0.0f; + } + + scale(scalingFactor, offset); + bounds(m_center, m_width, m_height, m_length, m_radius); +} + +void OBJModel::reverseWinding() +{ + int swap = 0; + + // Reverse face winding. + for (int i = 0; i < static_cast(m_indexBuffer.size()); i += 3) + { + swap = m_indexBuffer[i + 1]; + m_indexBuffer[i + 1] = m_indexBuffer[i + 2]; + m_indexBuffer[i + 2] = swap; + } + + float *pNormal = 0; + float *pTangent = 0; + + // Invert normals and tangents. + for (int i = 0; i < static_cast(m_vertexBuffer.size()); ++i) + { + pNormal = m_vertexBuffer[i].normal; + pNormal[0] = -pNormal[0]; + pNormal[1] = -pNormal[1]; + pNormal[2] = -pNormal[2]; + + pTangent = m_vertexBuffer[i].tangent; + pTangent[0] = -pTangent[0]; + pTangent[1] = -pTangent[1]; + pTangent[2] = -pTangent[2]; + } +} + +void OBJModel::scale(float scaleFactor, float offset[3]) +{ + float *pPosition = 0; + + for (int i = 0; i < static_cast(m_vertexBuffer.size()); ++i) + { + pPosition = m_vertexBuffer[i].position; + + pPosition[0] += offset[0]; + pPosition[1] += offset[1]; + pPosition[2] += offset[2]; + + pPosition[0] *= scaleFactor; + pPosition[1] *= scaleFactor; + pPosition[2] *= scaleFactor; + } +} + +void OBJModel::addTrianglePos(int index, int material, int v0, int v1, int v2) +{ + Vertex vertex = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f} + }; + + m_attributeBuffer[index] = material; + + vertex.position[0] = m_vertexCoords[v0 * 3]; + vertex.position[1] = m_vertexCoords[v0 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v0 * 3 + 2]; + m_indexBuffer[index * 3] = addVertex(v0, &vertex); + + vertex.position[0] = m_vertexCoords[v1 * 3]; + vertex.position[1] = m_vertexCoords[v1 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v1 * 3 + 2]; + m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex); + + vertex.position[0] = m_vertexCoords[v2 * 3]; + vertex.position[1] = m_vertexCoords[v2 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v2 * 3 + 2]; + m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex); +} + +void OBJModel::addTrianglePosNormal(int index, int material, int v0, int v1, + int v2, int vn0, int vn1, int vn2) +{ + Vertex vertex = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f} + }; + + m_attributeBuffer[index] = material; + + vertex.position[0] = m_vertexCoords[v0 * 3]; + vertex.position[1] = m_vertexCoords[v0 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v0 * 3 + 2]; + vertex.normal[0] = m_normals[vn0 * 3]; + vertex.normal[1] = m_normals[vn0 * 3 + 1]; + vertex.normal[2] = m_normals[vn0 * 3 + 2]; + m_indexBuffer[index * 3] = addVertex(v0, &vertex); + + vertex.position[0] = m_vertexCoords[v1 * 3]; + vertex.position[1] = m_vertexCoords[v1 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v1 * 3 + 2]; + vertex.normal[0] = m_normals[vn1 * 3]; + vertex.normal[1] = m_normals[vn1 * 3 + 1]; + vertex.normal[2] = m_normals[vn1 * 3 + 2]; + m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex); + + vertex.position[0] = m_vertexCoords[v2 * 3]; + vertex.position[1] = m_vertexCoords[v2 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v2 * 3 + 2]; + vertex.normal[0] = m_normals[vn2 * 3]; + vertex.normal[1] = m_normals[vn2 * 3 + 1]; + vertex.normal[2] = m_normals[vn2 * 3 + 2]; + m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex); +} + +void OBJModel::addTrianglePosTexCoord(int index, int material, int v0, int v1, + int v2, int vt0, int vt1, int vt2) +{ + Vertex vertex = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f} + }; + + m_attributeBuffer[index] = material; + + vertex.position[0] = m_vertexCoords[v0 * 3]; + vertex.position[1] = m_vertexCoords[v0 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v0 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt0 * 2]; + vertex.texCoord[1] = m_textureCoords[vt0 * 2 + 1]; + m_indexBuffer[index * 3] = addVertex(v0, &vertex); + + vertex.position[0] = m_vertexCoords[v1 * 3]; + vertex.position[1] = m_vertexCoords[v1 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v1 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt1 * 2]; + vertex.texCoord[1] = m_textureCoords[vt1 * 2 + 1]; + m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex); + + vertex.position[0] = m_vertexCoords[v2 * 3]; + vertex.position[1] = m_vertexCoords[v2 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v2 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt2 * 2]; + vertex.texCoord[1] = m_textureCoords[vt2 * 2 + 1]; + m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex); +} + +void OBJModel::addTrianglePosTexCoordNormal(int index, int material, int v0, + int v1, int v2, int vt0, int vt1, + int vt2, int vn0, int vn1, int vn2) +{ + Vertex vertex = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f} + }; + + m_attributeBuffer[index] = material; + + vertex.position[0] = m_vertexCoords[v0 * 3]; + vertex.position[1] = m_vertexCoords[v0 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v0 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt0 * 2]; + vertex.texCoord[1] = m_textureCoords[vt0 * 2 + 1]; + vertex.normal[0] = m_normals[vn0 * 3]; + vertex.normal[1] = m_normals[vn0 * 3 + 1]; + vertex.normal[2] = m_normals[vn0 * 3 + 2]; + m_indexBuffer[index * 3] = addVertex(v0, &vertex); + + vertex.position[0] = m_vertexCoords[v1 * 3]; + vertex.position[1] = m_vertexCoords[v1 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v1 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt1 * 2]; + vertex.texCoord[1] = m_textureCoords[vt1 * 2 + 1]; + vertex.normal[0] = m_normals[vn1 * 3]; + vertex.normal[1] = m_normals[vn1 * 3 + 1]; + vertex.normal[2] = m_normals[vn1 * 3 + 2]; + m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex); + + vertex.position[0] = m_vertexCoords[v2 * 3]; + vertex.position[1] = m_vertexCoords[v2 * 3 + 1]; + vertex.position[2] = m_vertexCoords[v2 * 3 + 2]; + vertex.texCoord[0] = m_textureCoords[vt2 * 2]; + vertex.texCoord[1] = m_textureCoords[vt2 * 2 + 1]; + vertex.normal[0] = m_normals[vn2 * 3]; + vertex.normal[1] = m_normals[vn2 * 3 + 1]; + vertex.normal[2] = m_normals[vn2 * 3 + 2]; + m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex); +} + +int OBJModel::addVertex(int hash, const Vertex *pVertex) +{ + int index = -1; + std::map >::const_iterator iter = m_vertexCache.find(hash); + + if (iter == m_vertexCache.end()) + { + // Vertex hash doesn't exist in the cache. + + index = static_cast(m_vertexBuffer.size()); + m_vertexBuffer.push_back(*pVertex); + m_vertexCache.insert(std::make_pair(hash, std::vector(1, index))); + } + else + { + // One or more vertices have been hashed to this entry in the cache. + + const std::vector &vertices = iter->second; + const Vertex *pCachedVertex = 0; + bool found = false; + + for (std::vector::const_iterator i = vertices.begin(); i != vertices.end(); ++i) + { + index = *i; + pCachedVertex = &m_vertexBuffer[index]; + + if (memcmp(pCachedVertex, pVertex, sizeof(Vertex)) == 0) + { + found = true; + break; + } + } + + if (!found) + { + index = static_cast(m_vertexBuffer.size()); + m_vertexBuffer.push_back(*pVertex); + m_vertexCache[hash].push_back(index); + } + } + + return index; +} + +void OBJModel::buildMeshes() +{ + // Group the model's triangles based on material type. + + Mesh *pMesh = 0; + int materialId = -1; + int numMeshes = 0; + + // Count the number of meshes. + for (int i = 0; i < static_cast(m_attributeBuffer.size()); ++i) + { + if (m_attributeBuffer[i] != materialId) + { + materialId = m_attributeBuffer[i]; + ++numMeshes; + } + } + + // Allocate memory for the meshes and reset counters. + m_numberOfMeshes = numMeshes; + m_meshes.resize(m_numberOfMeshes); + numMeshes = 0; + materialId = -1; + + // Build the meshes. One mesh for each unique material. + for (int i = 0; i < static_cast(m_attributeBuffer.size()); ++i) + { + if (m_attributeBuffer[i] != materialId) + { + materialId = m_attributeBuffer[i]; + pMesh = &m_meshes[numMeshes++]; + pMesh->pMaterial = &m_materials[materialId]; + pMesh->startIndex = i * 3; + ++pMesh->triangleCount; + } + else + { + ++pMesh->triangleCount; + } + } + + // Sort the meshes based on its material alpha. Fully opaque meshes + // towards the front and fully transparent towards the back. + std::sort(m_meshes.begin(), m_meshes.end(), MeshCompFunc); +} + +void OBJModel::generateNormals() +{ + const int *pTriangle = 0; + Vertex *pVertex0 = 0; + Vertex *pVertex1 = 0; + Vertex *pVertex2 = 0; + float edge1[3] = {0.0f, 0.0f, 0.0f}; + float edge2[3] = {0.0f, 0.0f, 0.0f}; + float normal[3] = {0.0f, 0.0f, 0.0f}; + float length = 0.0f; + int totalVertices = getNumberOfVertices(); + int totalTriangles = getNumberOfTriangles(); + + // Initialize all the vertex normals. + for (int i = 0; i < totalVertices; ++i) + { + pVertex0 = &m_vertexBuffer[i]; + pVertex0->normal[0] = 0.0f; + pVertex0->normal[1] = 0.0f; + pVertex0->normal[2] = 0.0f; + } + + // Calculate the vertex normals. + for (int i = 0; i < totalTriangles; ++i) + { + pTriangle = &m_indexBuffer[i * 3]; + + pVertex0 = &m_vertexBuffer[pTriangle[0]]; + pVertex1 = &m_vertexBuffer[pTriangle[1]]; + pVertex2 = &m_vertexBuffer[pTriangle[2]]; + + // Calculate triangle face normal. + + edge1[0] = pVertex1->position[0] - pVertex0->position[0]; + edge1[1] = pVertex1->position[1] - pVertex0->position[1]; + edge1[2] = pVertex1->position[2] - pVertex0->position[2]; + + edge2[0] = pVertex2->position[0] - pVertex0->position[0]; + edge2[1] = pVertex2->position[1] - pVertex0->position[1]; + edge2[2] = pVertex2->position[2] - pVertex0->position[2]; + + normal[0] = (edge1[1] * edge2[2]) - (edge1[2] * edge2[1]); + normal[1] = (edge1[2] * edge2[0]) - (edge1[0] * edge2[2]); + normal[2] = (edge1[0] * edge2[1]) - (edge1[1] * edge2[0]); + + // Accumulate the normals. + + pVertex0->normal[0] += normal[0]; + pVertex0->normal[1] += normal[1]; + pVertex0->normal[2] += normal[2]; + + pVertex1->normal[0] += normal[0]; + pVertex1->normal[1] += normal[1]; + pVertex1->normal[2] += normal[2]; + + pVertex2->normal[0] += normal[0]; + pVertex2->normal[1] += normal[1]; + pVertex2->normal[2] += normal[2]; + } + + // Normalize the vertex normals. + for (int i = 0; i < totalVertices; ++i) + { + pVertex0 = &m_vertexBuffer[i]; + + length = 1.0f / sqrtf(pVertex0->normal[0] * pVertex0->normal[0] + + pVertex0->normal[1] * pVertex0->normal[1] + + pVertex0->normal[2] * pVertex0->normal[2]); + + pVertex0->normal[0] *= length; + pVertex0->normal[1] *= length; + pVertex0->normal[2] *= length; + } + + m_hasNormals = true; +} + +void OBJModel::generateTangents() +{ + const int *pTriangle = 0; + Vertex *pVertex0 = 0; + Vertex *pVertex1 = 0; + Vertex *pVertex2 = 0; + float edge1[3] = {0.0f, 0.0f, 0.0f}; + float edge2[3] = {0.0f, 0.0f, 0.0f}; + float texEdge1[2] = {0.0f, 0.0f}; + float texEdge2[2] = {0.0f, 0.0f}; + float tangent[3] = {0.0f, 0.0f, 0.0f}; + float bitangent[3] = {0.0f, 0.0f, 0.0f}; + float det = 0.0f; + float nDotT = 0.0f; + float bDotB = 0.0f; + float length = 0.0f; + int totalVertices = getNumberOfVertices(); + int totalTriangles = getNumberOfTriangles(); + + // Initialize all the vertex tangents and bitangents. + for (int i = 0; i < totalVertices; ++i) + { + pVertex0 = &m_vertexBuffer[i]; + + pVertex0->tangent[0] = 0.0f; + pVertex0->tangent[1] = 0.0f; + pVertex0->tangent[2] = 0.0f; + pVertex0->tangent[3] = 0.0f; + + pVertex0->bitangent[0] = 0.0f; + pVertex0->bitangent[1] = 0.0f; + pVertex0->bitangent[2] = 0.0f; + } + + // Calculate the vertex tangents and bitangents. + for (int i = 0; i < totalTriangles; ++i) + { + pTriangle = &m_indexBuffer[i * 3]; + + pVertex0 = &m_vertexBuffer[pTriangle[0]]; + pVertex1 = &m_vertexBuffer[pTriangle[1]]; + pVertex2 = &m_vertexBuffer[pTriangle[2]]; + + // Calculate the triangle face tangent and bitangent. + + edge1[0] = pVertex1->position[0] - pVertex0->position[0]; + edge1[1] = pVertex1->position[1] - pVertex0->position[1]; + edge1[2] = pVertex1->position[2] - pVertex0->position[2]; + + edge2[0] = pVertex2->position[0] - pVertex0->position[0]; + edge2[1] = pVertex2->position[1] - pVertex0->position[1]; + edge2[2] = pVertex2->position[2] - pVertex0->position[2]; + + texEdge1[0] = pVertex1->texCoord[0] - pVertex0->texCoord[0]; + texEdge1[1] = pVertex1->texCoord[1] - pVertex0->texCoord[1]; + + texEdge2[0] = pVertex2->texCoord[0] - pVertex0->texCoord[0]; + texEdge2[1] = pVertex2->texCoord[1] - pVertex0->texCoord[1]; + + det = texEdge1[0] * texEdge2[1] - texEdge2[0] * texEdge1[1]; + + if (fabs(det) < 1e-6f) + { + tangent[0] = 1.0f; + tangent[1] = 0.0f; + tangent[2] = 0.0f; + + bitangent[0] = 0.0f; + bitangent[1] = 1.0f; + bitangent[2] = 0.0f; + } + else + { + det = 1.0f / det; + + tangent[0] = (texEdge2[1] * edge1[0] - texEdge1[1] * edge2[0]) * det; + tangent[1] = (texEdge2[1] * edge1[1] - texEdge1[1] * edge2[1]) * det; + tangent[2] = (texEdge2[1] * edge1[2] - texEdge1[1] * edge2[2]) * det; + + bitangent[0] = (-texEdge2[0] * edge1[0] + texEdge1[0] * edge2[0]) * det; + bitangent[1] = (-texEdge2[0] * edge1[1] + texEdge1[0] * edge2[1]) * det; + bitangent[2] = (-texEdge2[0] * edge1[2] + texEdge1[0] * edge2[2]) * det; + } + + // Accumulate the tangents and bitangents. + + pVertex0->tangent[0] += tangent[0]; + pVertex0->tangent[1] += tangent[1]; + pVertex0->tangent[2] += tangent[2]; + pVertex0->bitangent[0] += bitangent[0]; + pVertex0->bitangent[1] += bitangent[1]; + pVertex0->bitangent[2] += bitangent[2]; + + pVertex1->tangent[0] += tangent[0]; + pVertex1->tangent[1] += tangent[1]; + pVertex1->tangent[2] += tangent[2]; + pVertex1->bitangent[0] += bitangent[0]; + pVertex1->bitangent[1] += bitangent[1]; + pVertex1->bitangent[2] += bitangent[2]; + + pVertex2->tangent[0] += tangent[0]; + pVertex2->tangent[1] += tangent[1]; + pVertex2->tangent[2] += tangent[2]; + pVertex2->bitangent[0] += bitangent[0]; + pVertex2->bitangent[1] += bitangent[1]; + pVertex2->bitangent[2] += bitangent[2]; + } + + // Orthogonalize and normalize the vertex tangents. + for (int i = 0; i < totalVertices; ++i) + { + pVertex0 = &m_vertexBuffer[i]; + + // Gram-Schmidt orthogonalize tangent with normal. + + nDotT = pVertex0->normal[0] * pVertex0->tangent[0] + + pVertex0->normal[1] * pVertex0->tangent[1] + + pVertex0->normal[2] * pVertex0->tangent[2]; + + pVertex0->tangent[0] -= pVertex0->normal[0] * nDotT; + pVertex0->tangent[1] -= pVertex0->normal[1] * nDotT; + pVertex0->tangent[2] -= pVertex0->normal[2] * nDotT; + + // Normalize the tangent. + + length = 1.0f / sqrtf(pVertex0->tangent[0] * pVertex0->tangent[0] + + pVertex0->tangent[1] * pVertex0->tangent[1] + + pVertex0->tangent[2] * pVertex0->tangent[2]); + + pVertex0->tangent[0] *= length; + pVertex0->tangent[1] *= length; + pVertex0->tangent[2] *= length; + + // Calculate the handedness of the local tangent space. + // The bitangent vector is the cross product between the triangle face + // normal vector and the calculated tangent vector. The resulting + // bitangent vector should be the same as the bitangent vector + // calculated from the set of linear equations above. If they point in + // different directions then we need to invert the cross product + // calculated bitangent vector. We store this scalar multiplier in the + // tangent vector's 'w' component so that the correct bitangent vector + // can be generated in the normal mapping shader's vertex shader. + // + // Normal maps have a left handed coordinate system with the origin + // located at the top left of the normal map texture. The x coordinates + // run horizontally from left to right. The y coordinates run + // vertically from top to bottom. The z coordinates run out of the + // normal map texture towards the viewer. Our handedness calculations + // must take this fact into account as well so that the normal mapping + // shader's vertex shader will generate the correct bitangent vectors. + // Some normal map authoring tools such as Crazybump + // (http://www.crazybump.com/) includes options to allow you to control + // the orientation of the normal map normal's y-axis. + + bitangent[0] = (pVertex0->normal[1] * pVertex0->tangent[2]) - + (pVertex0->normal[2] * pVertex0->tangent[1]); + bitangent[1] = (pVertex0->normal[2] * pVertex0->tangent[0]) - + (pVertex0->normal[0] * pVertex0->tangent[2]); + bitangent[2] = (pVertex0->normal[0] * pVertex0->tangent[1]) - + (pVertex0->normal[1] * pVertex0->tangent[0]); + + bDotB = bitangent[0] * pVertex0->bitangent[0] + + bitangent[1] * pVertex0->bitangent[1] + + bitangent[2] * pVertex0->bitangent[2]; + + pVertex0->tangent[3] = (bDotB < 0.0f) ? 1.0f : -1.0f; + + pVertex0->bitangent[0] = bitangent[0]; + pVertex0->bitangent[1] = bitangent[1]; + pVertex0->bitangent[2] = bitangent[2]; + } + + m_hasTangents = true; +} + +void OBJModel::importGeometryFirstPass(FILE *pFile) +{ + m_hasTextureCoords = false; + m_hasNormals = false; + + m_numberOfVertexCoords = 0; + m_numberOfTextureCoords = 0; + m_numberOfNormals = 0; + m_numberOfTriangles = 0; + + int v = 0; + int vt = 0; + int vn = 0; + char buffer[256] = {0}; + std::string name; + + boost::filesystem::path file_path; + + while (fscanf(pFile, "%s", buffer) != EOF) + { + switch (buffer[0]) + { + case 'f': // v, v//vn, v/vt, v/vt/vn. + fscanf(pFile, "%s", buffer); + + if (strstr(buffer, "//")) // v//vn + { + sscanf(buffer, "%d//%d", &v, &vn); + fscanf(pFile, "%d//%d", &v, &vn); + fscanf(pFile, "%d//%d", &v, &vn); + ++m_numberOfTriangles; + + while (fscanf(pFile, "%d//%d", &v, &vn) > 0) + ++m_numberOfTriangles; + } + else if (sscanf(buffer, "%d/%d/%d", &v, &vt, &vn) == 3) // v/vt/vn + { + fscanf(pFile, "%d/%d/%d", &v, &vt, &vn); + fscanf(pFile, "%d/%d/%d", &v, &vt, &vn); + ++m_numberOfTriangles; + + while (fscanf(pFile, "%d/%d/%d", &v, &vt, &vn) > 0) + ++m_numberOfTriangles; + } + else if (sscanf(buffer, "%d/%d", &v, &vt) == 2) // v/vt + { + fscanf(pFile, "%d/%d", &v, &vt); + fscanf(pFile, "%d/%d", &v, &vt); + ++m_numberOfTriangles; + + while (fscanf(pFile, "%d/%d", &v, &vt) > 0) + ++m_numberOfTriangles; + } + else // v + { + fscanf(pFile, "%d", &v); + fscanf(pFile, "%d", &v); + ++m_numberOfTriangles; + + while (fscanf(pFile, "%d", &v) > 0) + ++m_numberOfTriangles; + } + break; + + case 'm': // mtllib + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + file_path = m_directoryPath; + file_path /= buffer; +// LogMessage ("full mtl path = %s", file_path.string().c_str()); + name = file_path.string(); + importMaterials(name.c_str()); + break; + + case 'v': // v, vt, or vn + switch (buffer[1]) + { + case '\0': + fgets(buffer, sizeof(buffer), pFile); + ++m_numberOfVertexCoords; + break; + + case 'n': + fgets(buffer, sizeof(buffer), pFile); + ++m_numberOfNormals; + break; + + case 't': + fgets(buffer, sizeof(buffer), pFile); + ++m_numberOfTextureCoords; + + default: + break; + } + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + } + + m_hasPositions = m_numberOfVertexCoords > 0; + m_hasNormals = m_numberOfNormals > 0; + m_hasTextureCoords = m_numberOfTextureCoords > 0; + + // Allocate memory for the OBJ model data. + m_vertexCoords.resize(m_numberOfVertexCoords * 3); + m_textureCoords.resize(m_numberOfTextureCoords * 2); + m_normals.resize(m_numberOfNormals * 3); + m_indexBuffer.resize(m_numberOfTriangles * 3); + m_attributeBuffer.resize(m_numberOfTriangles); + + // Define a default material if no materials were loaded. + if (m_numberOfMaterials == 0) + { + Material defaultMaterial = + { + {0.2f, 0.2f, 0.2f, 1.0f}, + {0.8f, 0.8f, 0.8f, 1.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}, + 0.0f, + 1.0f, + std::string("default"), + std::string(), + std::string() + }; + + m_materials.push_back(defaultMaterial); + m_materialCache[defaultMaterial.name] = 0; + } +} + +void OBJModel::importGeometrySecondPass(FILE *pFile) +{ + int v[3] = {0}; + int vt[3] = {0}; + int vn[3] = {0}; + int numVertices = 0; + int numTexCoords = 0; + int numNormals = 0; + int numTriangles = 0; + int activeMaterial = 0; + char buffer[256] = {0}; + std::string name; + std::map::const_iterator iter; + + while (fscanf(pFile, "%s", buffer) != EOF) + { + switch (buffer[0]) + { + case 'f': // v, v//vn, v/vt, or v/vt/vn. + v[0] = v[1] = v[2] = 0; + vt[0] = vt[1] = vt[2] = 0; + vn[0] = vn[1] = vn[2] = 0; + + fscanf(pFile, "%s", buffer); + + if (strstr(buffer, "//")) // v//vn + { + sscanf(buffer, "%d//%d", &v[0], &vn[0]); + fscanf(pFile, "%d//%d", &v[1], &vn[1]); + fscanf(pFile, "%d//%d", &v[2], &vn[2]); + + v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1; + v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1; + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + + vn[0] = (vn[0] < 0) ? vn[0] + numNormals - 1 : vn[0] - 1; + vn[1] = (vn[1] < 0) ? vn[1] + numNormals - 1 : vn[1] - 1; + vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1; + + addTrianglePosNormal(numTriangles++, activeMaterial, + v[0], v[1], v[2], vn[0], vn[1], vn[2]); + + v[1] = v[2]; + vn[1] = vn[2]; + + while (fscanf(pFile, "%d//%d", &v[2], &vn[2]) > 0) + { + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1; + + addTrianglePosNormal(numTriangles++, activeMaterial, + v[0], v[1], v[2], vn[0], vn[1], vn[2]); + + v[1] = v[2]; + vn[1] = vn[2]; + } + } + else if (sscanf(buffer, "%d/%d/%d", &v[0], &vt[0], &vn[0]) == 3) // v/vt/vn + { + fscanf(pFile, "%d/%d/%d", &v[1], &vt[1], &vn[1]); + fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]); + + v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1; + v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1; + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + + vt[0] = (vt[0] < 0) ? vt[0] + numTexCoords - 1 : vt[0] - 1; + vt[1] = (vt[1] < 0) ? vt[1] + numTexCoords - 1 : vt[1] - 1; + vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1; + + vn[0] = (vn[0] < 0) ? vn[0] + numNormals - 1 : vn[0] - 1; + vn[1] = (vn[1] < 0) ? vn[1] + numNormals - 1 : vn[1] - 1; + vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1; + + addTrianglePosTexCoordNormal(numTriangles++, activeMaterial, + v[0], v[1], v[2], vt[0], vt[1], vt[2], vn[0], vn[1], vn[2]); + + v[1] = v[2]; + vt[1] = vt[2]; + vn[1] = vn[2]; + + while (fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]) > 0) + { + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1; + vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1; + + addTrianglePosTexCoordNormal(numTriangles++, activeMaterial, + v[0], v[1], v[2], vt[0], vt[1], vt[2], vn[0], vn[1], vn[2]); + + v[1] = v[2]; + vt[1] = vt[2]; + vn[1] = vn[2]; + } + } + else if (sscanf(buffer, "%d/%d", &v[0], &vt[0]) == 2) // v/vt + { + fscanf(pFile, "%d/%d", &v[1], &vt[1]); + fscanf(pFile, "%d/%d", &v[2], &vt[2]); + + v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1; + v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1; + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + + vt[0] = (vt[0] < 0) ? vt[0] + numTexCoords - 1 : vt[0] - 1; + vt[1] = (vt[1] < 0) ? vt[1] + numTexCoords - 1 : vt[1] - 1; + vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1; + + addTrianglePosTexCoord(numTriangles++, activeMaterial, + v[0], v[1], v[2], vt[0], vt[1], vt[2]); + + v[1] = v[2]; + vt[1] = vt[2]; + + while (fscanf(pFile, "%d/%d", &v[2], &vt[2]) > 0) + { + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1; + + addTrianglePosTexCoord(numTriangles++, activeMaterial, + v[0], v[1], v[2], vt[0], vt[1], vt[2]); + + v[1] = v[2]; + vt[1] = vt[2]; + } + } + else // v + { + sscanf(buffer, "%d", &v[0]); + fscanf(pFile, "%d", &v[1]); + fscanf(pFile, "%d", &v[2]); + + v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1; + v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1; + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + + addTrianglePos(numTriangles++, activeMaterial, v[0], v[1], v[2]); + + v[1] = v[2]; + + while (fscanf(pFile, "%d", &v[2]) > 0) + { + v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1; + + addTrianglePos(numTriangles++, activeMaterial, v[0], v[1], v[2]); + + v[1] = v[2]; + } + } + break; + + case 'u': // usemtl + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + name = buffer; + iter = m_materialCache.find(buffer); + activeMaterial = (iter == m_materialCache.end()) ? 0 : iter->second; + break; + + case 'v': // v, vn, or vt. + switch (buffer[1]) + { + case '\0': // v + fscanf(pFile, "%f %f %f", + &m_vertexCoords[3 * numVertices], + &m_vertexCoords[3 * numVertices + 1], + &m_vertexCoords[3 * numVertices + 2]); + ++numVertices; + break; + + case 'n': // vn + fscanf(pFile, "%f %f %f", + &m_normals[3 * numNormals], + &m_normals[3 * numNormals + 1], + &m_normals[3 * numNormals + 2]); + ++numNormals; + break; + + case 't': // vt + fscanf(pFile, "%f %f", + &m_textureCoords[2 * numTexCoords], + &m_textureCoords[2 * numTexCoords + 1]); + ++numTexCoords; + break; + + default: + break; + } + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + } +} + +bool OBJModel::importMaterials(const char *pszFilename) +{ + LogMessage ("Loading material %s", pszFilename); + FILE *pFile = fopen(pszFilename, "r"); + + if (!pFile) + return false; + + Material *pMaterial = 0; + int illum = 0; + int numMaterials = 0; + char buffer[256] = {0}; + + // Count the number of materials in the MTL file. + while (fscanf(pFile, "%s", buffer) != EOF) + { + switch (buffer[0]) + { + case 'n': // newmtl + ++numMaterials; + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + } + + rewind(pFile); + + m_numberOfMaterials = numMaterials; + + LogMessage ("file contains %d materials", m_numberOfMaterials); + + numMaterials = 0; + m_materials.resize(m_numberOfMaterials); + boost::filesystem::path file_path; + + // Load the materials in the MTL file. + while (fscanf(pFile, "%s", buffer) != EOF) + { + switch (buffer[0]) + { + case 'N': // Ns + fscanf(pFile, "%f", &pMaterial->shininess); + + // Wavefront .MTL file shininess is from [0,1000]. + // Scale back to a generic [0,1] range. + pMaterial->shininess /= 1000.0f; + break; + + case 'K': // Ka, Kd, or Ks + switch (buffer[1]) + { + case 'a': // Ka + fscanf(pFile, "%f %f %f", + &pMaterial->ambient[0], + &pMaterial->ambient[1], + &pMaterial->ambient[2]); + pMaterial->ambient[3] = 1.0f; + break; + + case 'd': // Kd + fscanf(pFile, "%f %f %f", + &pMaterial->diffuse[0], + &pMaterial->diffuse[1], + &pMaterial->diffuse[2]); + pMaterial->diffuse[3] = 1.0f; + break; + + case 's': // Ks + fscanf(pFile, "%f %f %f", + &pMaterial->specular[0], + &pMaterial->specular[1], + &pMaterial->specular[2]); + pMaterial->specular[3] = 1.0f; + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + break; + + case 'T': // Tr + switch (buffer[1]) + { + case 'r': // Tr + fscanf(pFile, "%f", &pMaterial->alpha); + pMaterial->alpha = 1.0f - pMaterial->alpha; + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + break; + + case 'd': + fscanf(pFile, "%f", &pMaterial->alpha); + break; + + case 'i': // illum + fscanf(pFile, "%d", &illum); + + if (illum == 1) + { + pMaterial->specular[0] = 0.0f; + pMaterial->specular[1] = 0.0f; + pMaterial->specular[2] = 0.0f; + pMaterial->specular[3] = 1.0f; + } + break; + + case 'm': // map_Kd, map_bump + if (strstr(buffer, "map_Kd") != 0) + { + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + file_path = m_directoryPath; + file_path /= buffer; + pMaterial->colorMapFilename = file_path.string(); + LogMessage ("colorMapFilename = %s", pMaterial->colorMapFilename.c_str()); + } + else if (strstr(buffer, "map_bump") != 0) + { + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + file_path = m_directoryPath; + file_path /= buffer; + pMaterial->bumpMapFilename = file_path.string(); + LogMessage ("bumpMapFilename = %s", pMaterial->bumpMapFilename.c_str()); + } + else + { + fgets(buffer, sizeof(buffer), pFile); + } + break; + + case 'n': // newmtl + fgets(buffer, sizeof(buffer), pFile); + sscanf(buffer, "%s %s", buffer, buffer); + + pMaterial = &m_materials[numMaterials]; + pMaterial->ambient[0] = 0.2f; + pMaterial->ambient[1] = 0.2f; + pMaterial->ambient[2] = 0.2f; + pMaterial->ambient[3] = 1.0f; + pMaterial->diffuse[0] = 0.8f; + pMaterial->diffuse[1] = 0.8f; + pMaterial->diffuse[2] = 0.8f; + pMaterial->diffuse[3] = 1.0f; + pMaterial->specular[0] = 0.0f; + pMaterial->specular[1] = 0.0f; + pMaterial->specular[2] = 0.0f; + pMaterial->specular[3] = 1.0f; + pMaterial->shininess = 0.0f; + pMaterial->alpha = 1.0f; + pMaterial->name = buffer; + pMaterial->colorMapFilename.clear(); + pMaterial->bumpMapFilename.clear(); + + m_materialCache[pMaterial->name] = numMaterials; + ++numMaterials; + break; + + default: + fgets(buffer, sizeof(buffer), pFile); + break; + } + } + + fclose(pFile); + return true; +} + +} diff --git a/engine/OBJModel.h b/engine/OBJModel.h new file mode 100644 index 0000000..0d32534 --- /dev/null +++ b/engine/OBJModel.h @@ -0,0 +1,244 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2007 dhpoware. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OBJMODEL_H +#define _OBJMODEL_H + +#include +#include +#include + +//----------------------------------------------------------------------------- +// Alias|Wavefront OBJ file loader. +// +// This OBJ file loader contains the following restrictions: +// 1. Group information is ignored. Faces are grouped based on the material +// that each face uses. +// 2. Object information is ignored. This loader will merge everything into a +// single object. +// 3. The MTL file must be located in the same directory as the OBJ file. If +// it isn't then the MTL file will fail to load and a default material is +// used instead. +// 4. This loader triangulates all polygonal faces during importing. +//----------------------------------------------------------------------------- + +namespace Engine { + + class OBJModel + { + public: + struct Material + { + float ambient[4]; + float diffuse[4]; + float specular[4]; + float shininess; // [0 = min shininess, 1 = max shininess] + float alpha; // [0 = fully transparent, 1 = fully opaque] + + std::string name; + std::string colorMapFilename; + std::string bumpMapFilename; + }; + + struct Vertex + { + float position[3]; + float texCoord[2]; + float normal[3]; + float tangent[4]; + float bitangent[3]; + }; + + struct Mesh + { + int startIndex; + int triangleCount; + const Material *pMaterial; + }; + + OBJModel(); + ~OBJModel(); + + void destroy(); + bool import(const char *pszFilename, bool rebuildNormals = false); + void normalize(float scaleTo = 1.0f, bool center = true); + void reverseWinding(); + + // Getter methods. + + void getCenter(float &x, float &y, float &z) const; + float getWidth() const; + float getHeight() const; + float getLength() const; + float getRadius() const; + + const int *getIndexBuffer() const; + int getIndexSize() const; + + const Material &getMaterial(int i) const; + const Mesh &getMesh(int i) const; + + int getNumberOfIndices() const; + int getNumberOfMaterials() const; + int getNumberOfMeshes() const; + int getNumberOfTriangles() const; + int getNumberOfVertices() const; + + const std::string &getPath() const; + + const Vertex &getVertex(int i) const; + const Vertex *getVertexBuffer() const; + int getVertexSize() const; + + bool hasNormals() const; + bool hasPositions() const; + bool hasTangents() const; + bool hasTextureCoords() const; + + private: + void addTrianglePos(int index, int material, + int v0, int v1, int v2); + void addTrianglePosNormal(int index, int material, + int v0, int v1, int v2, + int vn0, int vn1, int vn2); + void addTrianglePosTexCoord(int index, int material, + int v0, int v1, int v2, + int vt0, int vt1, int vt2); + void addTrianglePosTexCoordNormal(int index, int material, + int v0, int v1, int v2, + int vt0, int vt1, int vt2, + int vn0, int vn1, int vn2); + int addVertex(int hash, const Vertex *pVertex); + void bounds(float center[3], float &width, float &height, + float &length, float &radius) const; + void buildMeshes(); + void generateNormals(); + void generateTangents(); + void importGeometryFirstPass(FILE *pFile); + void importGeometrySecondPass(FILE *pFile); + bool importMaterials(const char *pszFilename); + void scale(float scaleFactor, float offset[3]); + + bool m_hasPositions; + bool m_hasTextureCoords; + bool m_hasNormals; + bool m_hasTangents; + + int m_numberOfVertexCoords; + int m_numberOfTextureCoords; + int m_numberOfNormals; + int m_numberOfTriangles; + int m_numberOfMaterials; + int m_numberOfMeshes; + + float m_center[3]; + float m_width; + float m_height; + float m_length; + float m_radius; + + std::string m_directoryPath; + + std::vector m_meshes; + std::vector m_materials; + std::vector m_vertexBuffer; + std::vector m_indexBuffer; + std::vector m_attributeBuffer; + std::vector m_vertexCoords; + std::vector m_textureCoords; + std::vector m_normals; + + std::map m_materialCache; + std::map > m_vertexCache; + }; + + //----------------------------------------------------------------------------- + + inline void OBJModel::getCenter(float &x, float &y, float &z) const + { x = m_center[0]; y = m_center[1]; z = m_center[2]; } + + inline float OBJModel::getWidth() const + { return m_width; } + + inline float OBJModel::getHeight() const + { return m_height; } + + inline float OBJModel::getLength() const + { return m_length; } + + inline float OBJModel::getRadius() const + { return m_radius; } + + inline const int *OBJModel::getIndexBuffer() const + { return &m_indexBuffer[0]; } + + inline int OBJModel::getIndexSize() const + { return static_cast(sizeof(int)); } + + inline const OBJModel::Material &OBJModel::getMaterial(int i) const + { return m_materials[i]; } + + inline const OBJModel::Mesh &OBJModel::getMesh(int i) const + { return m_meshes[i]; } + + inline int OBJModel::getNumberOfIndices() const + { return m_numberOfTriangles * 3; } + + inline int OBJModel::getNumberOfMaterials() const + { return m_numberOfMaterials; } + + inline int OBJModel::getNumberOfMeshes() const + { return m_numberOfMeshes; } + + inline int OBJModel::getNumberOfTriangles() const + { return m_numberOfTriangles; } + + inline int OBJModel::getNumberOfVertices() const + { return static_cast(m_vertexBuffer.size()); } + + inline const std::string &OBJModel::getPath() const + { return m_directoryPath; } + + inline const OBJModel::Vertex &OBJModel::getVertex(int i) const + { return m_vertexBuffer[i]; } + + inline const OBJModel::Vertex *OBJModel::getVertexBuffer() const + { return &m_vertexBuffer[0]; } + + inline int OBJModel::getVertexSize() const + { return static_cast(sizeof(Vertex)); } + + inline bool OBJModel::hasNormals() const + { return m_hasNormals; } + + inline bool OBJModel::hasPositions() const + { return m_hasPositions; } + + inline bool OBJModel::hasTangents() const + { return m_hasTangents; } + + inline bool OBJModel::hasTextureCoords() const + { return m_hasTextureCoords; } + +} + +#endif diff --git a/engine/ViewBase.cc b/engine/ViewBase.cc index 66e038d..ec9ca4d 100644 --- a/engine/ViewBase.cc +++ b/engine/ViewBase.cc @@ -11,6 +11,8 @@ #include #include +#include "OBJModel.h" + #include "DrawingsGL.h" using namespace std; @@ -103,16 +105,14 @@ int ViewBase::OnInit (int argc, char* argv[]) { else mUseShaders = false; - /* // load the shaders if possible if (mUseShaders) { - mBlinnPhongShader = LoadShaderProgram ("./data/shaders/blinn_phong.glsl"); - mNormalMappingShader = LoadShaderProgram ("./data/shaders/normal_mapping.glsl"); + mBlinnPhongShader = LoadShaderProgram (GetResourceFullPath("/data/shaders/blinn_phong.glsl")); + mNormalMappingShader = LoadShaderProgram (GetResourceFullPath("/data/shaders/normal_mapping.glsl")); } else { mBlinnPhongShader = 0; mNormalMappingShader = 0; } - */ // Create a null texture that can be used for objects without textures // taken from http://www.dhpoware.com/demos/glslNormalMapping.html @@ -468,6 +468,501 @@ GLuint ViewBase::LoadTextureFromPNG (const std::string &filename) { return temp_sprite.GetGLTextureName(); } +/* + * OBJModel loading and drawing + */ + + +OBJModelPtr ViewBase::LoadOBJModel (const std::string &model_filename) { + LogDebug ("Loading OBJ model %s", model_filename.c_str()); + + OBJModelPtr result_model (new OBJModel); + + if(!result_model->import (model_filename.c_str(), true)) { + LogError("Could not load model %s", model_filename.c_str()); + return result_model; + } + + result_model->normalize(); + result_model->reverseWinding(); + + // Load any associated textures. + // Note the path where the textures are assumed to be located. + + const OBJModel::Material *pMaterial = 0; + GLuint textureId = 0; + std::string::size_type offset = 0; + std::string filename; + + for (int i = 0; i < result_model->getNumberOfMaterials(); ++i) + { + pMaterial = &result_model->getMaterial(i); + + // Look for and load any diffuse color map textures. + if (!pMaterial->colorMapFilename.empty()) { + // Try load the texture using the path in the .MTL file. + textureId = LoadTextureFromPNG(pMaterial->colorMapFilename.c_str()); + + if (!textureId) + { + offset = pMaterial->colorMapFilename.find_last_of('\\'); + + if (offset != std::string::npos) + filename = pMaterial->colorMapFilename.substr(++offset); + else + filename = pMaterial->colorMapFilename; + + // Try loading the texture from the same directory as the OBJ file. + textureId = LoadTextureFromPNG((filename).c_str()); + } + } else { + LogMessage ("No diffuse color map found!"); + } + + // Look for and load any normal map textures. + if (!pMaterial->bumpMapFilename.empty()) { + + // Try load the texture using the path in the .MTL file. + textureId = LoadTextureFromPNG((pMaterial->bumpMapFilename).c_str()); + + if (!textureId) + { + offset = pMaterial->bumpMapFilename.find_last_of('\\'); + + if (offset != std::string::npos) + filename = pMaterial->bumpMapFilename.substr(++offset); + else + filename = pMaterial->bumpMapFilename; + + // Try loading the texture from the same directory as the OBJ file. + textureId = LoadTextureFromPNG((result_model->getPath() + filename).c_str()); + } + } else { + LogMessage ("Material has no bumpmap!"); + continue; + } + + } + + // Update the window caption. + + LogMessage ("Loaded model %s successful, dimensions (whl): %f, %f, %f meshes %d vertices %d triangles: %d", + model_filename.c_str(), + result_model->getWidth(), + result_model->getHeight(), + result_model->getLength(), + result_model->getNumberOfMeshes(), + result_model->getNumberOfVertices(), + result_model->getNumberOfTriangles() + ); + + return result_model; +} + +void ViewBase::DrawOBJModel (OBJModelPtr obj_model) { + if (mUseShaders) { + DrawOBJModelShaded(obj_model); + } else { + DrawOBJModelSolid (obj_model); + } +} + +void ViewBase::DrawOBJModelSolid (OBJModelPtr obj_model) { + const OBJModel::Mesh *pMesh = 0; + const OBJModel::Material *pMaterial = 0; + const OBJModel::Vertex *pVertices = 0; + std::map::const_iterator iter; + + for (int i = 0; i < obj_model->getNumberOfMeshes(); ++i) + { + pMesh = &obj_model->getMesh(i); + pMaterial = pMesh->pMaterial; + pVertices = obj_model->getVertexBuffer(); + + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular); + + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f); + + if (pMaterial->colorMapFilename.size() > 0) + { + iter = mGLTextures.find(pMaterial->colorMapFilename); + + if (iter == mGLTextures.end()) + { + LogError ("Could not find required colormap '%s'", pMaterial->colorMapFilename.c_str()); + glDisable(GL_TEXTURE_2D); + } + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, iter->second); + } + } + else + { + glDisable(GL_TEXTURE_2D); + } + + if (obj_model->hasPositions()) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->position); + } + + if (obj_model->hasTextureCoords()) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->texCoord); + } + + if (obj_model->hasNormals()) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->normal); + } + + glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT, + obj_model->getIndexBuffer() + pMesh->startIndex); + + if (obj_model->hasNormals()) + glDisableClientState(GL_NORMAL_ARRAY); + + if (obj_model->hasTextureCoords()) + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + if (obj_model->hasPositions()) + glDisableClientState(GL_VERTEX_ARRAY); + + // disable texture drawing if active + if (pMaterial->colorMapFilename.size() > 0) + glDisable(GL_TEXTURE_2D); + } +} + +void ViewBase::DrawOBJModelShaded (OBJModelPtr obj_model) { + const OBJModel::Mesh *pMesh = 0; + const OBJModel::Material *pMaterial = 0; + const OBJModel::Vertex *pVertices = 0; + std::map::const_iterator iter; + GLuint texture = 0; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for (int i = 0; i < obj_model->getNumberOfMeshes(); ++i) + { + pMesh = &obj_model->getMesh(i); + pMaterial = pMesh->pMaterial; + pVertices = obj_model->getVertexBuffer(); + + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f); + + // if there is no bumpmap we will simply use a blinn phong shader and draw + // the color map onto our model + if (pMaterial->bumpMapFilename.empty()) + { + // LogMessage ("using Blinn Phong shader"); + // Per fragment Blinn-Phong code path. + glUseProgram(mBlinnPhongShader); + + // Bind the color map texture. + + texture = mNullTexture; + + if (pMaterial->colorMapFilename.size() > 0) { + iter = mGLTextures.find(pMaterial->colorMapFilename); + + if (iter != mGLTextures.end()) + texture = iter->second; + + } else { + LogMessage ("Disabling textures"); + } + + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + // Update shader parameters. + assert (glIsProgram (mBlinnPhongShader) == GL_TRUE); + assert (glGetUniformLocation(mBlinnPhongShader, "materialAlpha") != -1); + assert (glGetUniformLocation(mBlinnPhongShader, "colorMap") != -1); + glUniform1i(glGetUniformLocation( + mBlinnPhongShader, "colorMap"), 0); + glUniform1f(glGetUniformLocation( + mBlinnPhongShader, "materialAlpha"), pMaterial->alpha); + } + else + { + // if there is a bumpmap we use both the bump map and the color map and + // apply it on our model + + // LogMessage ("using Normal Mapping Shader"); + // Normal mapping code path. + + glUseProgram(mNormalMappingShader); + + // Bind the normal map texture. + + iter = mGLTextures.find(pMaterial->bumpMapFilename); + + if (iter != mGLTextures.end()) { + texture = iter->second; + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + } else { + LogMessage ("bumpmap %s not found", pMaterial->bumpMapFilename.c_str()); + } + + // Bind the color map texture. + + texture = mNullTexture; + + if (pMaterial->colorMapFilename.size() > 0) + { + iter = mGLTextures.find(pMaterial->colorMapFilename); + + if (iter != mGLTextures.end()) { + texture = iter->second; + } else { + LogMessage ("color map %s not found", pMaterial->colorMapFilename.c_str()); + } + } + + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + // Update shader parameters. + assert (glGetUniformLocation(mNormalMappingShader, "colorMap") != -1); + assert (glGetUniformLocation(mNormalMappingShader, "normalMap") != -1); + assert (glGetUniformLocation(mNormalMappingShader, "materialAlpha") != -1); + + glUniform1i(glGetUniformLocation( + mNormalMappingShader, "colorMap"), 0); + glUniform1i(glGetUniformLocation( + mNormalMappingShader, "normalMap"), 1); + glUniform1f(glGetUniformLocation( + mNormalMappingShader, "materialAlpha"), pMaterial->alpha); + } + + // Render mesh. + + if (obj_model->hasPositions()) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->position); + } + + if (obj_model->hasTextureCoords()) + { + glClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->texCoord); + } + + if (obj_model->hasNormals()) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->normal); + } + + if (obj_model->hasTangents()) + { + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(4, GL_FLOAT, obj_model->getVertexSize(), + obj_model->getVertexBuffer()->tangent); + } + + glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT, + obj_model->getIndexBuffer() + pMesh->startIndex); + + if (obj_model->hasTangents()) + { + glClientActiveTexture(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (obj_model->hasNormals()) + glDisableClientState(GL_NORMAL_ARRAY); + + if (obj_model->hasTextureCoords()) + { + glClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (obj_model->hasPositions()) + glDisableClientState(GL_VERTEX_ARRAY); + } + + // as we might be using multiple textures (at least for the normal mapping + // case) we have to disable both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + + glUseProgram(0); + glDisable(GL_BLEND); + + // disable texture drawing +// glDisable(GL_TEXTURE_2D); +} + +/* + * Shader loading, compiling, linking + */ +/** Compiles a shader program + * + * This function contains code mainly taken from dhpowares excellent + * objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ). + */ +GLuint ViewBase::CompileShader (GLenum type, const GLchar *source, GLint length) { + GLuint shader = glCreateShader(type); + + if (shader) { + GLint compiled = 0; + + glShaderSource (shader, 1, &source, &length); + glCompileShader(shader); + glGetShaderiv (shader, GL_COMPILE_STATUS, &compiled); + + if (!compiled) { + GLsizei info_log_size = 0; + std::string info_log; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_size); + info_log.resize (info_log_size); + glGetShaderInfoLog (shader, info_log_size, &info_log_size, &info_log[0]); + + LogError ("Error compiling shader: %s", info_log.c_str()); + } + } + + return shader; +} + +/** Links the given shader programs + * + * This function contains code mainly taken from dhpowares excellent + * objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ). + */ +GLuint ViewBase::LinkShaders (GLuint vertex_shader, GLuint fragment_shader) { + GLuint program = glCreateProgram(); + + if (program) { + GLint linked = 0; + + if (vertex_shader) + glAttachShader (program, vertex_shader); + + if (fragment_shader) + glAttachShader (program, fragment_shader); + + glLinkProgram (program); + glGetProgramiv (program, GL_LINK_STATUS, &linked); + + if (!linked) { + GLsizei info_log_size = 0; + std::string info_log; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_size); + info_log.resize (info_log_size); + glGetProgramInfoLog (program, info_log_size, &info_log_size, &info_log[0]); + + LogError ("Error linking shaders vert: %d, frag: %d", vertex_shader, fragment_shader); + } + + if (vertex_shader) + glDeleteShader (vertex_shader); + + if (fragment_shader) + glDeleteShader (fragment_shader); + } + + return program; +} + + +/** Loads a vertex or fragment shader program + * + * This function contains code mainly taken from dhpowares excellent + * objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ). + */ +GLuint ViewBase::LoadShaderProgram (const std::string &filename) { + LogDebug ("Loading shader program %s", filename.c_str()); + + std::ifstream program_file (filename.c_str()); + if (!program_file) { + LogError ("Could not open file '%s' while loading shader program", filename.c_str()); + } + + // read the whole file into a string + std::string shader_program ((std::istreambuf_iterator(program_file)), std::istreambuf_iterator()); + + program_file.close(); + + GLuint program = 0; + + if (shader_program.size() > 0) { + const GLchar *source; + GLint length = 0; + GLuint vertex_shader = 0; + GLuint fragment_shader = 0; + + std::string::size_type vertex_shader_offset = shader_program.find("[vert]"); + std::string::size_type fragment_shader_offset = shader_program.find("[frag]"); + + if (vertex_shader_offset != std::string::npos) { + // skip over the [vert] tag + vertex_shader_offset += 6; + source = reinterpret_cast (&shader_program[vertex_shader_offset]); + length = static_cast(fragment_shader_offset - vertex_shader_offset); + vertex_shader = CompileShader (GL_VERTEX_SHADER, source, length); + + LogMessage ("Compiled vertex shader with id %d", vertex_shader); + } + + if (fragment_shader_offset != std::string::npos) { + // skip over the [vert] tag + fragment_shader_offset += 6; + source = reinterpret_cast (&shader_program[fragment_shader_offset]); + length = static_cast(shader_program.length() - fragment_shader_offset - 1); + fragment_shader = CompileShader (GL_FRAGMENT_SHADER, source, length); + + LogMessage ("Compiled fragment shader with id %d", fragment_shader); + } + + program = LinkShaders (vertex_shader, fragment_shader); + + LogMessage ("Successfully linked shaders vert: %d frag: %d from file %s into program %d", vertex_shader, fragment_shader, filename.c_str(), program); + } + + mShaderPrograms[filename] = program; + + return program; +} + +/* + * Camera and other auxillary functions + */ + void ViewBase::GetCamereEye (float *eye_out) { assert (mCamera); mCamera->GetEye (eye_out); diff --git a/engine/ViewBase.h b/engine/ViewBase.h index 1217fa1..7d9edd6 100644 --- a/engine/ViewBase.h +++ b/engine/ViewBase.h @@ -16,6 +16,9 @@ namespace Engine { class Module; class ModelBase; class CameraBase; +class OBJModel; + +typedef boost::shared_ptr OBJModelPtr; /** \brief Performs the actual drawing based on Camera and Model */ @@ -134,9 +137,21 @@ class ViewBase : public Module{ /** \brief All loaded textures */ std::map mGLTextures; + OBJModelPtr LoadOBJModel (const std::string &model_filename); + void DrawOBJModel (OBJModelPtr mesh); + void DrawOBJModelSolid (OBJModelPtr mesh); + void DrawOBJModelShaded (OBJModelPtr mesh); + std::map mOBJModeles; + /** \brief Whether we can use shader programs */ bool mUseShaders; + GLuint CompileShader (GLenum type, const GLchar *source, GLint length); + GLuint LinkShaders (GLuint vertex_shader, GLuint fragment_shader); + GLuint LoadShaderProgram (const std::string &filename); + + std::map mShaderPrograms; + GLuint mBlinnPhongShader; GLuint mNormalMappingShader;