diff --git a/KREngine/KREngine.xcodeproj/project.pbxproj b/KREngine/KREngine.xcodeproj/project.pbxproj index 8829290..827e787 100644 --- a/KREngine/KREngine.xcodeproj/project.pbxproj +++ b/KREngine/KREngine.xcodeproj/project.pbxproj @@ -190,6 +190,10 @@ E4C454B3167BC04C003586CD /* KRModelSphere.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C454B1167BC04B003586CD /* KRModelSphere.h */; }; E4C454B5167BC05C003586CD /* KRModelSphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C454B4167BC05C003586CD /* KRModelSphere.cpp */; }; E4C454B6167BC05C003586CD /* KRModelSphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C454B4167BC05C003586CD /* KRModelSphere.cpp */; }; + E4C454B8167BD236003586CD /* KRHitInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C454B7167BD235003586CD /* KRHitInfo.h */; }; + E4C454B9167BD236003586CD /* KRHitInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C454B7167BD235003586CD /* KRHitInfo.h */; }; + E4C454BB167BD248003586CD /* KRHitInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C454BA167BD248003586CD /* KRHitInfo.cpp */; }; + E4C454BC167BD248003586CD /* KRHitInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C454BA167BD248003586CD /* KRHitInfo.cpp */; }; E4CA10E51637BD0A005D9400 /* KRTexturePVR.h in Headers */ = {isa = PBXBuildFile; fileRef = E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */; }; E4CA10E61637BD0A005D9400 /* KRTexturePVR.h in Headers */ = {isa = PBXBuildFile; fileRef = E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */; settings = {ATTRIBUTES = (Public, ); }; }; E4CA10E91637BD2B005D9400 /* KRTexturePVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4CA10E81637BD2B005D9400 /* KRTexturePVR.cpp */; }; @@ -389,6 +393,8 @@ E4C454AE167BB8FC003586CD /* KRModelCube.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KRModelCube.cpp; path = Classes/KRModelCube.cpp; sourceTree = ""; }; E4C454B1167BC04B003586CD /* KRModelSphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KRModelSphere.h; path = Classes/KRModelSphere.h; sourceTree = ""; }; E4C454B4167BC05C003586CD /* KRModelSphere.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KRModelSphere.cpp; path = Classes/KRModelSphere.cpp; sourceTree = ""; }; + E4C454B7167BD235003586CD /* KRHitInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KRHitInfo.h; path = Classes/KRHitInfo.h; sourceTree = ""; }; + E4C454BA167BD248003586CD /* KRHitInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KRHitInfo.cpp; path = Classes/KRHitInfo.cpp; sourceTree = ""; }; E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KRTexturePVR.h; path = Classes/KRTexturePVR.h; sourceTree = ""; }; E4CA10E81637BD2B005D9400 /* KRTexturePVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KRTexturePVR.cpp; path = Classes/KRTexturePVR.cpp; sourceTree = ""; }; E4CA10EB1637BD47005D9400 /* KRTextureTGA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KRTextureTGA.h; path = Classes/KRTextureTGA.h; sourceTree = ""; }; @@ -753,6 +759,8 @@ E4030E4B160A3CF000592648 /* KRStockGeometry.h */, E4CA11731639CBD1005D9400 /* KRViewport.h */, E4CA11771639CC8E005D9400 /* KRViewport.cpp */, + E4C454B7167BD235003586CD /* KRHitInfo.h */, + E4C454BA167BD248003586CD /* KRHitInfo.cpp */, ); name = Classes; sourceTree = ""; @@ -867,6 +875,7 @@ 104A335F1672D31C001C8BA6 /* KRCollider.h in Headers */, E4C454AC167BB8EC003586CD /* KRModelCube.h in Headers */, E4C454B2167BC04C003586CD /* KRModelSphere.h in Headers */, + E4C454B8167BD236003586CD /* KRHitInfo.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -929,6 +938,7 @@ E4324BAC16444DEF0043185B /* KRParticleSystemNewtonian.h in Headers */, E4C454AD167BB8EC003586CD /* KRModelCube.h in Headers */, E4C454B3167BC04C003586CD /* KRModelSphere.h in Headers */, + E4C454B9167BD236003586CD /* KRHitInfo.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1066,6 +1076,7 @@ 104A335E1672D31C001C8BA6 /* KRCollider.cpp in Sources */, E4C454AF167BB8FC003586CD /* KRModelCube.cpp in Sources */, E4C454B5167BC05C003586CD /* KRModelSphere.cpp in Sources */, + E4C454BB167BD248003586CD /* KRHitInfo.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1126,6 +1137,7 @@ E480BE6D1671C653004EC8AD /* KRBone.cpp in Sources */, E4C454B0167BB8FC003586CD /* KRModelCube.cpp in Sources */, E4C454B6167BC05C003586CD /* KRModelSphere.cpp in Sources */, + E4C454BC167BD248003586CD /* KRHitInfo.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/KREngine/KREngine/Classes/KRHitInfo.cpp b/KREngine/KREngine/Classes/KRHitInfo.cpp new file mode 100644 index 0000000..d383c3d --- /dev/null +++ b/KREngine/KREngine/Classes/KRHitInfo.cpp @@ -0,0 +1,86 @@ +// +// KRHitInfo.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRHitInfo.h" + +KRHitInfo::KRHitInfo() +{ + m_position = KRVector3::Zero(); + m_normal = KRVector3::Zero(); + m_node = NULL; +} + +KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node) +{ + m_position = position; + m_normal = normal; + m_node = node; +} + +KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal) +{ + m_position = position; + m_normal = normal; + m_node = NULL; +} + +KRHitInfo::~KRHitInfo() +{ + +} + +bool KRHitInfo::didHit() const +{ + return m_normal == KRVector3::Zero(); +} + +KRVector3 KRHitInfo::getPosition() const +{ + return m_position; +} + +KRVector3 KRHitInfo::getNormal() const +{ + return m_normal; +} + +KRNode *KRHitInfo::getNode() const +{ + return m_node; +} + +KRHitInfo& KRHitInfo::operator =(const KRHitInfo& b) +{ + m_position = b.m_position; + m_normal = b.m_normal; + m_node = b.m_node; + return *this; +} diff --git a/KREngine/KREngine/Classes/KRHitInfo.h b/KREngine/KREngine/Classes/KRHitInfo.h new file mode 100644 index 0000000..d3e3a14 --- /dev/null +++ b/KREngine/KREngine/Classes/KRHitInfo.h @@ -0,0 +1,59 @@ +// +// KRHitInfo.h +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#ifndef KRHITINFO_H +#define KRHITINFO_H + +#import "KRVector3.h" +#import "KRNode.h" + +class KRHitInfo { +public: + KRHitInfo(); + KRHitInfo(const KRVector3 &position, const KRVector3 &normal); + KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node); + ~KRHitInfo(); + + KRVector3 getPosition() const; + KRVector3 getNormal() const; + KRNode *getNode() const; + bool didHit() const; + + KRHitInfo& operator =(const KRHitInfo& b); + + +private: + KRNode *m_node; + KRVector3 m_position; + KRVector3 m_normal; +}; + +#endif diff --git a/KREngine/KREngine/Classes/KRModel.cpp b/KREngine/KREngine/Classes/KRModel.cpp index bd23f1c..b711caa 100644 --- a/KREngine/KREngine/Classes/KRModel.cpp +++ b/KREngine/KREngine/Classes/KRModel.cpp @@ -510,7 +510,7 @@ unsigned char *KRModel::getVertexData() const { return ((unsigned char *)m_pData->getStart()) + sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count; } -KRModel::pack_material *KRModel::getSubmesh(int mesh_index) +KRModel::pack_material *KRModel::getSubmesh(int mesh_index) const { return (pack_material *)((unsigned char *)m_pData->getStart() + sizeof(pack_header)) + mesh_index; } @@ -520,13 +520,13 @@ unsigned char *KRModel::getVertexData(int index) const return getVertexData() + m_vertex_size * index; } -int KRModel::getSubmeshCount() +int KRModel::getSubmeshCount() const { pack_header *header = getHeader(); return header->submesh_count; } -int KRModel::getVertexCount(int submesh) +int KRModel::getVertexCount(int submesh) const { return getSubmesh(submesh)->vertex_count; } @@ -708,7 +708,118 @@ void KRModel::optimize() // TODO - Add algorithm to convert model to indexed vertices, identying vertexes with identical attributes and optimizing order of trianges for best usage post-vertex-transform cache on GPU } -KRModel::model_format_t KRModel::getModelFormat() +KRModel::model_format_t KRModel::getModelFormat() const { return (model_format_t)getHeader()->model_format; } + +bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo) +{ + // algorithm based on Dan Sunday's implementation at http://geomalgorithms.com/a06-_intersect-2.html + const float SMALL_NUM = 0.00000001; // anything that avoids division overflow + KRVector3 u, v, n; // triangle vectors + KRVector3 dir, w0, w; // ray vectors + float r, a, b; // params to calc ray-plane intersect + + // get triangle edge vectors and plane normal + u = tri_v1 - tri_v0; + v = tri_v2 - tri_v0; + n = KRVector3::Cross(u, v); // cross product + if (n == KRVector3::Zero()) // triangle is degenerate + return false; // do not deal with this case + + dir = line_v1 - line_v0; // ray direction vector + w0 = line_v0 - tri_v0; + a = -KRVector3::Dot(n, w0); + b = KRVector3::Dot(n,dir); + if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane + if (a == 0) + return false; // ray lies in triangle plane + else { + return false; // ray disjoint from plane + } + } + + // get intersect point of ray with triangle plane + r = a / b; + if (r < 0.0) // ray goes away from triangle + return false; // => no intersect + // for a segment, also test if (r > 1.0) => no intersect + + + KRVector3 hit_point = line_v0 + dir * r; // intersect point of ray and plane + + // is hit_point inside triangle? + float uu, uv, vv, wu, wv, D; + uu = KRVector3::Dot(u,u); + uv = KRVector3::Dot(u,v); + vv = KRVector3::Dot(v,v); + w = hit_point - tri_v0; + wu = KRVector3::Dot(w,u); + wv = KRVector3::Dot(w,v); + D = uv * uv - uu * vv; + + // get and test parametric coords + float s, t; + s = (uv * wv - vv * wu) / D; + if (s < 0.0 || s > 1.0) // hit_point is outside triangle + return false; + t = (uv * wu - uu * wv) / D; + if (t < 0.0 || (s + t) > 1.0) // hit_point is outside triangle + return false; + + float new_hit_distance_sqr = (hit_point - line_v0).sqrMagnitude(); + float prev_hit_distance_sqr = (hitinfo.getPosition() - line_v0).sqrMagnitude(); + if(new_hit_distance_sqr < prev_hit_distance_sqr) { + // Update the hitinfo object if this hit is closer than the prior hit + + // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 + KRVector3 distances = KRVector3::Normalize(KRVector3((tri_v0 - hit_point).magnitude(), (tri_v1 - hit_point).magnitude(), (tri_v2 - hit_point).magnitude())); + KRVector3 normal = tri_n0 * (1.0 - distances[0]) + tri_n1 * (1.0 - distances[1]) + tri_n2 * (1.0 - distances[3]); + + hitinfo = KRHitInfo(hit_point, KRVector3()); + } + + return true; // hit_point is in triangle +} + +bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const +{ + return rayCast(line_v0, line_v1, getVertexPosition(tri_index0), getVertexPosition(tri_index1), getVertexPosition(tri_index2), getVertexNormal(tri_index0), getVertexNormal(tri_index1), getVertexNormal(tri_index2), hitinfo); +} + +bool KRModel::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const +{ + bool hit_found = false; + for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { + int vertex_count = getVertexCount(submesh_index); + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { + hit_found |= rayCast(v0, v1, getVertexPosition(triangle_index*3), getVertexPosition(triangle_index*3+1), getVertexPosition(triangle_index*3+2), getVertexNormal(triangle_index*3), getVertexNormal(triangle_index*3+1), getVertexNormal(triangle_index*3+2), hitinfo); + } + break; + case KRENGINE_MODEL_FORMAT_STRIP: + for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { + hit_found |= rayCast(v0, v1, getVertexPosition(triangle_index), getVertexPosition(triangle_index+1), getVertexPosition(triangle_index+2), getVertexNormal(triangle_index), getVertexNormal(triangle_index+1), getVertexNormal(triangle_index+2), hitinfo); + } + break; + default: + break; + } + } + return hit_found; +} + +bool KRModel::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const +{ + KRHitInfo new_hitinfo; + if(rayCast(v0, v1, new_hitinfo)) { + if((new_hitinfo.getPosition() - v0).sqrMagnitude() <= (v1 - v0).sqrMagnitude()) { + // The hit was between v1 and v2 + hitinfo = new_hitinfo; + return true; + } + } + return false; // Either no hit, or the hit was beyond v1 +} diff --git a/KREngine/KREngine/Classes/KRModel.h b/KREngine/KREngine/Classes/KRModel.h index 8f6e552..c31af57 100644 --- a/KREngine/KREngine/Classes/KRModel.h +++ b/KREngine/KREngine/Classes/KRModel.h @@ -56,6 +56,7 @@ using std::list; #import "KRMaterialManager.h" #import "KRCamera.h" #import "KRViewport.h" +#import "KRHitInfo.h" class KRMaterial; class KRNode; @@ -156,8 +157,8 @@ public: static bool lod_sort_predicate(const KRModel *m1, const KRModel *m2); bool has_vertex_attribute(vertex_attrib_t attribute_type) const; - int getSubmeshCount(); - int getVertexCount(int submesh); + int getSubmeshCount() const; + int getVertexCount(int submesh) const; KRVector3 getVertexPosition(int index) const; KRVector3 getVertexNormal(int index) const; KRVector3 getVertexTangent(int index) const; @@ -181,9 +182,13 @@ public: char *getBoneName(int bone_index); - model_format_t getModelFormat(); -private: + model_format_t getModelFormat() const; + bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const; + bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const; +private: + bool rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const; + static bool rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo); int m_lodCoverage; // This LOD level is activated when the bounding box of the model will cover less than this percent of the screen (100 = highest detail model) vector m_materials; @@ -223,7 +228,7 @@ private: - pack_material *getSubmesh(int mesh_index); + pack_material *getSubmesh(int mesh_index) const; unsigned char *getVertexData() const; unsigned char *getVertexData(int index) const; pack_header *getHeader() const;