@@ -20,7 +20,8 @@ import type {
2020 IBasePhysicsCollisionEvent ,
2121 ConstrainedBodyPair ,
2222} from "../IPhysicsEnginePlugin" ;
23- import type { IRaycastQuery , PhysicsRaycastResult } from "../../physicsRaycastResult" ;
23+ import type { IRaycastQuery } from "../../physicsRaycastResult" ;
24+ import { PhysicsRaycastResult } from "../../physicsRaycastResult" ;
2425import { Logger } from "../../../Misc/logger" ;
2526import type { PhysicsBody } from "../physicsBody" ;
2627import type { PhysicsConstraint , Physics6DoFConstraint } from "../physicsConstraint" ;
@@ -272,6 +273,13 @@ class TriggerEvent {
272273 }
273274}
274275
276+ export interface HavokPluginParameters {
277+ /**
278+ * Maximum number of raycast hits to process
279+ */
280+ maxQueryCollectorHits ?: number ;
281+ }
282+
275283/**
276284 * The Havok Physics plugin
277285 */
@@ -292,7 +300,9 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
292300 * We only have a single raycast in-flight right now
293301 */
294302 private _queryCollector ;
303+ private _multiQueryCollector : any = undefined ;
295304 private _fixedTimeStep : number = 1 / 60 ;
305+ private _maxQueryCollectorHits : number = 1 ;
296306 private _tmpVec3 = BuildArray ( 3 , Vector3 . Zero ) ;
297307 private _bodies = new Map < bigint , { body : PhysicsBody ; index : number } > ( ) ;
298308 private _shapes = new Map < bigint , PhysicsShape > ( ) ;
@@ -316,7 +326,8 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
316326
317327 public constructor (
318328 private _useDeltaForWorldStep : boolean = true ,
319- hpInjection : any = HK
329+ hpInjection : any = HK ,
330+ parameters : HavokPluginParameters = { }
320331 ) {
321332 if ( typeof hpInjection === "function" ) {
322333 Logger . Error ( "Havok is not ready. Please make sure you await HK() before using the plugin." ) ;
@@ -330,7 +341,9 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
330341 return ;
331342 }
332343 this . world = this . _hknp . HP_World_Create ( ) [ 1 ] ;
344+
333345 this . _queryCollector = this . _hknp . HP_QueryCollector_Create ( 1 ) [ 1 ] ;
346+ this . setMaxQueryCollectorHits ( parameters . maxQueryCollectorHits ?? 1 ) ;
334347 }
335348 /**
336349 * If this plugin is supported
@@ -370,6 +383,35 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
370383 return this . _fixedTimeStep ;
371384 }
372385
386+ /**
387+ * Sets the maximum number of raycast hits to process.
388+ *
389+ * @param maxQueryCollectorHits - The maximum number of raycast hits to process.
390+ */
391+ public setMaxQueryCollectorHits ( maxQueryCollectorHits : number ) : void {
392+ if ( maxQueryCollectorHits === this . _maxQueryCollectorHits ) {
393+ return ;
394+ }
395+
396+ if ( this . _multiQueryCollector ) {
397+ this . _hknp . HP_QueryCollector_Release ( this . _multiQueryCollector ) ;
398+ this . _multiQueryCollector = undefined ;
399+ }
400+
401+ if ( maxQueryCollectorHits > 1 ) {
402+ this . _multiQueryCollector = this . _hknp . HP_QueryCollector_Create ( maxQueryCollectorHits ) [ 1 ] ;
403+ }
404+ }
405+
406+ /**
407+ * Gets the maximum number of raycast hits to process.
408+ *
409+ * @returns The maximum number of raycast hits to process.
410+ */
411+ public getMaxQueryCollectorHits ( ) : number {
412+ return this . _maxQueryCollectorHits ;
413+ }
414+
373415 /**
374416 * Executes a single step of the physics engine.
375417 *
@@ -2106,28 +2148,65 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
21062148 *
21072149 * @param from - The start point of the raycast.
21082150 * @param to - The end point of the raycast.
2109- * @param result - The PhysicsRaycastResult object to store the result of the raycast.
2151+ * @param result - The PhysicsRaycastResult object (or array of PhysicsRaycastResults) to store the result of the raycast.
21102152 * @param query - The raycast query options. See [[IRaycastQuery]] for more information.
21112153 *
21122154 * Performs a raycast. It takes in two points, from and to, and a PhysicsRaycastResult object to store the result of the raycast.
21132155 * It then performs the raycast and stores the hit data in the PhysicsRaycastResult object.
2156+ * If result is an empty array, it will be populated with every detected raycast hit.
2157+ * If result is a populated array, it will only fill the PhysicsRaycastResults present in the array.
21142158 */
2115- public raycast ( from : Vector3 , to : Vector3 , result : PhysicsRaycastResult , query ?: IRaycastQuery ) : void {
2159+ public raycast ( from : Vector3 , to : Vector3 , result : PhysicsRaycastResult | Array < PhysicsRaycastResult > , query ?: IRaycastQuery ) : void {
21162160 const queryMembership = query ?. membership ?? ~ 0 ;
21172161 const queryCollideWith = query ?. collideWith ?? ~ 0 ;
21182162 const shouldHitTriggers = query ?. shouldHitTriggers ?? false ;
2163+ const bodyToIgnore = query ?. ignoreBody ? [ BigInt ( query . ignoreBody . _pluginData . hpBodyId [ 0 ] ) ] : [ BigInt ( 0 ) ] ;
21192164
2120- result . reset ( from , to ) ;
2165+ const results = Array . isArray ( result ) ? result : [ result ] ;
2166+ for ( const raycastResult of results ) {
2167+ raycastResult . reset ( from , to ) ;
2168+ }
21212169
2122- const bodyToIgnore = query ?. ignoreBody ? [ BigInt ( query . ignoreBody . _pluginData . hpBodyId [ 0 ] ) ] : [ BigInt ( 0 ) ] ;
21232170 const hkQuery = [ this . _bVecToV3 ( from ) , this . _bVecToV3 ( to ) , [ queryMembership , queryCollideWith ] , shouldHitTriggers , bodyToIgnore ] ;
2124- this . _hknp . HP_World_CastRayWithCollector ( this . world , this . _queryCollector , hkQuery ) ;
2171+ const queryCollector = results . length === 1 || ! this . _multiQueryCollector ? this . _queryCollector : this . _multiQueryCollector ;
2172+ this . _hknp . HP_World_CastRayWithCollector ( this . world , queryCollector , hkQuery ) ;
21252173
2126- if ( this . _hknp . HP_QueryCollector_GetNumHits ( this . _queryCollector ) [ 1 ] > 0 ) {
2127- const [ , hitData ] = this . _hknp . HP_QueryCollector_GetCastRayResult ( this . _queryCollector , 0 ) [ 1 ] ;
2174+ const numHits = this . _hknp . HP_QueryCollector_GetNumHits ( queryCollector ) [ 1 ] ;
2175+ if ( numHits <= 0 ) {
2176+ return ;
2177+ }
21282178
2129- this . _populateHitData ( hitData , result ) ;
2130- result . calculateHitDistance ( ) ;
2179+ if ( ! results . length ) {
2180+ for ( let i = 0 ; i < numHits ; i ++ ) {
2181+ const raycastResult = new PhysicsRaycastResult ( ) ;
2182+ raycastResult . reset ( from , to ) ;
2183+ results . push ( raycastResult ) ;
2184+ }
2185+ }
2186+
2187+ // QueryCollector results are not sorted by distance, so we need to sort them manually
2188+ const hitDatas : Array < { hitData : any ; distance : number } > = new Array ( numHits ) ;
2189+ for ( let i = 0 ; i < numHits ; i ++ ) {
2190+ const [ , hitData ] = this . _hknp . HP_QueryCollector_GetCastRayResult ( queryCollector , i ) [ 1 ] ;
2191+
2192+ const hitPos = hitData [ 3 ] ;
2193+ from . subtractFromFloatsToRef ( hitPos [ 0 ] , hitPos [ 1 ] , hitPos [ 2 ] , this . _tmpVec3 [ 0 ] ) ;
2194+ const distance = this . _tmpVec3 [ 0 ] . lengthSquared ( ) ;
2195+
2196+ hitDatas [ i ] = {
2197+ hitData,
2198+ distance : distance ,
2199+ } ;
2200+ }
2201+
2202+ hitDatas . sort ( ( a , b ) => a . distance - b . distance ) ;
2203+
2204+ for ( let i = 0 ; i < Math . min ( numHits , results . length ) ; i ++ ) {
2205+ const raycastResult = results [ i ] ;
2206+ const hitData = hitDatas [ i ] ;
2207+
2208+ this . _populateHitData ( hitData . hitData , raycastResult ) ;
2209+ raycastResult . setHitDistance ( Math . sqrt ( hitData . distance ) ) ;
21312210 }
21322211 }
21332212
@@ -2404,6 +2483,10 @@ export class HavokPlugin implements IPhysicsEnginePluginV2 {
24042483 this . _hknp . HP_QueryCollector_Release ( this . _queryCollector ) ;
24052484 this . _queryCollector = undefined ;
24062485 }
2486+ if ( this . _multiQueryCollector ) {
2487+ this . _hknp . HP_QueryCollector_Release ( this . _multiQueryCollector ) ;
2488+ this . _multiQueryCollector ;
2489+ }
24072490 if ( this . world ) {
24082491 this . _hknp . HP_World_Release ( this . world ) ;
24092492 this . world = undefined ;
0 commit comments