@@ -254,6 +254,164 @@ void ConvertMatrixToEulerAngles(const CMatrix_Padded& matrixPadded, float& fX, f
254
254
}
255
255
}
256
256
257
+
258
+ auto CWorldSA::ProcessLineAgainstMesh (CEntitySAInterface* targetEntity, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult
259
+ {
260
+ assert (targetEntity);
261
+
262
+ SProcessLineOfSightMaterialInfoResult ret;
263
+
264
+ struct Context
265
+ {
266
+ float minHitDistSq{}; // < [squared] hit distance from the line segment's origin
267
+ CVector originOS, endOS, dirOS; // < Line origin, end and dir [in object space]
268
+ CMatrix entMat, entInvMat; // < The hit entity's matrix, and it's inverse
269
+ RpTriangle* hitTri{}; // < The triangle hit
270
+ RpAtomic* hitAtomic{}; // < The atomic of the hit triangle's geometry
271
+ RpGeometry* hitGeo{}; // < The geometry of the hit triangle
272
+ CVector hitBary{}; // < Barycentric coordinates [on the hit triangle] of the hit
273
+ CVector hitPosOS{}; // < Hit position in object space
274
+ CEntitySAInterface* entity{}; // < The hit entity
275
+ } c = {};
276
+
277
+ c.entity = targetEntity;
278
+
279
+ if (!c.entity ->m_pRwObject ) {
280
+ return ret; // isValid will be false in this case
281
+ }
282
+
283
+ // Get matrix, and it's inverse
284
+ c.entity ->Placeable .matrix ->ConvertToMatrix (c.entMat );
285
+ c.entInvMat = c.entMat .Inverse ();
286
+
287
+ // ...to transform the line origin and end into object space
288
+ c.originOS = c.entInvMat .TransformVector (start);
289
+ c.endOS = c.entInvMat .TransformVector (end);
290
+ c.dirOS = c.endOS - c.originOS ;
291
+ c.minHitDistSq = c.dirOS .LengthSquared (); // By setting it to this value we avoid collisions that would be detected after the line segment
292
+ // [but are still ont the ray]
293
+
294
+ // Do raycast against the DFF to get hit position material UV and name
295
+ // This is very slow
296
+ // Perhaps we could parallelize it somehow? [OpenMP?]
297
+ const auto ProcessOneAtomic = [](RpAtomic* a, void * data)
298
+ {
299
+ Context* const c = static_cast <Context*>(data);
300
+ RwFrame* const f = RpAtomicGetFrame (a);
301
+
302
+ const auto GetFrameCMatrix = [](RwFrame* f)
303
+ {
304
+ CMatrix out;
305
+ pGame->GetRenderWare ()->RwMatrixToCMatrix (*RwFrameGetMatrix (f), out);
306
+ return out;
307
+ };
308
+
309
+ // Atomic not visible
310
+ if (!a->renderCallback || !(a->object .object .flags & 0x04 /* rpATOMICRENDER*/ ))
311
+ {
312
+ return true ;
313
+ }
314
+
315
+ // Sometimes atomics have no geometry [I don't think that should be possible, but okay]
316
+ RpGeometry* const geo = a->geometry ;
317
+ if (!geo)
318
+ {
319
+ return true ;
320
+ }
321
+
322
+ // Calculate transformation by traversing the hierarchy from the bottom (this frame) -> top (root frame)
323
+ CMatrix localToObjTransform{};
324
+ for (RwFrame* i = f; i && i != i->root ; i = RwFrameGetParent (i))
325
+ {
326
+ localToObjTransform = GetFrameCMatrix (i) * localToObjTransform;
327
+ }
328
+ const CMatrix objToLocalTransform = localToObjTransform.Inverse ();
329
+
330
+ const auto ObjectToLocalSpace = [&](const CVector& in) { return objToLocalTransform.TransformVector (in); };
331
+
332
+ // Transform from object space, into local (the frame's) space
333
+ const CVector localOrigin = ObjectToLocalSpace (c->originOS );
334
+ const CVector localEnd = ObjectToLocalSpace (c->endOS );
335
+
336
+ #if 0
337
+ if (!CCollisionSA::TestLineSphere(
338
+ CColLineSA{localOrigin, 0.f, localEnd, 0.f},
339
+ reinterpret_cast<CColSphereSA&>(*RpAtomicGetBoundingSphere(a)) // Fine for now
340
+ )) {
341
+ return true; // Line segment doesn't touch bsp
342
+ }
343
+ #endif
344
+ const CVector localDir = localEnd - localOrigin;
345
+
346
+ const CVector* const verts = reinterpret_cast <CVector*>(geo->morph_target ->verts ); // It's fine, trust me bro
347
+ for (auto i = geo->triangles_size ; i-- > 0 ;)
348
+ {
349
+ RpTriangle* const tri = &geo->triangles [i];
350
+
351
+ // Process the line against the triangle
352
+ CVector hitBary, hitPos;
353
+ if (!localOrigin.IntersectsSegmentTriangle (localDir, verts[tri->verts [0 ]], verts[tri->verts [1 ]], verts[tri->verts [2 ]], &hitPos, &hitBary))
354
+ {
355
+ continue ; // No intersection at all
356
+ }
357
+
358
+ // Intersection, check if it's closer than the previous one
359
+ const float hitDistSq = (hitPos - localOrigin).LengthSquared ();
360
+ if (c->minHitDistSq > hitDistSq)
361
+ {
362
+ c->minHitDistSq = hitDistSq;
363
+ c->hitGeo = geo;
364
+ c->hitAtomic = a;
365
+ c->hitTri = tri;
366
+ c->hitBary = hitBary;
367
+ c->hitPosOS = localToObjTransform.TransformVector (hitPos); // Transform back into object space
368
+ }
369
+ }
370
+
371
+ return true ;
372
+ };
373
+
374
+ if (c.entity ->m_pRwObject ->object .type == 2 /* rpCLUMP*/ )
375
+ {
376
+ RpClumpForAllAtomics (c.entity ->m_pRwObject , ProcessOneAtomic, &c);
377
+ }
378
+ else
379
+ { // Object is a single atomic, so process directly
380
+ ProcessOneAtomic (reinterpret_cast <RpAtomic*>(c.entity ->m_pRwObject ), &c);
381
+ }
382
+
383
+ if (ret.valid = c.hitGeo != nullptr )
384
+ {
385
+ // Now, calculate texture UV, etc based on the hit [if we've hit anything at all]
386
+ // Since we have the barycentric coords of the hit, calculating it is easy
387
+ ret.uv = {};
388
+ for (int i = 0 ; i < 3 ; i++)
389
+ {
390
+ // UV set index - Usually models only use level 0 indices, so let's stick with that
391
+ const int uvSetIdx = 0 ;
392
+
393
+ // Vertex's UV position
394
+ RwTextureCoordinates* const vtxUV = &c.hitGeo ->texcoords [uvSetIdx][c.hitTri ->verts [i]];
395
+
396
+ // Now, just interpolate
397
+ ret.uv += CVector2D{vtxUV->u , vtxUV->v } * c.hitBary [i];
398
+ }
399
+
400
+ // Find out material texture name
401
+ // For some reason this is sometimes null
402
+ RwTexture* const tex = c.hitGeo ->materials .materials [c.hitTri ->materialId ]->texture ;
403
+ ret.textureName = tex ? tex->name : nullptr ;
404
+
405
+ RwFrame* const hitFrame = RpAtomicGetFrame (c.hitAtomic );
406
+ ret.frameName = hitFrame ? hitFrame->szName : nullptr ;
407
+
408
+ // Get hit position in world space
409
+ ret.hitPos = c.entMat .TransformVector (c.hitPosOS );
410
+ }
411
+
412
+ return ret;
413
+ }
414
+
257
415
bool CWorldSA::ProcessLineOfSight (const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity,
258
416
const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo)
259
417
{
@@ -354,153 +512,15 @@ bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd
354
512
}
355
513
}
356
514
357
- if (outMatInfo && targetEntity && targetEntity-> m_pRwObject )
515
+ if (outMatInfo)
358
516
{
359
- struct Context
517
+ outMatInfo->valid = false ;
518
+ if (targetEntity)
360
519
{
361
- float minHitDistSq{}; // < [squared] hit distance from the line segment's origin
362
- CVector originOS, endOS, dirOS; // < Line origin, end and dir [in object space]
363
- CMatrix entMat, entInvMat; // < The hit entity's matrix, and it's inverse
364
- RpTriangle* hitTri{}; // < The triangle hit
365
- RpAtomic* hitAtomic{}; // < The atomic of the hit triangle's geometry
366
- RpGeometry* hitGeo{}; // < The geometry of the hit triangle
367
- CVector hitBary{}; // < Barycentric coordinates [on the hit triangle] of the hit
368
- CVector hitPosOS{}; // < Hit position in object space
369
- CEntitySAInterface* entity{}; // < The hit entity
370
- } c = {};
371
-
372
- c.entity = targetEntity;
373
-
374
- // Get matrix, and it's inverse
375
- targetEntity->Placeable .matrix ->ConvertToMatrix (c.entMat );
376
- c.entInvMat = c.entMat .Inverse ();
377
-
378
- // ...to transform the line origin and end into object space
379
- c.originOS = c.entInvMat .TransformVector (*vecStart);
380
- c.endOS = c.entInvMat .TransformVector (*vecEnd);
381
- c.dirOS = c.endOS - c.originOS ;
382
- c.minHitDistSq = c.dirOS .LengthSquared (); // By setting it to this value we avoid collisions that would be detected after the line segment
383
- // [but are still ont the ray]
384
-
385
- // Do raycast against the DFF to get hit position material UV and name
386
- // This is very slow
387
- // Perhaps we could paralellize it somehow? [OpenMP?]
388
- const auto ProcessOneAtomic = [](RpAtomic* a, void * data)
389
- {
390
- const auto c = (Context*)data;
391
- const auto f = RpAtomicGetFrame (a);
392
-
393
- const auto GetFrameCMatrix = [](RwFrame* f)
394
- {
395
- CMatrix out;
396
- pGame->GetRenderWare ()->RwMatrixToCMatrix (*RwFrameGetMatrix (f), out);
397
- return out;
398
- };
399
-
400
- // Atomic not visible
401
- if (!a->renderCallback || !(a->object .object .flags & 0x04 /* rpATOMICRENDER*/ ))
402
- {
403
- return true ;
404
- }
405
-
406
- // Sometimes atomics have no geometry [I don't think that should be possible, but okay]
407
- const auto geo = a->geometry ;
408
- if (!geo)
409
- {
410
- return true ;
411
- }
412
-
413
- // Calculate transformation by traversing the hierarchy from the bottom (this frame) -> top (root frame)
414
- CMatrix localToObjTransform{};
415
- for (auto i = f; i && i != i->root ; i = RwFrameGetParent (i))
416
- {
417
- localToObjTransform = GetFrameCMatrix (i) * localToObjTransform;
418
- }
419
- const auto objToLocalTransform = localToObjTransform.Inverse ();
420
-
421
- const auto ObjectToLocalSpace = [&](const CVector& in) { return objToLocalTransform.TransformVector (in); };
422
-
423
- // Transform from object space, into local (the frame's) space
424
- const auto localOrigin = ObjectToLocalSpace (c->originOS );
425
- const auto localEnd = ObjectToLocalSpace (c->endOS );
426
-
427
- #if 0
428
- if (!CCollisionSA::TestLineSphere(
429
- CColLineSA{localOrigin, 0.f, localEnd, 0.f},
430
- reinterpret_cast<CColSphereSA&>(*RpAtomicGetBoundingSphere(a)) // Fine for now
431
- )) {
432
- return true; // Line segment doesn't touch bsp
433
- }
434
- #endif
435
- const auto localDir = localEnd - localOrigin;
436
-
437
- const auto verts = reinterpret_cast <CVector*>(geo->morph_target ->verts ); // It's fine, trust me bro
438
- for (auto i = geo->triangles_size ; i-- > 0 ;)
439
- {
440
- const auto tri = &geo->triangles [i];
441
-
442
- // Process the line against the triangle
443
- CVector hitBary, hitPos;
444
- if (!localOrigin.IntersectsSegmentTriangle (localDir, verts[tri->verts [0 ]], verts[tri->verts [1 ]], verts[tri->verts [2 ]], &hitPos, &hitBary))
445
- {
446
- continue ; // No intersection at all
447
- }
448
-
449
- // Intersection, check if it's closer than the previous one
450
- const auto hitDistSq = (hitPos - localOrigin).LengthSquared ();
451
- if (c->minHitDistSq > hitDistSq)
452
- {
453
- c->minHitDistSq = hitDistSq;
454
- c->hitGeo = geo;
455
- c->hitAtomic = a;
456
- c->hitTri = tri;
457
- c->hitBary = hitBary;
458
- c->hitPosOS = localToObjTransform.TransformVector (hitPos); // Transform back into object space
459
- }
460
- }
461
-
462
- return true ;
463
- };
464
-
465
- if (targetEntity->m_pRwObject ->object .type == 2 /* rpCLUMP*/ )
466
- {
467
- RpClumpForAllAtomics (targetEntity->m_pRwObject , ProcessOneAtomic, &c);
468
- }
469
- else
470
- { // Object is a single atomic, so process directly
471
- ProcessOneAtomic (reinterpret_cast <RpAtomic*>(targetEntity->m_pRwObject ), &c);
472
- }
473
-
474
- // It might be false if the collision model differs from the clump
475
- // This is completely normal as collisions models are meant to be simplified
476
- // compared to the clump's geometry
477
- if (outMatInfo->valid = c.hitGeo != nullptr )
478
- {
479
- // Now, calculate texture UV, etc based on the hit [if we've hit anything at all]
480
- // Since we have the barycentric coords of the hit, calculating it is easy
481
- outMatInfo->uv = {};
482
- for (auto i = 3u ; i-- > 0 ;)
483
- {
484
- // UV set index - Usually models only use level 0 indices, so let's stick with that
485
- const auto uvSetIdx = 0 ;
486
-
487
- // Vertex's UV position
488
- const auto vtxUV = &c.hitGeo ->texcoords [uvSetIdx][c.hitTri ->verts [i]];
489
-
490
- // Now, just interpolate
491
- outMatInfo->uv += CVector2D{vtxUV->u , vtxUV->v } * c.hitBary [i];
492
- }
493
-
494
- // Find out material texture name
495
- // For some reason this is sometimes null
496
- const auto tex = c.hitGeo ->materials .materials [c.hitTri ->materialId ]->texture ;
497
- outMatInfo->textureName = tex ? tex->name : nullptr ;
498
-
499
- const auto frame = RpAtomicGetFrame (c.hitAtomic ); // `RpAtomicGetFrame`
500
- outMatInfo->frameName = frame ? frame->szName : nullptr ;
501
-
502
- // Get hit position in world space
503
- outMatInfo->hitPos = c.entMat .TransformVector (c.hitPosOS );
520
+ // There might not be a texture hit info result as the collision model differs from the mesh itself.
521
+ // This is completely normal as collisions models are meant to be simplified
522
+ // compared to the mesh
523
+ *outMatInfo = ProcessLineAgainstMesh (targetEntity, *vecStart, *vecEnd);
504
524
}
505
525
}
506
526
0 commit comments