| 47 | | } |
| 48 | | |
| 49 | | |
| 50 | | void AnimPoseT::SetSequNr(int SequNr) |
| 51 | | { |
| 52 | | if (m_SequNr==SequNr) return; |
| 53 | | |
| 54 | | m_SequNr=SequNr; |
| 55 | | NormalizeInput(); |
| 56 | | |
| 57 | | m_NeedsRecache=true; |
| 58 | | |
| 59 | | // Recursively update the chain of dlod poses. |
| 60 | | if (m_DlodPose) m_DlodPose->SetSequNr(SequNr); |
| 61 | | } |
| 62 | | |
| 63 | | |
| 64 | | void AnimPoseT::SetFrameNr(float FrameNr) |
| 65 | | { |
| 66 | | if (m_FrameNr==FrameNr) return; |
| 67 | | |
| 68 | | m_FrameNr=FrameNr; |
| 69 | | NormalizeInput(); |
| 70 | | |
| 71 | | m_NeedsRecache=true; |
| 72 | | |
| 73 | | // Recursively update the chain of dlod poses. |
| 74 | | if (m_DlodPose) m_DlodPose->SetFrameNr(FrameNr); |
| 75 | | } |
| 76 | | |
| 77 | | |
| 78 | | void AnimPoseT::SetSuperPose(const AnimPoseT* SuperPose) |
| 79 | | { |
| 80 | | if (m_SuperPose==SuperPose) return; |
| 81 | | |
| 82 | | m_SuperPose=SuperPose; |
| 83 | | |
| 84 | | m_NeedsRecache=true; |
| 85 | | |
| 86 | | // Recursively update the chain of dlod poses. |
| 87 | | if (m_DlodPose) m_DlodPose->SetSuperPose(SuperPose); |
| 88 | | } |
| 89 | | |
| 90 | | |
| 91 | | void AnimPoseT::Advance(float Time, bool ForceLoop) |
| 92 | | { |
| 93 | | // TODO: Beachte korrekte Wrap-Regeln für mit loopen und ohne. |
| 94 | | // TODO: Sollte in NormalizeInput() die m_FrameNr gegen das jeweilige Maximum begrenzt werden? |
| 95 | | // TODO: Loops (next vs. ForceLoop) richtig behandeln |
| 96 | | const ArrayT<CafuModelT::AnimT>& Anims=m_Model.GetAnims(); |
| 97 | | |
| 98 | | if (m_SequNr<0 || m_SequNr>=int(Anims.Size())) { SetFrameNr(0.0f); return; } |
| 99 | | if (Anims[m_SequNr].Frames.Size()<=1) { SetFrameNr(0.0f); return; } |
| 100 | | |
| 101 | | const float NumFrames=float(Anims[m_SequNr].Frames.Size()); |
| 102 | | |
| 103 | | float FrameNr=m_FrameNr + Time*Anims[m_SequNr].FPS; |
| 104 | | |
| 105 | | if (ForceLoop) |
| 106 | | { |
| 107 | | // Wrap the sequence (it's a looping (repeating) sequence, like idle, walk, ...). |
| 108 | | FrameNr=fmod(FrameNr, NumFrames); |
| 109 | | if (FrameNr<0.0f) FrameNr+=NumFrames; |
| 110 | | } |
| 111 | | else |
| 112 | | { |
| 113 | | // Clamp the sequence (it's a play-once (non-repeating) sequence, like dying). |
| 114 | | // On clamping, stop the sequence 1/100th sec before the end of the last frame. |
| 115 | | if (FrameNr>=NumFrames-1.0f) FrameNr=NumFrames-1.0f-0.01f; |
| 116 | | if (FrameNr<0.0f) FrameNr=0.0f; |
| 117 | | } |
| 118 | | |
| 119 | | SetFrameNr(FrameNr); |
| 120 | | |
| 121 | | // Recursively update the chain of dlod poses. |
| 122 | | if (m_DlodPose) m_DlodPose->Advance(Time, ForceLoop); |
| 123 | | } |
| 124 | | |
| 125 | | |
| 126 | | void AnimPoseT::Draw(int SkinNr, float LodDist) const |
| 127 | | { |
| 128 | | if (m_Model.GetDlodModel() && LodDist >= m_Model.GetDlodDist()) |
| 129 | | { |
| 130 | | m_DlodPose->Draw(SkinNr, LodDist); |
| 131 | | return; |
| 132 | | } |
| 133 | | |
| 134 | | Recache(); |
| 135 | | |
| 136 | | // Do an early check whether the light and the sequence BBs intersect. |
| 137 | | switch (MatSys::Renderer->GetCurrentRenderAction()) |
| 138 | | { |
| 139 | | case MatSys::RendererI::AMBIENT: |
| 140 | | break; |
| 141 | | |
| 142 | | case MatSys::RendererI::LIGHTING: |
| 143 | | case MatSys::RendererI::STENCILSHADOW: |
| 144 | | { |
| 145 | | const float LightRadius=MatSys::Renderer->GetCurrentLightSourceRadius(); |
| 146 | | const Vector3T<float> LightBox(LightRadius, LightRadius, LightRadius); |
| 147 | | const Vector3T<float> LightPosOLD(MatSys::Renderer->GetCurrentLightSourcePosition()); |
| 148 | | const BoundingBox3T<float> LightBB(LightPosOLD+LightBox, LightPosOLD-LightBox); |
| 149 | | |
| 150 | | if (!LightBB.Intersects(m_BoundingBox)) return; |
| 151 | | break; |
| 152 | | } |
| 153 | | } |
| 154 | | |
| 155 | | switch (MatSys::Renderer->GetCurrentRenderAction()) |
| 156 | | { |
| 157 | | case MatSys::RendererI::AMBIENT: |
| 158 | | { |
| 159 | | for (unsigned long MeshNr=0; MeshNr<m_Draw_Meshes.Size(); MeshNr++) |
| 160 | | { |
| 161 | | MatSys::Renderer->SetCurrentMaterial(m_Model.GetRenderMaterial(MeshNr, SkinNr)); |
| 162 | | MatSys::Renderer->RenderMesh(m_Draw_Meshes[MeshNr]); |
| 163 | | } |
| 164 | | break; |
| 165 | | } |
| 166 | | |
| 167 | | case MatSys::RendererI::LIGHTING: |
| 168 | | { |
| 169 | | for (unsigned long MeshNr=0; MeshNr<m_Draw_Meshes.Size(); MeshNr++) |
| 170 | | { |
| 171 | | MatSys::Renderer->SetCurrentMaterial(m_Model.GetRenderMaterial(MeshNr, SkinNr)); |
| 172 | | MatSys::Renderer->RenderMesh(m_Draw_Meshes[MeshNr]); |
| 173 | | } |
| 174 | | break; |
| 175 | | } |
| 176 | | |
| 177 | | case MatSys::RendererI::STENCILSHADOW: |
| 178 | | { |
| 179 | | typedef CafuModelT::MeshT MeshT; |
| 180 | | |
| 181 | | const ArrayT<MeshT>& Meshes=m_Model.GetMeshes(); |
| 182 | | const Vector3fT LightPos(MatSys::Renderer->GetCurrentLightSourcePosition()); |
| 183 | | |
| 184 | | for (unsigned long MeshNr=0; MeshNr<Meshes.Size(); MeshNr++) |
| 185 | | { |
| 186 | | const MeshT& Mesh =Meshes[MeshNr]; |
| 187 | | const MeshInfoT& MeshInfo=m_MeshInfos[MeshNr]; |
| 188 | | const MaterialT* MeshMat =m_Model.GetMaterial(MeshNr, SkinNr); |
| 189 | | |
| 190 | | if (MeshMat==NULL || MeshMat->NoShadows) continue; |
| 191 | | |
| 192 | | static ArrayT<bool> TriangleIsFrontFacing; |
| 193 | | if (TriangleIsFrontFacing.Size()<Mesh.Triangles.Size()) |
| 194 | | TriangleIsFrontFacing.PushBackEmpty(Mesh.Triangles.Size()-TriangleIsFrontFacing.Size()); |
| 195 | | |
| 196 | | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| 197 | | { |
| 198 | | const MeshT::TriangleT& Tri =Mesh.Triangles[TriNr]; |
| 199 | | const MeshInfoT::TriangleT& TriInfo=MeshInfo.Triangles[TriNr]; |
| 200 | | |
| 201 | | const float Dot=(LightPos-MeshInfo.Vertices[Tri.VertexIdx[0]].Pos).dot(TriInfo.Normal); |
| 202 | | |
| 203 | | TriangleIsFrontFacing[TriNr]=Dot>0; |
| 204 | | } |
| 205 | | |
| 206 | | |
| 207 | | // Note that we have to cull the following polygons wrt. the *VIEWER* (not the light source)! |
| 208 | | static MatSys::MeshT MeshSilhouette(MatSys::MeshT::Quads); // The default winding order is "CW". |
| 209 | | MeshSilhouette.Vertices.Overwrite(); |
| 210 | | |
| 211 | | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| 212 | | { |
| 213 | | if (!TriangleIsFrontFacing[TriNr]) continue; |
| 214 | | |
| 215 | | // This triangle is front-facing wrt. the light source. |
| 216 | | const MeshT::TriangleT& Tri=Mesh.Triangles[TriNr]; |
| 217 | | |
| 218 | | for (unsigned long EdgeNr=0; EdgeNr<3; EdgeNr++) |
| 219 | | { |
| 220 | | // If an edge has no (-1) or more than one (-2) neighbours, make it a silhouette edge. |
| 221 | | const bool IsSilhouetteEdge=(Tri.NeighbIdx[EdgeNr]<0) ? true : !TriangleIsFrontFacing[Tri.NeighbIdx[EdgeNr]]; |
| 222 | | |
| 223 | | if (IsSilhouetteEdge) |
| 224 | | { |
| 225 | | // The neighbour at edge 'EdgeNr' is back-facing (or non-existant), so we have found a possible silhouette edge. |
| 226 | | const unsigned long v1=EdgeNr; |
| 227 | | const unsigned long v2=(EdgeNr+1) % 3; |
| 228 | | const Vector3fT LA=MeshInfo.Vertices[Tri.VertexIdx[v1]].Pos-LightPos; |
| 229 | | const Vector3fT LB=MeshInfo.Vertices[Tri.VertexIdx[v2]].Pos-LightPos; |
| 230 | | |
| 231 | | MeshSilhouette.Vertices.PushBackEmpty(4); |
| 232 | | |
| 233 | | const unsigned long MeshSize=MeshSilhouette.Vertices.Size(); |
| 234 | | |
| 235 | | MeshSilhouette.Vertices[MeshSize-4].SetOrigin(MeshInfo.Vertices[Tri.VertexIdx[v2]].Pos); |
| 236 | | MeshSilhouette.Vertices[MeshSize-3].SetOrigin(MeshInfo.Vertices[Tri.VertexIdx[v1]].Pos); |
| 237 | | MeshSilhouette.Vertices[MeshSize-2].SetOrigin(LA, 0.0); |
| 238 | | MeshSilhouette.Vertices[MeshSize-1].SetOrigin(LB, 0.0); |
| 239 | | } |
| 240 | | } |
| 241 | | } |
| 242 | | |
| 243 | | MatSys::Renderer->RenderMesh(MeshSilhouette); |
| 244 | | |
| 245 | | |
| 246 | | static MatSys::MeshT MeshCaps(MatSys::MeshT::Triangles); // The default winding order is "CW". |
| 247 | | MeshCaps.Vertices.Overwrite(); |
| 248 | | |
| 249 | | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| 250 | | { |
| 251 | | if (!TriangleIsFrontFacing[TriNr]) continue; |
| 252 | | |
| 253 | | // This triangle is front-facing wrt. the light source. |
| 254 | | const CafuModelT::MeshT::TriangleT& Tri=Mesh.Triangles[TriNr]; |
| 255 | | |
| 256 | | MeshCaps.Vertices.PushBackEmpty(6); |
| 257 | | |
| 258 | | const unsigned long MeshSize=MeshCaps.Vertices.Size(); |
| 259 | | |
| 260 | | // Render the occluder (front-facing wrt. the light source). |
| 261 | | const Vector3fT& A=MeshInfo.Vertices[Tri.VertexIdx[0]].Pos; |
| 262 | | const Vector3fT& B=MeshInfo.Vertices[Tri.VertexIdx[1]].Pos; |
| 263 | | const Vector3fT& C=MeshInfo.Vertices[Tri.VertexIdx[2]].Pos; |
| 264 | | |
| 265 | | MeshCaps.Vertices[MeshSize-6].SetOrigin(A); |
| 266 | | MeshCaps.Vertices[MeshSize-5].SetOrigin(B); |
| 267 | | MeshCaps.Vertices[MeshSize-4].SetOrigin(C); |
| 268 | | |
| 269 | | // Render the occluder (back-facing wrt. the light source). |
| 270 | | const Vector3fT LA=A-LightPos; |
| 271 | | const Vector3fT LB=B-LightPos; |
| 272 | | const Vector3fT LC=C-LightPos; |
| 273 | | |
| 274 | | MeshCaps.Vertices[MeshSize-3].SetOrigin(LC, 0.0); |
| 275 | | MeshCaps.Vertices[MeshSize-2].SetOrigin(LB, 0.0); |
| 276 | | MeshCaps.Vertices[MeshSize-1].SetOrigin(LA, 0.0); |
| 277 | | } |
| 278 | | |
| 279 | | MatSys::Renderer->RenderMesh(MeshCaps); |
| 280 | | } |
| 281 | | |
| 282 | | break; |
| 283 | | } |
| 284 | | } |
| 285 | | } |
| 286 | | |
| 287 | | |
| 288 | | bool AnimPoseT::TraceRay(int SkinNr, const Vector3fT& RayOrigin, const Vector3fT& RayDir, ModelT::TraceResultT& Result) const |
| 289 | | { |
| 290 | | // if (m_Model.GetDlodModel() && LodDist >= m_Model.GetDlodDist()) |
| 291 | | // { |
| 292 | | // return m_DlodPose->TraceRay(SkinNr, RayOrigin, RayDir, Result); |
| 293 | | // } |
| 294 | | |
| 295 | | Recache(); |
| 296 | | |
| 297 | | // If we miss the bounding-box, then we miss all the triangles as well. |
| 298 | | float Fraction=0.0f; |
| 299 | | if (!GetBB().TraceRay(RayOrigin, RayDir, Fraction)) return false; |
| 300 | | |
| 301 | | typedef CafuModelT::MeshT MeshT; |
| 302 | | |
| 303 | | const ArrayT<MeshT>& Meshes=m_Model.GetMeshes(); |
| 304 | | |
| 305 | | for (unsigned long MeshNr=0; MeshNr<Meshes.Size(); MeshNr++) |
| 306 | | { |
| 307 | | const MeshT& Mesh =Meshes[MeshNr]; |
| 308 | | const MeshInfoT& MeshInfo=m_MeshInfos[MeshNr]; |
| 309 | | const MaterialT* MeshMat =m_Model.GetMaterial(MeshNr, SkinNr); |
| 310 | | |
| 311 | | // If the ClipFlags don't match the ClipMask, this polygon doesn't interfere with the trace. |
| 312 | | if (!MeshMat) continue; |
| 313 | | // if ((MeshMat->ClipFlags & ClipMask)==0) continue; |
| 314 | | |
| 315 | | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| 316 | | { |
| 317 | | // This code is a modification of the code in CollisionModelStaticT::PolygonT::TraceRay(), |
| 318 | | // see there for details and additional information. |
| 319 | | using namespace cf::math; |
| 320 | | const MeshT::TriangleT& Tri =Mesh.Triangles[TriNr]; |
| 321 | | const MeshInfoT::TriangleT& TriInfo=MeshInfo.Triangles[TriNr]; |
| 322 | | |
| 323 | | const Vector3fT& A=MeshInfo.Vertices[Tri.VertexIdx[0]].Pos; |
| 324 | | const Vector3fT& B=MeshInfo.Vertices[Tri.VertexIdx[1]].Pos; |
| 325 | | const Vector3fT& C=MeshInfo.Vertices[Tri.VertexIdx[2]].Pos; |
| 326 | | |
| 327 | | const PlueckerfT R=PlueckerfT::CreateFromRay(RayOrigin, RayDir); |
| 328 | | |
| 329 | | // We use Pluecker coordinates for the orientation tests. |
| 330 | | // Note that Christer Ericson has shown in his blog at http://realtimecollisiondetection.net/blog/?p=13 |
| 331 | | // that scalar triple products (Spatprodukte) are equivalent and could be used as well. ;-) |
| 332 | | if (!MeshMat->TwoSided) |
| 333 | | { |
| 334 | | if (R*PlueckerfT::CreateFromLine(A, B) >= 0) continue; |
| 335 | | if (R*PlueckerfT::CreateFromLine(B, C) >= 0) continue; |
| 336 | | if (R*PlueckerfT::CreateFromLine(C, A) >= 0) continue; |
| 337 | | } |
| 338 | | else |
| 339 | | { |
| 340 | | int Count=0; |
| 341 | | |
| 342 | | // Should not change Count if result == 0... |
| 343 | | Count+=(R*PlueckerfT::CreateFromLine(A, B) >= 0) ? -1 : 1; |
| 344 | | Count+=(R*PlueckerfT::CreateFromLine(B, C) >= 0) ? -1 : 1; |
| 345 | | Count+=(R*PlueckerfT::CreateFromLine(C, A) >= 0) ? -1 : 1; |
| 346 | | |
| 347 | | if (Count!=-3 && Count!=3) continue; |
| 348 | | } |
| 349 | | |
| 350 | | // The "aperture" test passed, now compute the fraction at which RayDir intersects the triangle plane. |
| 351 | | const float Nenner=dot(TriInfo.Normal, RayDir); |
| 352 | | |
| 353 | | if (Nenner==0) continue; // If Nenner==0, then RayDir is parallel to the triangle plane (no intersection). |
| 354 | | assert(MeshMat->TwoSided || Nenner<0); // If the material is single sided, then Nenner<0, a consequence of the Pluecker tests above. |
| 355 | | |
| 356 | | const float Dist=dot(TriInfo.Normal, RayOrigin-A); // The distance of RayOrigin to the triangle plane. |
| 357 | | const float F =-(Dist-0.03125f)/Nenner; |
| 358 | | |
| 359 | | // The intersection is only valid in the positive direction of RayDir. |
| 360 | | if (F<0) continue; |
| 361 | | |
| 362 | | // Hit the triangle! |
| 363 | | Result.Fraction=F; |
| 364 | | Result.Normal =(Nenner<0) ? TriInfo.Normal : -TriInfo.Normal; // Handle two-sided materials properly. |
| 365 | | Result.Material=MeshMat; |
| 366 | | Result.MeshNr =MeshNr; |
| 367 | | Result.TriNr =TriNr; |
| 368 | | return true; |
| 369 | | } |
| 370 | | } |
| 371 | | |
| 372 | | return false; |
| 373 | | } |
| 374 | | |
| 375 | | |
| 376 | | unsigned int AnimPoseT::FindClosestVertex(unsigned int MeshNr, unsigned int TriNr, const Vector3fT& P) const |
| 377 | | { |
| 378 | | unsigned int BestVertexNr=0; |
| 379 | | float BestDist =0.0f; |
| 380 | | |
| 381 | | Recache(); |
| 382 | | |
| 383 | | for (unsigned int i=0; i<3; i++) |
| 384 | | { |
| 385 | | const unsigned int VertexNr=m_Model.GetMeshes()[MeshNr].Triangles[TriNr].VertexIdx[i]; |
| 386 | | const Vector3fT& DrawPos =m_MeshInfos[MeshNr].Vertices[VertexNr].Pos; |
| 387 | | const float Dist =length(DrawPos-P); |
| 388 | | |
| 389 | | if (i==0 || Dist<BestDist) |
| 390 | | { |
| 391 | | BestDist =Dist; |
| 392 | | BestVertexNr=VertexNr; |
| 393 | | } |
| 394 | | } |
| 395 | | |
| 396 | | return BestVertexNr; |
| 397 | | } |
| 398 | | |
| 399 | | |
| 400 | | const ArrayT<MatrixT>& AnimPoseT::GetJointMatrices() const |
| 401 | | { |
| 402 | | Recache(); |
| 403 | | |
| 404 | | return m_JointMatrices; |
| 405 | | } |
| 406 | | |
| 407 | | |
| 408 | | const MatrixT* AnimPoseT::GetJointMatrix(const std::string& JointName) const |
| 409 | | { |
| 410 | | Recache(); |
| 411 | | |
| 412 | | for (unsigned int JointNr=0; JointNr<m_Model.GetJoints().Size(); JointNr++) |
| 413 | | if (m_Model.GetJoints()[JointNr].Name == JointName) |
| 414 | | return &m_JointMatrices[JointNr]; |
| 415 | | |
| 416 | | return NULL; |
| 417 | | } |
| 418 | | |
| 419 | | |
| 420 | | const ArrayT<AnimPoseT::MeshInfoT>& AnimPoseT::GetMeshInfos() const |
| 421 | | { |
| 422 | | Recache(); |
| 423 | | |
| 424 | | return m_MeshInfos; |
| 425 | | } |
| 426 | | |
| 427 | | |
| 428 | | const ArrayT<MatSys::MeshT>& AnimPoseT::GetDrawMeshes() const |
| 429 | | { |
| 430 | | Recache(); |
| 431 | | |
| 432 | | return m_Draw_Meshes; |
| 433 | | } |
| 434 | | |
| 435 | | |
| 436 | | const BoundingBox3fT& AnimPoseT::GetBB() const |
| 437 | | { |
| 438 | | Recache(); |
| 439 | | |
| 440 | | return m_BoundingBox; |
| 441 | | } |
| 442 | | |
| 443 | | |
| 444 | | void AnimPoseT::NormalizeInput() |
| 445 | | { |
| 446 | | const ArrayT<CafuModelT::AnimT>& Anims=m_Model.GetAnims(); |
| 447 | | |
| 448 | | // m_SequNr==-1 means "use the bind pose from the model file only (no anim)". |
| 449 | | if (m_SequNr < -1) m_SequNr = -1; |
| 450 | | if (m_SequNr >= int(Anims.Size())) m_SequNr = -1; |
| 451 | | if (m_SequNr != -1 && (Anims[m_SequNr].FPS<0.0 || Anims[m_SequNr].Frames.Size()==0)) m_SequNr = -1; |
| 452 | | if (m_SequNr == -1) m_FrameNr = 0.0f; |
| | 455 | |
| | 456 | |
| | 457 | void AnimPoseT::NormalizeInput() |
| | 458 | { |
| | 459 | const ArrayT<CafuModelT::AnimT>& Anims=m_Model.GetAnims(); |
| | 460 | |
| | 461 | // m_SequNr==-1 means "use the bind pose from the model file only (no anim)". |
| | 462 | if (m_SequNr < -1) m_SequNr = -1; |
| | 463 | if (m_SequNr >= int(Anims.Size())) m_SequNr = -1; |
| | 464 | if (m_SequNr != -1 && (Anims[m_SequNr].FPS<0.0 || Anims[m_SequNr].Frames.Size()==0)) m_SequNr = -1; |
| | 465 | if (m_SequNr == -1) m_FrameNr = 0.0f; |
| | 466 | } |
| | 467 | |
| | 468 | |
| | 469 | void AnimPoseT::SetSequNr(int SequNr) |
| | 470 | { |
| | 471 | if (m_SequNr==SequNr) return; |
| | 472 | |
| | 473 | m_SequNr=SequNr; |
| | 474 | NormalizeInput(); |
| | 475 | |
| | 476 | m_NeedsRecache=true; |
| | 477 | |
| | 478 | // Recursively update the chain of dlod poses. |
| | 479 | if (m_DlodPose) m_DlodPose->SetSequNr(SequNr); |
| | 480 | } |
| | 481 | |
| | 482 | |
| | 483 | void AnimPoseT::SetFrameNr(float FrameNr) |
| | 484 | { |
| | 485 | if (m_FrameNr==FrameNr) return; |
| | 486 | |
| | 487 | m_FrameNr=FrameNr; |
| | 488 | NormalizeInput(); |
| | 489 | |
| | 490 | m_NeedsRecache=true; |
| | 491 | |
| | 492 | // Recursively update the chain of dlod poses. |
| | 493 | if (m_DlodPose) m_DlodPose->SetFrameNr(FrameNr); |
| | 494 | } |
| | 495 | |
| | 496 | |
| | 497 | void AnimPoseT::SetSuperPose(const AnimPoseT* SuperPose) |
| | 498 | { |
| | 499 | if (m_SuperPose==SuperPose) return; |
| | 500 | |
| | 501 | m_SuperPose=SuperPose; |
| | 502 | |
| | 503 | m_NeedsRecache=true; |
| | 504 | |
| | 505 | // Recursively update the chain of dlod poses. |
| | 506 | if (m_DlodPose) m_DlodPose->SetSuperPose(SuperPose); |
| | 507 | } |
| | 508 | |
| | 509 | |
| | 510 | void AnimPoseT::Advance(float Time, bool ForceLoop) |
| | 511 | { |
| | 512 | // TODO: Beachte korrekte Wrap-Regeln für mit loopen und ohne. |
| | 513 | // TODO: Sollte in NormalizeInput() die m_FrameNr gegen das jeweilige Maximum begrenzt werden? |
| | 514 | // TODO: Loops (next vs. ForceLoop) richtig behandeln |
| | 515 | const ArrayT<CafuModelT::AnimT>& Anims=m_Model.GetAnims(); |
| | 516 | |
| | 517 | if (m_SequNr<0 || m_SequNr>=int(Anims.Size())) { SetFrameNr(0.0f); return; } |
| | 518 | if (Anims[m_SequNr].Frames.Size()<=1) { SetFrameNr(0.0f); return; } |
| | 519 | |
| | 520 | const float NumFrames=float(Anims[m_SequNr].Frames.Size()); |
| | 521 | |
| | 522 | float FrameNr=m_FrameNr + Time*Anims[m_SequNr].FPS; |
| | 523 | |
| | 524 | if (ForceLoop) |
| | 525 | { |
| | 526 | // Wrap the sequence (it's a looping (repeating) sequence, like idle, walk, ...). |
| | 527 | FrameNr=fmod(FrameNr, NumFrames); |
| | 528 | if (FrameNr<0.0f) FrameNr+=NumFrames; |
| | 529 | } |
| | 530 | else |
| | 531 | { |
| | 532 | // Clamp the sequence (it's a play-once (non-repeating) sequence, like dying). |
| | 533 | // On clamping, stop the sequence 1/100th sec before the end of the last frame. |
| | 534 | if (FrameNr>=NumFrames-1.0f) FrameNr=NumFrames-1.0f-0.01f; |
| | 535 | if (FrameNr<0.0f) FrameNr=0.0f; |
| | 536 | } |
| | 537 | |
| | 538 | SetFrameNr(FrameNr); |
| | 539 | |
| | 540 | // Recursively update the chain of dlod poses. |
| | 541 | if (m_DlodPose) m_DlodPose->Advance(Time, ForceLoop); |
| | 542 | } |
| | 543 | |
| | 544 | |
| | 545 | void AnimPoseT::Draw(int SkinNr, float LodDist) const |
| | 546 | { |
| | 547 | if (m_Model.GetDlodModel() && LodDist >= m_Model.GetDlodDist()) |
| | 548 | { |
| | 549 | m_DlodPose->Draw(SkinNr, LodDist); |
| | 550 | return; |
| | 551 | } |
| | 552 | |
| | 553 | Recache(); |
| | 554 | |
| | 555 | // Do an early check whether the light and the sequence BBs intersect. |
| | 556 | switch (MatSys::Renderer->GetCurrentRenderAction()) |
| | 557 | { |
| | 558 | case MatSys::RendererI::AMBIENT: |
| | 559 | break; |
| | 560 | |
| | 561 | case MatSys::RendererI::LIGHTING: |
| | 562 | case MatSys::RendererI::STENCILSHADOW: |
| | 563 | { |
| | 564 | const float LightRadius=MatSys::Renderer->GetCurrentLightSourceRadius(); |
| | 565 | const Vector3T<float> LightBox(LightRadius, LightRadius, LightRadius); |
| | 566 | const Vector3T<float> LightPosOLD(MatSys::Renderer->GetCurrentLightSourcePosition()); |
| | 567 | const BoundingBox3T<float> LightBB(LightPosOLD+LightBox, LightPosOLD-LightBox); |
| | 568 | |
| | 569 | if (!LightBB.Intersects(m_BoundingBox)) return; |
| | 570 | break; |
| | 571 | } |
| | 572 | } |
| | 573 | |
| | 574 | switch (MatSys::Renderer->GetCurrentRenderAction()) |
| | 575 | { |
| | 576 | case MatSys::RendererI::AMBIENT: |
| | 577 | { |
| | 578 | for (unsigned long MeshNr=0; MeshNr<m_Draw_Meshes.Size(); MeshNr++) |
| | 579 | { |
| | 580 | MatSys::Renderer->SetCurrentMaterial(m_Model.GetRenderMaterial(MeshNr, SkinNr)); |
| | 581 | MatSys::Renderer->RenderMesh(m_Draw_Meshes[MeshNr]); |
| | 582 | } |
| | 583 | break; |
| | 584 | } |
| | 585 | |
| | 586 | case MatSys::RendererI::LIGHTING: |
| | 587 | { |
| | 588 | for (unsigned long MeshNr=0; MeshNr<m_Draw_Meshes.Size(); MeshNr++) |
| | 589 | { |
| | 590 | MatSys::Renderer->SetCurrentMaterial(m_Model.GetRenderMaterial(MeshNr, SkinNr)); |
| | 591 | MatSys::Renderer->RenderMesh(m_Draw_Meshes[MeshNr]); |
| | 592 | } |
| | 593 | break; |
| | 594 | } |
| | 595 | |
| | 596 | case MatSys::RendererI::STENCILSHADOW: |
| | 597 | { |
| | 598 | typedef CafuModelT::MeshT MeshT; |
| | 599 | |
| | 600 | const ArrayT<MeshT>& Meshes=m_Model.GetMeshes(); |
| | 601 | const Vector3fT LightPos(MatSys::Renderer->GetCurrentLightSourcePosition()); |
| | 602 | |
| | 603 | for (unsigned long MeshNr=0; MeshNr<Meshes.Size(); MeshNr++) |
| | 604 | { |
| | 605 | const MeshT& Mesh =Meshes[MeshNr]; |
| | 606 | const MeshInfoT& MeshInfo=m_MeshInfos[MeshNr]; |
| | 607 | const MaterialT* MeshMat =m_Model.GetMaterial(MeshNr, SkinNr); |
| | 608 | |
| | 609 | if (MeshMat==NULL || MeshMat->NoShadows) continue; |
| | 610 | |
| | 611 | static ArrayT<bool> TriangleIsFrontFacing; |
| | 612 | if (TriangleIsFrontFacing.Size()<Mesh.Triangles.Size()) |
| | 613 | TriangleIsFrontFacing.PushBackEmpty(Mesh.Triangles.Size()-TriangleIsFrontFacing.Size()); |
| | 614 | |
| | 615 | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| | 616 | { |
| | 617 | const MeshT::TriangleT& Tri =Mesh.Triangles[TriNr]; |
| | 618 | const MeshInfoT::TriangleT& TriInfo=MeshInfo.Triangles[TriNr]; |
| | 619 | |
| | 620 | const float Dot=(LightPos-MeshInfo.Vertices[Tri.VertexIdx[0]].Pos).dot(TriInfo.Normal); |
| | 621 | |
| | 622 | TriangleIsFrontFacing[TriNr]=Dot>0; |
| | 623 | } |
| | 624 | |
| | 625 | |
| | 626 | // Note that we have to cull the following polygons wrt. the *VIEWER* (not the light source)! |
| | 627 | static MatSys::MeshT MeshSilhouette(MatSys::MeshT::Quads); // The default winding order is "CW". |
| | 628 | MeshSilhouette.Vertices.Overwrite(); |
| | 629 | |
| | 630 | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| | 631 | { |
| | 632 | if (!TriangleIsFrontFacing[TriNr]) continue; |
| | 633 | |
| | 634 | // This triangle is front-facing wrt. the light source. |
| | 635 | const MeshT::TriangleT& Tri=Mesh.Triangles[TriNr]; |
| | 636 | |
| | 637 | for (unsigned long EdgeNr=0; EdgeNr<3; EdgeNr++) |
| | 638 | { |
| | 639 | // If an edge has no (-1) or more than one (-2) neighbours, make it a silhouette edge. |
| | 640 | const bool IsSilhouetteEdge=(Tri.NeighbIdx[EdgeNr]<0) ? true : !TriangleIsFrontFacing[Tri.NeighbIdx[EdgeNr]]; |
| | 641 | |
| | 642 | if (IsSilhouetteEdge) |
| | 643 | { |
| | 644 | // The neighbour at edge 'EdgeNr' is back-facing (or non-existant), so we have found a possible silhouette edge. |
| | 645 | const unsigned long v1=EdgeNr; |
| | 646 | const unsigned long v2=(EdgeNr+1) % 3; |
| | 647 | const Vector3fT LA=MeshInfo.Vertices[Tri.VertexIdx[v1]].Pos-LightPos; |
| | 648 | const Vector3fT LB=MeshInfo.Vertices[Tri.VertexIdx[v2]].Pos-LightPos; |
| | 649 | |
| | 650 | MeshSilhouette.Vertices.PushBackEmpty(4); |
| | 651 | |
| | 652 | const unsigned long MeshSize=MeshSilhouette.Vertices.Size(); |
| | 653 | |
| | 654 | MeshSilhouette.Vertices[MeshSize-4].SetOrigin(MeshInfo.Vertices[Tri.VertexIdx[v2]].Pos); |
| | 655 | MeshSilhouette.Vertices[MeshSize-3].SetOrigin(MeshInfo.Vertices[Tri.VertexIdx[v1]].Pos); |
| | 656 | MeshSilhouette.Vertices[MeshSize-2].SetOrigin(LA, 0.0); |
| | 657 | MeshSilhouette.Vertices[MeshSize-1].SetOrigin(LB, 0.0); |
| | 658 | } |
| | 659 | } |
| | 660 | } |
| | 661 | |
| | 662 | MatSys::Renderer->RenderMesh(MeshSilhouette); |
| | 663 | |
| | 664 | |
| | 665 | static MatSys::MeshT MeshCaps(MatSys::MeshT::Triangles); // The default winding order is "CW". |
| | 666 | MeshCaps.Vertices.Overwrite(); |
| | 667 | |
| | 668 | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| | 669 | { |
| | 670 | if (!TriangleIsFrontFacing[TriNr]) continue; |
| | 671 | |
| | 672 | // This triangle is front-facing wrt. the light source. |
| | 673 | const CafuModelT::MeshT::TriangleT& Tri=Mesh.Triangles[TriNr]; |
| | 674 | |
| | 675 | MeshCaps.Vertices.PushBackEmpty(6); |
| | 676 | |
| | 677 | const unsigned long MeshSize=MeshCaps.Vertices.Size(); |
| | 678 | |
| | 679 | // Render the occluder (front-facing wrt. the light source). |
| | 680 | const Vector3fT& A=MeshInfo.Vertices[Tri.VertexIdx[0]].Pos; |
| | 681 | const Vector3fT& B=MeshInfo.Vertices[Tri.VertexIdx[1]].Pos; |
| | 682 | const Vector3fT& C=MeshInfo.Vertices[Tri.VertexIdx[2]].Pos; |
| | 683 | |
| | 684 | MeshCaps.Vertices[MeshSize-6].SetOrigin(A); |
| | 685 | MeshCaps.Vertices[MeshSize-5].SetOrigin(B); |
| | 686 | MeshCaps.Vertices[MeshSize-4].SetOrigin(C); |
| | 687 | |
| | 688 | // Render the occluder (back-facing wrt. the light source). |
| | 689 | const Vector3fT LA=A-LightPos; |
| | 690 | const Vector3fT LB=B-LightPos; |
| | 691 | const Vector3fT LC=C-LightPos; |
| | 692 | |
| | 693 | MeshCaps.Vertices[MeshSize-3].SetOrigin(LC, 0.0); |
| | 694 | MeshCaps.Vertices[MeshSize-2].SetOrigin(LB, 0.0); |
| | 695 | MeshCaps.Vertices[MeshSize-1].SetOrigin(LA, 0.0); |
| | 696 | } |
| | 697 | |
| | 698 | MatSys::Renderer->RenderMesh(MeshCaps); |
| | 699 | } |
| | 700 | |
| | 701 | break; |
| | 702 | } |
| | 703 | } |
| | 704 | } |
| | 705 | |
| | 706 | |
| | 707 | bool AnimPoseT::TraceRay(int SkinNr, const Vector3fT& RayOrigin, const Vector3fT& RayDir, ModelT::TraceResultT& Result) const |
| | 708 | { |
| | 709 | // if (m_Model.GetDlodModel() && LodDist >= m_Model.GetDlodDist()) |
| | 710 | // { |
| | 711 | // return m_DlodPose->TraceRay(SkinNr, RayOrigin, RayDir, Result); |
| | 712 | // } |
| | 713 | |
| | 714 | Recache(); |
| | 715 | |
| | 716 | // If we miss the bounding-box, then we miss all the triangles as well. |
| | 717 | float Fraction=0.0f; |
| | 718 | if (!GetBB().TraceRay(RayOrigin, RayDir, Fraction)) return false; |
| | 719 | |
| | 720 | typedef CafuModelT::MeshT MeshT; |
| | 721 | |
| | 722 | const ArrayT<MeshT>& Meshes=m_Model.GetMeshes(); |
| | 723 | |
| | 724 | for (unsigned long MeshNr=0; MeshNr<Meshes.Size(); MeshNr++) |
| | 725 | { |
| | 726 | const MeshT& Mesh =Meshes[MeshNr]; |
| | 727 | const MeshInfoT& MeshInfo=m_MeshInfos[MeshNr]; |
| | 728 | const MaterialT* MeshMat =m_Model.GetMaterial(MeshNr, SkinNr); |
| | 729 | |
| | 730 | // If the ClipFlags don't match the ClipMask, this polygon doesn't interfere with the trace. |
| | 731 | if (!MeshMat) continue; |
| | 732 | // if ((MeshMat->ClipFlags & ClipMask)==0) continue; |
| | 733 | |
| | 734 | for (unsigned long TriNr=0; TriNr<Mesh.Triangles.Size(); TriNr++) |
| | 735 | { |
| | 736 | // This code is a modification of the code in CollisionModelStaticT::PolygonT::TraceRay(), |
| | 737 | // see there for details and additional information. |
| | 738 | using namespace cf::math; |
| | 739 | const MeshT::TriangleT& Tri =Mesh.Triangles[TriNr]; |
| | 740 | const MeshInfoT::TriangleT& TriInfo=MeshInfo.Triangles[TriNr]; |
| | 741 | |
| | 742 | const Vector3fT& A=MeshInfo.Vertices[Tri.VertexIdx[0]].Pos; |
| | 743 | const Vector3fT& B=MeshInfo.Vertices[Tri.VertexIdx[1]].Pos; |
| | 744 | const Vector3fT& C=MeshInfo.Vertices[Tri.VertexIdx[2]].Pos; |
| | 745 | |
| | 746 | const PlueckerfT R=PlueckerfT::CreateFromRay(RayOrigin, RayDir); |
| | 747 | |
| | 748 | // We use Pluecker coordinates for the orientation tests. |
| | 749 | // Note that Christer Ericson has shown in his blog at http://realtimecollisiondetection.net/blog/?p=13 |
| | 750 | // that scalar triple products (Spatprodukte) are equivalent and could be used as well. ;-) |
| | 751 | if (!MeshMat->TwoSided) |
| | 752 | { |
| | 753 | if (R*PlueckerfT::CreateFromLine(A, B) >= 0) continue; |
| | 754 | if (R*PlueckerfT::CreateFromLine(B, C) >= 0) continue; |
| | 755 | if (R*PlueckerfT::CreateFromLine(C, A) >= 0) continue; |
| | 756 | } |
| | 757 | else |
| | 758 | { |
| | 759 | int Count=0; |
| | 760 | |
| | 761 | // Should not change Count if result == 0... |
| | 762 | Count+=(R*PlueckerfT::CreateFromLine(A, B) >= 0) ? -1 : 1; |
| | 763 | Count+=(R*PlueckerfT::CreateFromLine(B, C) >= 0) ? -1 : 1; |
| | 764 | Count+=(R*PlueckerfT::CreateFromLine(C, A) >= 0) ? -1 : 1; |
| | 765 | |
| | 766 | if (Count!=-3 && Count!=3) continue; |
| | 767 | } |
| | 768 | |
| | 769 | // The "aperture" test passed, now compute the fraction at which RayDir intersects the triangle plane. |
| | 770 | const float Nenner=dot(TriInfo.Normal, RayDir); |
| | 771 | |
| | 772 | if (Nenner==0) continue; // If Nenner==0, then RayDir is parallel to the triangle plane (no intersection). |
| | 773 | assert(MeshMat->TwoSided || Nenner<0); // If the material is single sided, then Nenner<0, a consequence of the Pluecker tests above. |
| | 774 | |
| | 775 | const float Dist=dot(TriInfo.Normal, RayOrigin-A); // The distance of RayOrigin to the triangle plane. |
| | 776 | const float F =-(Dist-0.03125f)/Nenner; |
| | 777 | |
| | 778 | // The intersection is only valid in the positive direction of RayDir. |
| | 779 | if (F<0) continue; |
| | 780 | |
| | 781 | // Hit the triangle! |
| | 782 | Result.Fraction=F; |
| | 783 | Result.Normal =(Nenner<0) ? TriInfo.Normal : -TriInfo.Normal; // Handle two-sided materials properly. |
| | 784 | Result.Material=MeshMat; |
| | 785 | Result.MeshNr =MeshNr; |
| | 786 | Result.TriNr =TriNr; |
| | 787 | return true; |
| | 788 | } |
| | 789 | } |
| | 790 | |
| | 791 | return false; |
| | 792 | } |
| | 793 | |
| | 794 | |
| | 795 | unsigned int AnimPoseT::FindClosestVertex(unsigned int MeshNr, unsigned int TriNr, const Vector3fT& P) const |
| | 796 | { |
| | 797 | unsigned int BestVertexNr=0; |
| | 798 | float BestDist =0.0f; |
| | 799 | |
| | 800 | Recache(); |
| | 801 | |
| | 802 | for (unsigned int i=0; i<3; i++) |
| | 803 | { |
| | 804 | const unsigned int VertexNr=m_Model.GetMeshes()[MeshNr].Triangles[TriNr].VertexIdx[i]; |
| | 805 | const Vector3fT& DrawPos =m_MeshInfos[MeshNr].Vertices[VertexNr].Pos; |
| | 806 | const float Dist =length(DrawPos-P); |
| | 807 | |
| | 808 | if (i==0 || Dist<BestDist) |
| | 809 | { |
| | 810 | BestDist =Dist; |
| | 811 | BestVertexNr=VertexNr; |
| | 812 | } |
| | 813 | } |
| | 814 | |
| | 815 | return BestVertexNr; |
| | 816 | } |
| | 817 | |
| | 818 | |
| | 819 | const ArrayT<MatrixT>& AnimPoseT::GetJointMatrices() const |
| | 820 | { |
| | 821 | Recache(); |
| | 822 | |
| | 823 | return m_JointMatrices; |
| | 824 | } |
| | 825 | |
| | 826 | |
| | 827 | const MatrixT* AnimPoseT::GetJointMatrix(const std::string& JointName) const |
| | 828 | { |
| | 829 | Recache(); |
| | 830 | |
| | 831 | for (unsigned int JointNr=0; JointNr<m_Model.GetJoints().Size(); JointNr++) |
| | 832 | if (m_Model.GetJoints()[JointNr].Name == JointName) |
| | 833 | return &m_JointMatrices[JointNr]; |
| | 834 | |
| | 835 | return NULL; |
| | 836 | } |
| | 837 | |
| | 838 | |
| | 839 | const ArrayT<AnimPoseT::MeshInfoT>& AnimPoseT::GetMeshInfos() const |
| | 840 | { |
| | 841 | Recache(); |
| | 842 | |
| | 843 | return m_MeshInfos; |
| | 844 | } |
| | 845 | |
| | 846 | |
| | 847 | const ArrayT<MatSys::MeshT>& AnimPoseT::GetDrawMeshes() const |
| | 848 | { |
| | 849 | Recache(); |
| | 850 | |
| | 851 | return m_Draw_Meshes; |
| | 852 | } |
| | 853 | |
| | 854 | |
| | 855 | const BoundingBox3fT& AnimPoseT::GetBB() const |
| | 856 | { |
| | 857 | Recache(); |
| | 858 | |
| | 859 | return m_BoundingBox; |
| | 860 | } |