Initial commit

This commit is contained in:
Joshua Ashton 2022-08-28 22:31:01 +01:00
commit 11adbb57bd
59 changed files with 11926 additions and 0 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
# This file is here so GitHub uses 4-space tabs...
root = true
[*]
indent_style = tab
indent_size = 4

52
.github/workflows/artifacts-windows.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Build Artifacts for Windows
on: [push, pull_request, workflow_dispatch]
jobs:
build-set-windows:
runs-on: windows-2022
steps:
- name: Checkout Mini Source SDK 2013
id: checkout-minisdk
uses: actions/checkout@v3
with:
repository: 'Joshua-Ashton/mini-source-sdk-2013'
- name: Checkout VPhysics Jolt
id: checkout-code
uses: actions/checkout@v3
with:
path: 'mp/src/vphysics_jolt'
submodules: recursive
- name: Find Visual Studio
id: find-vs
shell: pwsh
run: |
$installationPath = Get-VSSetupInstance `
| Select-VSSetupInstance -Require Microsoft.VisualStudio.Workload.NativeDesktop -Latest `
| Select-Object -ExpandProperty InstallationPath
Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" `
| Out-File -FilePath "${Env:GITHUB_ENV}" -Append
- name: Build MSVC x86
id: build
shell: pwsh
working-directory: 'mp/src'
run: |
& "${Env:COMSPEC}" /s /c "`"${Env:VSDEVCMD}`" -arch=x86 -host_arch=x64 -no_logo && set" `
| % { , ($_ -Split '=', 2) } `
| % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) }
.\fix_registry.bat
.\createjoltprojects.bat
devenv jolt.sln /upgrade
msbuild jolt.sln /nodeReuse:false /t:Rebuild /p:Configuration=Release /p:Platform=x86 /m /v:minimal
- name: Upload artifacts
id: upload-artifacts
uses: actions/upload-artifact@v2
with:
name: vphysics_jolt_sdk2013_win32
path: mp/game
if-no-files-found: error

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "joltphysics/src"]
path = joltphysics/src
url = https://github.com/jrouwe/JoltPhysics/

7
LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright 2022 Joshua Ashton and Josh Dowell (Slartibarty)
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.

100
README.md Normal file
View File

@ -0,0 +1,100 @@
![VPhysics Jolt Logo](assets/cube_base_nobg.png "VPhysics Jolt")
## What is Volt? ⚡
Volt (VPhysics Jolt) is a replacement for Source's VPhysics which uses IVP/Havok<br>
Created by [Joshua Ashton (🐸✨)](https://github.com/Joshua-Ashton) [@phys_ballsocket](https://twitter.com/phys_ballsocket) and [Josh Dowell (Slartibarty)](https://github.com/Slartibarty) [@Slartbarty](https://twitter.com/Slartbarty).
Volt is designed to be incredibly high performance, supporting thousands of objects at once without bringing tick/framerate down to a crawl.
In our testing, the performance overhead from having thousands of objects moving at once, now comes from the client code needing to update/render, as going out of the PVS of these objects will still cause them to be simulated, but will no longer be rendered.
## Features
Volt is mostly feature complete, but is missing some things such as support for raycast vehicles, breakable constraints, and perhaps other things we've missed!
It is our goal to implement any missing features at some point.
Below is a feature table of Volt vs VPhysics and the Bullet VPhysics project.
If we missed anything we don't support or we do, feel free to add to it.
It is not meant to be a bash on anyone elses work however, the Bullet VPhysics project was a great inspiration to us!
| Feature | VPhysics | Volt (VPhysics Jolt) | Bullet VPhysics |
|:--------------|:--------:|:-------------:|:---------------:|
| Constraints (except Pulleys) | ✔️ | ✔️ | ✔️ |
| Pulleys | ✔️ | ❌ | ❌ |
| Breakable constraints | ✔️ | ❌ | ❌ |
| Motors (Motion Controllers) | ✔️ | ✔️ | ✔️ |
| Motors (Constraint) | ✔️ | ✔️ | ❌ |
| Ragdolls | ✔️ | ✔️ (some bugs) | ✔️ |
| Triggers | ✔️ | ✔️ | ❌ |
| Object touch callbacks | ✔️ | ✔️ | ❌ |
| Prop damage/breaking | ✔️ | ✔️ | ❌ |
| Fluid events | ✔️ | ✔️ | ❌ |
| Prop splashing effects | ✔️ | ✔️ | ❌ |
| Wheeled Vehicles | ✔️ | ✔️ | ✔️ |
| Raycast Vehicles (ie. Airboat) | ✔️ | ❌ | 〰️ (janky) |
| NPCs/Doors (Shadow Controllers) | ✔️ | ✔️ | ✔️ |
| Save/Restore Support | ✔️ | ✔️ | ❌ |
| Portal Support | ✔️ | ✔️ | ❌ |
| Game per-object collide callback support<br>eg. no-collide | ✔️ | ✔️ | ❌ |
| Crash-resistant solver | ❌ | ✔️ | (no data) |
| Supports thousands of objects without lag | ❌ | ✔️ | ❌ |
| Multithreaded | ❌ | ✔️ | ✔️ (partially) |
| Proper player controller | ✔️ | ❌ (needs work!) | ✔️ (partially) |
## Bugs
VPhysics Jolt is not without its flaws, however. See the [issue tracker](https://github.com/Joshua-Ashton/VPhysics-Jolt/issues) for bugs that are known.
There are definitely going to be bugs that we don't know about or haven't encountered, or different quirks across engine branches.
## How to build
The Volt code is provided as-is, it is up to you to build it for your SDK, etc.
Volt should build fine against Source SDK 2013 and Alien Swarm SDK on a MSVC or GCC compiler with at least C++20 support.
*If you are building directly against the public SDK 2013 and Alien Swarm SDKs, you will need to do minor work to memoverride.cpp to make it compatible with the newer compilers and newer Windows SDKs.*
Unfortunately we cannot redistribute the additional code/headers needed to build Volt for games such as Garry's Mod which uses a different VPhysics interface (CS:GO's) to what is found in the Valve-provided public SDKs.
## Download
For each release, binary builds are provided for Garry's Mod and Source SDK 2013 on the [Releases](https://github.com/Joshua-Ashton/VPhysics-Jolt/releases/) page.
## Media
### Lots of Melons + Dumpster
[![Lots of Melons + Dumpster](https://img.youtube.com/vi/gPDQkmfQCsc/0.jpg)](https://www.youtube.com/watch?v=gPDQkmfQCsc "Lots of Melons + Dumpster")
### Physically Simulated Chain
[![Physically Simulated Chain](https://img.youtube.com/vi/tVmQTmbSJM0/0.jpg)](https://www.youtube.com/watch?v=tVmQTmbSJM0 "Physically Simulated Chain")
### Lots of Balls Test
[![Lots of Balls Test](https://img.youtube.com/vi/tYfiTyRtmz8/0.jpg)](https://www.youtube.com/watch?v=tYfiTyRtmz8 "Lots of Balls Test")
### Wheels + Weld Car Dupe Test
[![Weld Car Dupe Test](https://img.youtube.com/vi/5_QbbXbIrg8/0.jpg)](https://www.youtube.com/watch?v=5_QbbXbIrg8 "Weld Car Dupe Test")
### Door + NPC (Physics Shadowed Objects) Test
[![Door + NPC (Physics Shadowed Objects) Test](https://img.youtube.com/vi/SdEj7HTuJmU/0.jpg)](https://www.youtube.com/watch?v=SdEj7HTuJmU "Door + NPC (Physics Shadowed Objects) Test")
### Lots of Cubes + Ragdolls + Funnel
[![Lots of Cubes + Ragdolls + Funnel](https://img.youtube.com/vi/CLVnSwg33Dk/0.jpg)](https://www.youtube.com/watch?v=CLVnSwg33Dk "Lots of Cubes + Ragdolls + Funnel")
### Slow Mo Cubes
[![Slow Mo Cubes](https://img.youtube.com/vi/GzW_4bufwEk/0.jpg)](https://www.youtube.com/watch?v=GzW_4bufwEk "Slow Mo Cubes")
### Propane in Dumpster
[![Propane in Dumpster](https://img.youtube.com/vi/10vvRJVHGQc/0.jpg)](https://www.youtube.com/watch?v=10vvRJVHGQc "Propane in Dumpster")
*Have some cool media of stuff going on in Volt you'd like to add? Feel free to make a pull request!*
## Projects using Volt
### [Portal 2: Desolation](https://emberspark.games/desolation/)
### [Prelude Online](https://prelude.online/)
# Have fun! 🐸⚡

BIN
assets/cube_base_nobg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

416
joltphysics/joltphysics.vpc Normal file
View File

@ -0,0 +1,416 @@
//-----------------------------------------------------------------------------
// JOLTPHYSICS.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR "..\.."
$Macro OUTLIBNAME "joltphysics"
$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Include "joltphysics_settings.vpc"
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Use Precompiled Header (/Yu)"
$Create/UsePCHThroughFile "Jolt/Jolt.h"
}
}
$Project "JoltPhysics"
{
$Folder "Precompiled Header"
{
$File "pch.cpp"
{
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Create Precompiled Header (/Yc)"
}
}
}
}
$Folder "Source Files"
{
$File "src\Jolt\AABBTree\AABBTreeBuilder.cpp"
$File "src\Jolt\AABBTree\AABBTreeBuilder.h"
$File "src\Jolt\AABBTree\AABBTreeToBuffer.h"
$File "src\Jolt\AABBTree\NodeCodec\NodeCodecQuadTreeHalfFloat.h"
$File "src\Jolt\AABBTree\TriangleCodec\TriangleCodecIndexed8BitPackSOA4Flags.h"
$File "src\Jolt\Core\Atomics.h"
$File "src\Jolt\Core\ByteBuffer.h"
$File "src\Jolt\Core\Color.cpp"
$File "src\Jolt\Core\Color.h"
$File "src\Jolt\Core\Core.h"
$File "src\Jolt\Core\Factory.cpp"
$File "src\Jolt\Core\Factory.h"
$File "src\Jolt\Core\FixedSizeFreeList.h"
$File "src\Jolt\Core\FixedSizeFreeList.inl"
$File "src\Jolt\Core\FPControlWord.h"
$File "src\Jolt\Core\FPException.h"
$File "src\Jolt\Core\FPFlushDenormals.h"
$File "src\Jolt\Core\HashCombine.h"
$File "src\Jolt\Core\IssueReporting.cpp"
$File "src\Jolt\Core\IssueReporting.h"
$File "src\Jolt\Core\JobSystem.h"
$File "src\Jolt\Core\JobSystem.inl"
$File "src\Jolt\Core\JobSystemThreadPool.cpp"
$File "src\Jolt\Core\JobSystemThreadPool.h"
$File "src\Jolt\Core\LinearCurve.cpp"
$File "src\Jolt\Core\LinearCurve.h"
$File "src\Jolt\Core\LockFreeHashMap.h"
$File "src\Jolt\Core\LockFreeHashMap.inl"
$File "src\Jolt\Core\Memory.cpp"
$File "src\Jolt\Core\Memory.h"
$File "src\Jolt\Core\Mutex.h"
$File "src\Jolt\Core\MutexArray.h"
$File "src\Jolt\Core\NonCopyable.h"
$File "src\Jolt\Core\Profiler.cpp"
$File "src\Jolt\Core\Profiler.h"
$File "src\Jolt\Core\Profiler.inl"
$File "src\Jolt\Core\Reference.h"
$File "src\Jolt\Core\Result.h"
$File "src\Jolt\Core\RTTI.cpp"
$File "src\Jolt\Core\RTTI.h"
$File "src\Jolt\Core\StaticArray.h"
$File "src\Jolt\Core\StreamIn.h"
$File "src\Jolt\Core\StreamOut.h"
$File "src\Jolt\Core\StreamWrapper.h"
$File "src\Jolt\Core\StringTools.cpp"
$File "src\Jolt\Core\StringTools.h"
$File "src\Jolt\Core\TempAllocator.h"
$File "src\Jolt\Core\TickCounter.cpp"
$File "src\Jolt\Core\TickCounter.h"
$File "src\Jolt\Geometry\AABox.h"
$File "src\Jolt\Geometry\AABox4.h"
$File "src\Jolt\Geometry\ClipPoly.h"
$File "src\Jolt\Geometry\ClosestPoint.h"
$File "src\Jolt\Geometry\ConvexHullBuilder.cpp"
$File "src\Jolt\Geometry\ConvexHullBuilder.h"
$File "src\Jolt\Geometry\ConvexHullBuilder2D.cpp"
$File "src\Jolt\Geometry\ConvexHullBuilder2D.h"
$File "src\Jolt\Geometry\ConvexSupport.h"
$File "src\Jolt\Geometry\Ellipse.h"
$File "src\Jolt\Geometry\EPAConvexHullBuilder.h"
$File "src\Jolt\Geometry\EPAPenetrationDepth.h"
$File "src\Jolt\Geometry\GJKClosestPoint.h"
$File "src\Jolt\Geometry\IndexedTriangle.h"
$File "src\Jolt\Geometry\Indexify.cpp"
$File "src\Jolt\Geometry\Indexify.h"
$File "src\Jolt\Geometry\MortonCode.h"
$File "src\Jolt\Geometry\OrientedBox.cpp"
$File "src\Jolt\Geometry\OrientedBox.h"
$File "src\Jolt\Geometry\Plane.h"
$File "src\Jolt\Geometry\RayAABox.h"
$File "src\Jolt\Geometry\RayAABox8.h"
$File "src\Jolt\Geometry\RayCapsule.h"
$File "src\Jolt\Geometry\RayCylinder.h"
$File "src\Jolt\Geometry\RaySphere.h"
$File "src\Jolt\Geometry\RayTriangle.h"
$File "src\Jolt\Geometry\RayTriangle8.h"
$File "src\Jolt\Geometry\Sphere.h"
$File "src\Jolt\Geometry\Triangle.h"
$File "src\Jolt\Jolt.h"
$File "src\Jolt\Math\DVec3.h"
$File "src\Jolt\Math\DVec3.inl"
$File "src\Jolt\Math\EigenValueSymmetric.h"
$File "src\Jolt\Math\FindRoot.h"
$File "src\Jolt\Math\Float2.h"
$File "src\Jolt\Math\Float3.h"
$File "src\Jolt\Math\Float4.h"
$File "src\Jolt\Math\GaussianElimination.h"
$File "src\Jolt\Math\HalfFloat.h"
$File "src\Jolt\Math\Mat44.h"
$File "src\Jolt\Math\Mat44.inl"
$File "src\Jolt\Math\Math.h"
$File "src\Jolt\Math\MathTypes.h"
$File "src\Jolt\Math\Matrix.h"
$File "src\Jolt\Math\Quat.h"
$File "src\Jolt\Math\Quat.inl"
$File "src\Jolt\Math\Swizzle.h"
$File "src\Jolt\Math\UVec4.cpp"
$File "src\Jolt\Math\UVec4.h"
$File "src\Jolt\Math\UVec4.inl"
$File "src\Jolt\Math\UVec8.h"
$File "src\Jolt\Math\UVec8.inl"
$File "src\Jolt\Math\Vec3.cpp"
$File "src\Jolt\Math\Vec3.h"
$File "src\Jolt\Math\Vec3.inl"
$File "src\Jolt\Math\Vec4.h"
$File "src\Jolt\Math\Vec4.inl"
$File "src\Jolt\Math\Vec8.h"
$File "src\Jolt\Math\Vec8.inl"
$File "src\Jolt\Math\Vector.h"
$File "src\Jolt\ObjectStream\GetPrimitiveTypeOfType.h"
$File "src\Jolt\ObjectStream\ObjectStream.cpp"
$File "src\Jolt\ObjectStream\ObjectStream.h"
$File "src\Jolt\ObjectStream\ObjectStreamBinaryIn.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamBinaryIn.h"
$File "src\Jolt\ObjectStream\ObjectStreamBinaryOut.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamBinaryOut.h"
$File "src\Jolt\ObjectStream\ObjectStreamIn.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamIn.h"
$File "src\Jolt\ObjectStream\ObjectStreamOut.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamOut.h"
$File "src\Jolt\ObjectStream\ObjectStreamTextIn.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamTextIn.h"
$File "src\Jolt\ObjectStream\ObjectStreamTextOut.cpp"
$File "src\Jolt\ObjectStream\ObjectStreamTextOut.h"
$File "src\Jolt\ObjectStream\ObjectStreamTypes.h"
$File "src\Jolt\ObjectStream\SerializableAttribute.h"
$File "src\Jolt\ObjectStream\SerializableAttributeEnum.h"
$File "src\Jolt\ObjectStream\SerializableAttributeTyped.h"
$File "src\Jolt\ObjectStream\SerializableObject.cpp"
$File "src\Jolt\ObjectStream\SerializableObject.h"
$File "src\Jolt\ObjectStream\TypeDeclarations.cpp"
$File "src\Jolt\ObjectStream\TypeDeclarations.h"
$File "src\Jolt\Physics\Body\Body.cpp"
$File "src\Jolt\Physics\Body\Body.h"
$File "src\Jolt\Physics\Body\Body.inl"
$File "src\Jolt\Physics\Body\BodyAccess.cpp"
$File "src\Jolt\Physics\Body\BodyAccess.h"
$File "src\Jolt\Physics\Body\BodyActivationListener.h"
$File "src\Jolt\Physics\Body\BodyCreationSettings.cpp"
$File "src\Jolt\Physics\Body\BodyCreationSettings.h"
$File "src\Jolt\Physics\Body\BodyFilter.h"
$File "src\Jolt\Physics\Body\BodyID.h"
$File "src\Jolt\Physics\Body\BodyInterface.cpp"
$File "src\Jolt\Physics\Body\BodyInterface.h"
$File "src\Jolt\Physics\Body\BodyLock.h"
$File "src\Jolt\Physics\Body\BodyLockInterface.h"
$File "src\Jolt\Physics\Body\BodyLockMulti.h"
$File "src\Jolt\Physics\Body\BodyManager.cpp"
$File "src\Jolt\Physics\Body\BodyManager.h"
$File "src\Jolt\Physics\Body\BodyPair.h"
$File "src\Jolt\Physics\Body\MassProperties.cpp"
$File "src\Jolt\Physics\Body\MassProperties.h"
$File "src\Jolt\Physics\Body\MotionProperties.cpp"
$File "src\Jolt\Physics\Body\MotionProperties.h"
$File "src\Jolt\Physics\Body\MotionProperties.inl"
$File "src\Jolt\Physics\Body\MotionQuality.h"
$File "src\Jolt\Physics\Body\MotionType.h"
$File "src\Jolt\Physics\Character\Character.cpp"
$File "src\Jolt\Physics\Character\Character.h"
$File "src\Jolt\Physics\Collision\AABoxCast.h"
$File "src\Jolt\Physics\Collision\ActiveEdgeMode.h"
$File "src\Jolt\Physics\Collision\ActiveEdges.h"
$File "src\Jolt\Physics\Collision\BackFaceMode.h"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhase.cpp"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhase.h"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseBruteForce.cpp"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseBruteForce.h"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseLayer.h"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseQuadTree.cpp"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseQuadTree.h"
$File "src\Jolt\Physics\Collision\BroadPhase\BroadPhaseQuery.h"
$File "src\Jolt\Physics\Collision\BroadPhase\QuadTree.cpp"
$File "src\Jolt\Physics\Collision\BroadPhase\QuadTree.h"
$File "src\Jolt\Physics\Collision\CastConvexVsTriangles.cpp"
$File "src\Jolt\Physics\Collision\CastConvexVsTriangles.h"
$File "src\Jolt\Physics\Collision\CastSphereVsTriangles.cpp"
$File "src\Jolt\Physics\Collision\CastSphereVsTriangles.h"
$File "src\Jolt\Physics\Collision\CastResult.h"
$File "src\Jolt\Physics\Collision\CollectFacesMode.h"
$File "src\Jolt\Physics\Collision\CollideConvexVsTriangles.cpp"
$File "src\Jolt\Physics\Collision\CollideConvexVsTriangles.h"
$File "src\Jolt\Physics\Collision\CollidePointResult.h"
$File "src\Jolt\Physics\Collision\CollideShape.h"
$File "src\Jolt\Physics\Collision\CollideSphereVsTriangles.cpp"
$File "src\Jolt\Physics\Collision\CollideSphereVsTriangles.h"
$File "src\Jolt\Physics\Collision\CollisionCollector.h"
$File "src\Jolt\Physics\Collision\CollisionCollectorImpl.h"
$File "src\Jolt\Physics\Collision\CollisionDispatch.cpp"
$File "src\Jolt\Physics\Collision\CollisionDispatch.h"
$File "src\Jolt\Physics\Collision\CollisionGroup.cpp"
$File "src\Jolt\Physics\Collision\CollisionGroup.h"
$File "src\Jolt\Physics\Collision\ContactListener.h"
$File "src\Jolt\Physics\Collision\GroupFilter.cpp"
$File "src\Jolt\Physics\Collision\GroupFilter.h"
$File "src\Jolt\Physics\Collision\GroupFilterTable.cpp"
$File "src\Jolt\Physics\Collision\GroupFilterTable.h"
$File "src\Jolt\Physics\Collision\ManifoldBetweenTwoFaces.cpp"
$File "src\Jolt\Physics\Collision\ManifoldBetweenTwoFaces.h"
$File "src\Jolt\Physics\Collision\NarrowPhaseQuery.cpp"
$File "src\Jolt\Physics\Collision\NarrowPhaseQuery.h"
$File "src\Jolt\Physics\Collision\NarrowPhaseStats.cpp"
$File "src\Jolt\Physics\Collision\NarrowPhaseStats.h"
$File "src\Jolt\Physics\Collision\ObjectLayer.h"
$File "src\Jolt\Physics\Collision\PhysicsMaterial.cpp"
$File "src\Jolt\Physics\Collision\PhysicsMaterial.h"
$File "src\Jolt\Physics\Collision\PhysicsMaterialSimple.cpp"
$File "src\Jolt\Physics\Collision\PhysicsMaterialSimple.h"
$File "src\Jolt\Physics\Collision\RayCast.h"
$File "src\Jolt\Physics\Collision\Shape\BoxShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\BoxShape.h"
$File "src\Jolt\Physics\Collision\Shape\CapsuleShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\CapsuleShape.h"
$File "src\Jolt\Physics\Collision\Shape\CompoundShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\CompoundShape.h"
$File "src\Jolt\Physics\Collision\Shape\CompoundShapeVisitors.h"
$File "src\Jolt\Physics\Collision\Shape\ConvexHullShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\ConvexHullShape.h"
$File "src\Jolt\Physics\Collision\Shape\ConvexShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\ConvexShape.h"
$File "src\Jolt\Physics\Collision\Shape\CylinderShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\CylinderShape.h"
$File "src\Jolt\Physics\Collision\Shape\DecoratedShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\DecoratedShape.h"
$File "src\Jolt\Physics\Collision\Shape\GetTrianglesContext.h"
$File "src\Jolt\Physics\Collision\Shape\HeightFieldShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\HeightFieldShape.h"
$File "src\Jolt\Physics\Collision\Shape\MeshShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\MeshShape.h"
$File "src\Jolt\Physics\Collision\Shape\MutableCompoundShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\MutableCompoundShape.h"
$File "src\Jolt\Physics\Collision\Shape\OffsetCenterOfMassShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\OffsetCenterOfMassShape.h"
$File "src\Jolt\Physics\Collision\Shape\PolyhedronSubmergedVolumeCalculator.h"
$File "src\Jolt\Physics\Collision\Shape\RotatedTranslatedShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\RotatedTranslatedShape.h"
$File "src\Jolt\Physics\Collision\Shape\ScaledShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\ScaledShape.h"
$File "src\Jolt\Physics\Collision\Shape\ScaleHelpers.h"
$File "src\Jolt\Physics\Collision\Shape\Shape.cpp"
$File "src\Jolt\Physics\Collision\Shape\Shape.h"
$File "src\Jolt\Physics\Collision\Shape\SphereShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\SphereShape.h"
$File "src\Jolt\Physics\Collision\Shape\StaticCompoundShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\StaticCompoundShape.h"
$File "src\Jolt\Physics\Collision\Shape\SubShapeID.h"
$File "src\Jolt\Physics\Collision\Shape\SubShapeIDPair.h"
$File "src\Jolt\Physics\Collision\Shape\TaperedCapsuleShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\TaperedCapsuleShape.gliffy"
$File "src\Jolt\Physics\Collision\Shape\TaperedCapsuleShape.h"
$File "src\Jolt\Physics\Collision\Shape\TriangleShape.cpp"
$File "src\Jolt\Physics\Collision\Shape\TriangleShape.h"
$File "src\Jolt\Physics\Collision\ShapeCast.h"
$File "src\Jolt\Physics\Collision\ShapeFilter.h"
$File "src\Jolt\Physics\Collision\SortReverseAndStore.h"
$File "src\Jolt\Physics\Collision\TransformedShape.cpp"
$File "src\Jolt\Physics\Collision\TransformedShape.h"
$File "src\Jolt\Physics\Constraints\ConeConstraint.cpp"
$File "src\Jolt\Physics\Constraints\ConeConstraint.h"
$File "src\Jolt\Physics\Constraints\Constraint.cpp"
$File "src\Jolt\Physics\Constraints\Constraint.h"
$File "src\Jolt\Physics\Constraints\ConstraintManager.cpp"
$File "src\Jolt\Physics\Constraints\ConstraintManager.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\AngleConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\AxisConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\DualAxisConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\GearConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\HingeRotationConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\PointConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\RackAndPinionConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\RotationEulerConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\RotationQuatConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\SpringPart.h"
$File "src\Jolt\Physics\Constraints\ConstraintPart\SwingTwistConstraintPart.h"
$File "src\Jolt\Physics\Constraints\ContactConstraintManager.cpp"
$File "src\Jolt\Physics\Constraints\ContactConstraintManager.h"
$File "src\Jolt\Physics\Constraints\DistanceConstraint.cpp"
$File "src\Jolt\Physics\Constraints\DistanceConstraint.h"
$File "src\Jolt\Physics\Constraints\FixedConstraint.cpp"
$File "src\Jolt\Physics\Constraints\FixedConstraint.h"
$File "src\Jolt\Physics\Constraints\GearConstraint.cpp"
$File "src\Jolt\Physics\Constraints\GearConstraint.h"
$File "src\Jolt\Physics\Constraints\HingeConstraint.cpp"
$File "src\Jolt\Physics\Constraints\HingeConstraint.h"
$File "src\Jolt\Physics\Constraints\MotorSettings.cpp"
$File "src\Jolt\Physics\Constraints\MotorSettings.h"
$File "src\Jolt\Physics\Constraints\PathConstraint.cpp"
$File "src\Jolt\Physics\Constraints\PathConstraint.h"
$File "src\Jolt\Physics\Constraints\PathConstraintPath.cpp"
$File "src\Jolt\Physics\Constraints\PathConstraintPath.h"
$File "src\Jolt\Physics\Constraints\PathConstraintPathHermite.cpp"
$File "src\Jolt\Physics\Constraints\PathConstraintPathHermite.h"
$File "src\Jolt\Physics\Constraints\PointConstraint.cpp"
$File "src\Jolt\Physics\Constraints\PointConstraint.h"
$File "src\Jolt\Physics\Constraints\RackAndPinionConstraint.cpp"
$File "src\Jolt\Physics\Constraints\RackAndPinionConstraint.h"
$File "src\Jolt\Physics\Constraints\SixDOFConstraint.cpp"
$File "src\Jolt\Physics\Constraints\SixDOFConstraint.h"
$File "src\Jolt\Physics\Constraints\SliderConstraint.cpp"
$File "src\Jolt\Physics\Constraints\SliderConstraint.h"
$File "src\Jolt\Physics\Constraints\SwingTwistConstraint.cpp"
$File "src\Jolt\Physics\Constraints\SwingTwistConstraint.h"
$File "src\Jolt\Physics\Constraints\TwoBodyConstraint.cpp"
$File "src\Jolt\Physics\Constraints\TwoBodyConstraint.h"
$File "src\Jolt\Physics\EActivation.h"
$File "src\Jolt\Physics\IslandBuilder.cpp"
$File "src\Jolt\Physics\IslandBuilder.h"
$File "src\Jolt\Physics\PhysicsLock.cpp"
$File "src\Jolt\Physics\PhysicsLock.h"
$File "src\Jolt\Physics\PhysicsScene.cpp"
$File "src\Jolt\Physics\PhysicsScene.h"
$File "src\Jolt\Physics\PhysicsSettings.h"
$File "src\Jolt\Physics\PhysicsStepListener.h"
$File "src\Jolt\Physics\PhysicsSystem.cpp"
$File "src\Jolt\Physics\PhysicsSystem.h"
$File "src\Jolt\Physics\PhysicsUpdateContext.cpp"
$File "src\Jolt\Physics\PhysicsUpdateContext.h"
$File "src\Jolt\Physics\Ragdoll\Ragdoll.cpp"
$File "src\Jolt\Physics\Ragdoll\Ragdoll.h"
$File "src\Jolt\Physics\StateRecorder.h"
$File "src\Jolt\Physics\StateRecorderImpl.cpp"
$File "src\Jolt\Physics\StateRecorderImpl.h"
$File "src\Jolt\Physics\Vehicle\TrackedVehicleController.cpp"
$File "src\Jolt\Physics\Vehicle\TrackedVehicleController.h"
$File "src\Jolt\Physics\Vehicle\VehicleAntiRollBar.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleAntiRollBar.h"
$File "src\Jolt\Physics\Vehicle\VehicleCollisionTester.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleCollisionTester.h"
$File "src\Jolt\Physics\Vehicle\VehicleConstraint.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleConstraint.h"
$File "src\Jolt\Physics\Vehicle\VehicleController.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleController.h"
$File "src\Jolt\Physics\Vehicle\VehicleDifferential.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleDifferential.h"
$File "src\Jolt\Physics\Vehicle\VehicleEngine.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleEngine.h"
$File "src\Jolt\Physics\Vehicle\VehicleTrack.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleTrack.h"
$File "src\Jolt\Physics\Vehicle\VehicleTransmission.cpp"
$File "src\Jolt\Physics\Vehicle\VehicleTransmission.h"
$File "src\Jolt\Physics\Vehicle\Wheel.cpp"
$File "src\Jolt\Physics\Vehicle\Wheel.h"
$File "src\Jolt\Physics\Vehicle\WheeledVehicleController.cpp"
$File "src\Jolt\Physics\Vehicle\WheeledVehicleController.h"
$File "src\Jolt\RegisterTypes.cpp"
$File "src\Jolt\RegisterTypes.h"
$File "src\Jolt\Renderer\DebugRenderer.cpp"
$File "src\Jolt\Renderer\DebugRenderer.h"
$File "src\Jolt\Renderer\DebugRendererPlayback.cpp"
$File "src\Jolt\Renderer\DebugRendererPlayback.h"
$File "src\Jolt\Renderer\DebugRendererRecorder.cpp"
$File "src\Jolt\Renderer\DebugRendererRecorder.h"
$File "src\Jolt\Skeleton\SkeletalAnimation.cpp"
$File "src\Jolt\Skeleton\SkeletalAnimation.h"
$File "src\Jolt\Skeleton\Skeleton.cpp"
$File "src\Jolt\Skeleton\Skeleton.h"
$File "src\Jolt\Skeleton\SkeletonPose.cpp"
$File "src\Jolt\Skeleton\SkeletonPose.h"
$File "src\Jolt\TriangleGrouper\TriangleGrouper.h"
$File "src\Jolt\TriangleGrouper\TriangleGrouperClosestCentroid.cpp"
$File "src\Jolt\TriangleGrouper\TriangleGrouperClosestCentroid.h"
$File "src\Jolt\TriangleGrouper\TriangleGrouperMorton.cpp"
$File "src\Jolt\TriangleGrouper\TriangleGrouperMorton.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitter.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitter.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitterBinning.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitterBinning.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitterFixedLeafSize.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitterFixedLeafSize.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitterLongestAxis.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitterLongestAxis.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitterMean.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitterMean.h"
$File "src\Jolt\TriangleSplitter\TriangleSplitterMorton.cpp"
$File "src\Jolt\TriangleSplitter\TriangleSplitterMorton.h"
}
}

View File

@ -0,0 +1,22 @@
//
// Configuration settings for Jolt Physics
//
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;$SRCDIR\vphysics_jolt\joltphysics\src"
$PreprocessorDefinitions "$BASE;JPH_DISABLE_CUSTOM_ALLOCATOR;JPH_DEBUG_RENDERER"
$PreprocessorDefinitions "$BASE;JPH_ENABLE_ASSERTS" [$DEVELOPMENT_ONLY]
//$PreprocessorDefinitions "$BASE;JPH_PROFILE_ENABLED" [$DEVELOPMENT_ONLY]
// CPU feature macros, we may want to tune these before release.
$PreprocessorDefinitions "$BASE;JPH_USE_SSE4_1;JPH_USE_SSE4_2;JPH_USE_LZCNT;JPH_USE_TZCNT;JPH_USE_F16C;JPH_USE_FMADD"
// We DO want to comment this line out for release, AVX 1 & 2 adoption isn't reliable enough yet
// $EnableEnhancedInstructionSet "Advanced Vector Extensions 2 (/arch:AVX2)" [$DEVELOPMENT_ONLY]
}
}

1
joltphysics/pch.cpp Normal file
View File

@ -0,0 +1 @@
#include "Jolt/Jolt.h"

1
joltphysics/src Submodule

@ -0,0 +1 @@
Subproject commit 22d675437edc441329ed244f632807a8789fa5a6

2
vphysics_jolt/cbase.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "cbase.h"

114
vphysics_jolt/cbase.h Normal file
View File

@ -0,0 +1,114 @@
//=================================================================================================
//
// This is the precompiled header
//
//=================================================================================================
#pragma once
// Tier0
#include "tier0/basetypes.h"
#include "tier0/dbg.h"
#ifndef GAME_SDK2013
#include "tier0/logging.h"
#endif
#ifdef GAME_SDK2013
#include "compat/compat_sdk2013.h"
#endif
#include "compat/branch_overrides.h"
// STD
// Ensure cmath is included everywhere
// so we get those sweet overloaded maths functions
#include <cstdlib>
#include <cmath>
// STL
#include <array>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <fstream>
// Mathlib
#include "mathlib/mathlib.h"
#include "mathlib/vector.h"
// Tier1
#include "tier1/tier1.h"
#include "tier1/strtools.h"
#include "tier1/interface.h"
#include "tier1/keyvalues.h"
#include "tier1/UtlStringMap.h"
#include "tier1/utlbuffer.h"
// Misc
#ifdef JPH_DEBUG_RENDERER
#include "engine/ivdebugoverlay.h"
#endif
#include "bspfile.h"
#include "cmodel.h"
#include "const.h"
#include "isaverestore.h"
#include "vcollide_parse.h"
// VPhysics Interface
#include "vphysics_interface.h"
#include "vphysics/collision_set.h"
#include "vphysics/constraints.h"
#include "vphysics/friction.h"
#include "vphysics/object_hash.h"
#include "vphysics/performance.h"
#include "vphysics/player_controller.h"
#include "vphysics/stats.h"
#include "vphysics/vehicles.h"
#include "vphysics/virtualmesh.h"
// Jolt
#include <Jolt/Jolt.h>
#include <Jolt/RegisterTypes.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Core/Factory.h>
#include <Jolt/Skeleton/Skeleton.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/PhysicsScene.h>
#include <Jolt/Physics/Body/Body.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Collision/RayCast.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Physics/Collision/CollidePointResult.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
#include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
#include <Jolt/Physics/Ragdoll/Ragdoll.h>
#include <Jolt/Physics/Collision/ShapeCast.h>
#include <Jolt/Physics/Collision/CollisionDispatch.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Collision/GroupFilter.h>
#include <Jolt/Physics/Collision/GroupFilterTable.h>
#include <Jolt/Physics/Constraints/ConeConstraint.h>
#include <Jolt/Physics/Constraints/PointConstraint.h>
#include <Jolt/Physics/Constraints/HingeConstraint.h>
#include <Jolt/Physics/Constraints/SwingTwistConstraint.h>
#include <Jolt/Physics/Constraints/FixedConstraint.h>
#include <Jolt/Physics/Constraints/SixDOFConstraint.h>
#include <Jolt/Physics/Constraints/DistanceConstraint.h>
#include <Jolt/Physics/Constraints/SliderConstraint.h>
#include <Jolt/Physics/Vehicle/VehicleConstraint.h>
#include <Jolt/Physics/Vehicle/VehicleCollisionTester.h>
#include <Jolt/Physics/Vehicle/WheeledVehicleController.h>
// Ourselves
#include "vjolt_interface.h"
#include "vjolt_util.h"

View File

@ -0,0 +1,39 @@
#pragma once
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOWINRES
#define NOSERVICE
#define NOMCX
#define NOIME
#define NOCRYPT
#define NOMETAFILE
#define NOMINMAX
#define MMNOSOUND
#include <windows.h>
#undef PostMessage
#undef CreateEvent
#undef PlaySound
#undef SetCursor
#undef ProgressBox
#undef RegisterClass
#undef AddJob
#undef GetJob
#undef Yield
#undef SetPort
#undef CreateFont
#undef ShellExecute
#undef ShellExecuteEx
#undef GetCurrentTime
#undef GetTickCount
#undef GetCurrentDirectory
#undef GetObject
#undef LoadCursorFromFile
#undef GetCharABCWidths
#endif

View File

@ -0,0 +1,38 @@
#pragma once
#if defined( GAME_CSGO ) || defined( GAME_DESOLATION )
#define GAME_CSGO_OR_NEWER
#define override_csgo override
#define override_not_csgo
#else
#define override_csgo
#define override_not_csgo override
#endif
#if defined( GAME_CSGO ) || defined( GAME_DESOLATION ) || defined( GAME_PORTAL2 )
#define GAME_PORTAL2_OR_NEWER
#define override_portal2 override
#define override_not_portal2
#else
#define override_portal2
#define override_not_portal2 override
#endif
#if defined( GAME_CSGO ) || defined( GAME_DESOLATION ) || defined( GAME_PORTAL2 ) || defined( GAME_L4D2 ) || defined( GAME_ASW )
#define GAME_ASW_OR_NEWER
#define override_asw override
#define override_not_asw
#else
#define override_asw
#define override_not_asw override
#endif
#ifndef GAME_DESOLATION
using strlen_t = int;
#endif
#ifndef GAME_CSGO_OR_NEWER
#define FastASCIIToUpper( c ) ( ( ( (c) >= 'a' ) && ( (c) <= 'z' ) ) ? ( (c) - 32 ) : (c) )
#define FastASCIIToLower( c ) ( ( ( (c) >= 'A' ) && ( (c) <= 'Z' ) ) ? ( (c) + 32 ) : (c) )
#endif

View File

@ -0,0 +1,75 @@
#pragma once
// This file provides compatibility with stuff used from the Alien Swarm SDK and above
// Source Engine branches for SDK 2013.
#include "Color.h"
enum LoggingSeverity_t
{
LS_MESSAGE = 0,
LS_WARNING = 1,
LS_ASSERT = 2,
LS_ERROR = 3,
LS_HIGHEST_SEVERITY = 4,
};
struct LoggingChannelInfo_t
{
const char *pszName;
int nFlags;
LoggingSeverity_t nSeverity;
Color color = Color(255, 255, 255);
};
#define DECLARE_LOGGING_CHANNEL( Channel ) extern LoggingChannelInfo_t g_LoggingInfo##Channel;
#define DEFINE_LOGGING_CHANNEL_NO_TAGS( Channel, ChannelName, ... ) LoggingChannelInfo_t g_LoggingInfo##Channel = { ChannelName, __VA_ARGS__ };
static const int MAX_LOGGING_MESSAGE_LENGTH = 2048;
// TODO
#define DevAssert( ... )
#define DevAssertMsg( ... )
#define AssertMsg_Internal( ... )
#define InternalMsg( Channel, Fmt, ... ) ConColorMsg( g_LoggingInfo##Channel.color, "[%s]" Fmt, g_LoggingInfo##Channel.pszName, ##__VA_ARGS__ )
#define Log_Msg( Channel, Fmt, ... ) InternalMsg( Channel, Fmt, ##__VA_ARGS__ )
#define Log_Warning( Channel, Fmt, ... ) InternalMsg( Channel, Fmt, ##__VA_ARGS__ )
#define Log_Error( Channel, Fmt, ... ) InternalMsg( Channel, Fmt, ##__VA_ARGS__ )
// Unused, just makes stuff cleaner to not have ifdef spam.
enum collisionhints
{
COLLISION_HINT_DEBRIS = 0x0001,
COLLISION_HINT_STATICSOLID = 0x0002,
};
class IPhysicsCollisionSet;
class IPhysics;
struct ragdollcollisionrules_t
{
void Defaults( IPhysics *pPhysics, IPhysicsCollisionSet *pSetIn )
{
pCollisionSet = pSetIn;
bSelfCollisions = true;
}
int bSelfCollisions;
IPhysicsCollisionSet *pCollisionSet;
};
struct ragdollanimatedfriction_t
{
float minFriction;
float maxFriction;
float timeIn;
float timeOut;
float timeHold;
};
enum PlayerContactState_t
{
PLAYER_CONTACT_PHYSICS = 1,
PLAYER_CONTACT_GAMEOBJECT = 2,
};

View File

@ -0,0 +1,25 @@
#pragma once
#include "compat/better_winlite.h"
#ifdef _MSC_VER
#define VJOLT_RETURN_ADDRESS() _ReturnAddress()
#else
#define VJOLT_RETURN_ADDRESS() __builtin_return_address(0)
#endif
FORCEINLINE void GetCallingFunctionModulePath( void *pReturnAddress, char *pszModulePath, size_t len )
{
#ifdef _WIN32
MEMORY_BASIC_INFORMATION mbi;
if ( ::VirtualQuery( pReturnAddress, &mbi, sizeof(mbi)) ) {
HMODULE module = reinterpret_cast< HMODULE >( mbi.AllocationBase );
::GetModuleFileNameA( module, pszModulePath, DWORD( len ) );
return;
}
#else
V_strncpy( pszModulePath, "Unknown", len);
#endif
}

View File

@ -0,0 +1,927 @@
#include "cbase.h"
#include "coordsize.h"
#include "mathlib/polyhedron.h"
#include "vjolt_parse.h"
#include "vjolt_querymodel.h"
#include "vjolt_collide.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
// Also in vjolt_collide_trace.cpp, should unify or just remove entirely
static constexpr float kMaxConvexRadius = JPH::cDefaultConvexRadius;
JoltPhysicsCollision JoltPhysicsCollision::s_PhysicsCollision;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( JoltPhysicsCollision, IPhysicsCollision, VPHYSICS_COLLISION_INTERFACE_VERSION, JoltPhysicsCollision::GetInstance() );
//-------------------------------------------------------------------------------------------------
// Creates a shape from shape settings, resolving the reference
template < typename ShapeType, typename T >
ShapeType *ShapeSettingsToShape( const T& settings )
{
auto result = settings.Create();
if ( !result.IsValid() )
{
// Josh:
// Need to handle degenerate convexes and stuff.
const char *error = result.HasError()
? result.GetError().c_str()
: "Unknown";
Log_Warning( LOG_VJolt, "Failed to create shape: %s.\n", error );
return nullptr;
}
return static_cast< ShapeType * >( ToDanglingRef( result.Get() ) );
}
// Creates a JPH::ConvexShape from shape settings, and casts to a CPhysConvex
template < typename T >
CPhysConvex *ShapeSettingsToPhysConvex( const T& settings )
{
return CPhysConvex::FromConvexShape( ShapeSettingsToShape< JPH::ConvexShape >( settings ) );
}
// Creates a JPH::Shape from shape settings, and casts to a CPhysCollide
template < typename T >
CPhysCollide *ShapeSettingsToPhysCollide( const T& settings )
{
return CPhysCollide::FromShape( ShapeSettingsToShape< JPH::Shape >( settings ) );
}
//-------------------------------------------------------------------------------------------------
CPhysConvex *JoltPhysicsCollision::ConvexFromVerts( Vector **pVerts, int vertCount )
{
// This uses this and not a CUtlVector or std::vector or whatever for two reasons:
// 1: This is a single allocation we can then toss away instead of growing
// 2: We do not want to initialize these vectors on allocation
// 3: Automatic deletion when out of scope
// Please do not change me to either.
std::unique_ptr< JPH::Vec3[] > verts = std::make_unique< JPH::Vec3[] >( vertCount );
for ( int i = 0; i < vertCount; i++ )
verts[ i ] = SourceToJolt::Distance( *pVerts[ i ] );
JPH::ConvexHullShapeSettings settings( verts.get(), vertCount, kMaxConvexRadius, nullptr /* material */ );
settings.mHullTolerance = 0.0f;
return ShapeSettingsToPhysConvex( settings );
}
CPhysConvex *JoltPhysicsCollision::ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance )
{
Log_Stub( LOG_VJolt );
return nullptr;
}
float JoltPhysicsCollision::ConvexVolume( CPhysConvex *pConvex )
{
return JoltToSource::Volume( pConvex->ToConvexShape()->GetVolume() );
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsCollision::ConvexSurfaceArea( CPhysConvex *pConvex )
{
Log_Stub( LOG_VJolt );
return 0.0f;
}
void JoltPhysicsCollision::SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData )
{
pConvex->ToConvexShape()->SetUserData( gameData );
}
void JoltPhysicsCollision::ConvexFree( CPhysConvex *pConvex )
{
pConvex->ToConvexShape()->Release();
}
CPhysConvex *JoltPhysicsCollision::BBoxToConvex( const Vector &mins, const Vector &maxs )
{
JPH::AABox aabox = SourceToJolt::AABBBounds( mins, maxs );
JPH::BoxShape *pBoxShape = new JPH::BoxShape( aabox.GetExtent(), kMaxConvexRadius, nullptr /* material */ );
JPH::RotatedTranslatedShapeSettings rotatedSettings( aabox.mMin + aabox.GetExtent(), JPH::Quat::sIdentity(), pBoxShape );
return ShapeSettingsToPhysConvex( rotatedSettings );
}
CPhysConvex *JoltPhysicsCollision::ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron )
{
std::unique_ptr< JPH::Vec3[] > pPoints = std::make_unique< JPH::Vec3[] >( ConvexPolyhedron.iVertexCount );
// This loop fills me with rage
for ( unsigned short i = 0; i < ConvexPolyhedron.iVertexCount; ++i )
pPoints[i] = SourceToJolt::Distance( ConvexPolyhedron.pVertices[i] );
JPH::ConvexHullShapeSettings settings( pPoints.get(), ConvexPolyhedron.iVertexCount, kMaxConvexRadius, nullptr /* material */);
settings.mHullTolerance = 0.0f; // Slart: Otherwise some polyhedrons crash :(
CPhysConvex *pPhysConvex = ShapeSettingsToPhysConvex( settings );
return pPhysConvex;
}
void JoltPhysicsCollision::ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput )
{
// Slart: Unused
Log_Stub( LOG_VJolt );
}
//-------------------------------------------------------------------------------------------------
CPhysPolysoup *JoltPhysicsCollision::PolysoupCreate()
{
return new CPhysPolysoup;
}
void JoltPhysicsCollision::PolysoupDestroy( CPhysPolysoup *pSoup )
{
delete pSoup;
}
void JoltPhysicsCollision::PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits )
{
// Add both windings to make this two-faced.
pSoup->Triangles.push_back( JPH::Triangle( SourceToJolt::DistanceFloat3( c ), SourceToJolt::DistanceFloat3( b ), SourceToJolt::DistanceFloat3( a ) ) );
pSoup->Triangles.push_back( JPH::Triangle( SourceToJolt::DistanceFloat3( a ), SourceToJolt::DistanceFloat3( b ), SourceToJolt::DistanceFloat3( c ) ) );
}
CPhysCollide *JoltPhysicsCollision::ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP )
{
VJoltAssertMsg( !useMOPP, "MOPPs not supported\n" );
if ( useMOPP )
return nullptr;
// ConvertPolysoupToCollide does NOT free the Polysoup.
JPH::MeshShapeSettings settings( pSoup->Triangles );
return ShapeSettingsToPhysCollide( settings );
}
//-------------------------------------------------------------------------------------------------
CPhysCollide *JoltPhysicsCollision::ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount )
{
// If we only have one convex shape, we can just use that directly,
// without making a compound shape.
if ( convexCount == 1 )
return pConvex[0]->ToPhysCollide();
JPH::StaticCompoundShapeSettings settings;
for ( int i = 0; i < convexCount; i++ )
settings.AddShape( JPH::Vec3::sZero(), JPH::Quat::sIdentity(), pConvex[i]->ToConvexShape() );
CPhysCollide *pCollide = ShapeSettingsToPhysCollide( settings );
// This function also 'frees' the convexes.
for ( int i = 0; i < convexCount; i++ )
pConvex[i]->ToConvexShape()->Release();
return pCollide;
}
CPhysCollide *JoltPhysicsCollision::ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams )
{
// Slart: The parameters are just IVP crap and the only one that isn't is never ever used. HA!
return ConvertConvexToCollide( pConvex, convexCount );
}
void JoltPhysicsCollision::DestroyCollide( CPhysCollide *pCollide )
{
if ( !pCollide )
return;
pCollide->ToShape()->Release();
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsCollision::CollideSize( CPhysCollide *pCollide )
{
Log_Stub( LOG_VJolt );
return 0;
}
int JoltPhysicsCollision::CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap /*= false*/ )
{
Log_Stub( LOG_VJolt );
return 0;
}
CPhysCollide *JoltPhysicsCollision::UnserializeCollide( char *pBuffer, int size, int index )
{
Log_Stub( LOG_VJolt );
return nullptr;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsCollision::CollideVolume( CPhysCollide *pCollide )
{
return JoltToSource::Volume( pCollide->ToShape()->GetVolume() );
}
float JoltPhysicsCollision::CollideSurfaceArea( CPhysCollide *pCollide )
{
Log_Stub( LOG_VJolt );
return 0.0f;
}
//-------------------------------------------------------------------------------------------------
Vector JoltPhysicsCollision::CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction )
{
if ( !pCollide )
return collideOrigin;
const JPH::Shape *pShape = pCollide->ToShape();
JPH::Vec3 vecDirection = JPH::Vec3( direction.x, direction.y, direction.z );
JPH::Mat44 matCollideTransform = JPH::Mat44::sRotationTranslation(
SourceToJolt::Angle( collideAngles ), SourceToJolt::Distance( collideOrigin ) - pShape->GetCenterOfMass() );
float flMaxDot = -FLT_MAX;
JPH::Vec3 vecMaxExtent = JPH::Vec3::sZero();
ActOnSubShapes< JPH::ConvexShape >( pShape, [&]( const JPH::ConvexShape* pConvexShape, JPH::Mat44Arg matSubShapeTransform )
{
JPH::ConvexShape::SupportingFace supportingFace;
pConvexShape->GetSupportingFace( vecDirection, JPH::Vec3::sReplicate( 1.0f ), supportingFace );
for ( const JPH::Vec3 &vecVertex : supportingFace )
{
JPH::Vec3 vecTransformedVertex = matCollideTransform * matSubShapeTransform * vecVertex;
const float flDot = vecTransformedVertex.Dot( vecDirection );
if ( flDot > flMaxDot )
{
vecMaxExtent = vecTransformedVertex;
flMaxDot = flDot;
}
}
});
return JoltToSource::Distance( vecMaxExtent );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsCollision::CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles )
{
JPH::Vec3 position = SourceToJolt::Distance( collideOrigin );
JPH::Quat rotation = SourceToJolt::Angle( collideAngles );
const JPH::Shape *pShape = pCollide->ToShape();
const JPH::Mat44 translation = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() );
const JPH::AABox worldBounds = pShape->GetWorldSpaceBounds( translation, JPH::Vec3::sReplicate( 1.0f ) );
JoltToSource::AABBBounds( worldBounds, *pMins, *pMaxs );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsCollision::CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter )
{
*pOutMassCenter = JoltToSource::Distance( pCollide->ToShape()->GetCenterOfMass() );
}
void JoltPhysicsCollision::CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter )
{
// Slart: Only used in studiomdl
Log_Stub( LOG_VJolt );
}
Vector JoltPhysicsCollision::CollideGetOrthographicAreas( const CPhysCollide *pCollide )
{
// Slart: Only used in studiomdl... In a part that is #if 0'd out...
Log_Stub( LOG_VJolt );
return vec3_origin;
}
void JoltPhysicsCollision::CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas )
{
// Slart: Never used
Log_Stub( LOG_VJolt );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsCollision::CollideIndex( const CPhysCollide *pCollide )
{
// Slart: Only used by code behind #ifdef _DEBUG
Log_Stub( LOG_VJolt );
return 0;
}
//-------------------------------------------------------------------------------------------------
CPhysCollide *JoltPhysicsCollision::BBoxToCollide( const Vector &mins, const Vector &maxs )
{
return BBoxToConvex( mins, maxs )->ToPhysCollide();
}
int JoltPhysicsCollision::GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit )
{
const JPH::Shape *pShape = pCollideable->ToShape();
JPH::EShapeType shapeType = pShape->GetType();
if ( shapeType != JPH::EShapeType::Compound )
{
pOutputArray[0] = const_cast<CPhysConvex *>( CPhysConvex::FromConvexShape( static_cast<const JPH::ConvexShape *>( pShape ) ) );
return 1;
}
const JPH::StaticCompoundShape *pCompoundShape = static_cast<const JPH::StaticCompoundShape *>( pShape );
const JPH::StaticCompoundShape::SubShapes &subShapes = pCompoundShape->GetSubShapes();
const uint maxNumShapes = Min<uint>( pCompoundShape->GetNumSubShapes(), iOutputArrayLimit );
for ( uint i = 0; i < maxNumShapes; ++i )
pOutputArray[i] = const_cast<CPhysConvex *>( CPhysConvex::FromConvexShape( static_cast<const JPH::ConvexShape *>( subShapes[i].mShape.GetPtr() ) ) );
return maxNumShapes;
}
//-------------------------------------------------------------------------------------------------
bool JoltPhysicsCollision::IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone )
{
// Slart: Never used
Log_Stub( LOG_VJolt );
return false;
}
//-------------------------------------------------------------------------------------------------
//
// See studiobyteswap from the public 2013 SDK for more info about these defines.
// https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/common/studiobyteswap.cpp
//
namespace ivp_compat
{
struct collideheader_t
{
int size;
int vphysicsID;
short version;
short modelType;
};
struct compactsurfaceheader_t
{
int surfaceSize;
Vector dragAxisAreas;
int axisMapSize;
};
struct moppsurfaceheader_t
{
int moppSize;
};
struct compactsurface_t
{
float mass_center[3];
float rotation_inertia[3];
float upper_limit_radius;
unsigned int max_factor_surface_deviation : 8;
int byte_size : 24;
int offset_ledgetree_root;
int dummy[3];
};
struct compactmopp_t
{
float mass_center[3];
float rotation_inertia[3];
float upper_limit_radius;
unsigned int max_factor_surface_deviation : 8;
int byte_size : 24;
int offset_ledgetree_root;
int offset_ledges;
int size_convex_hull;
int dummy;
};
struct compactledge_t
{
int c_point_offset;
union
{
int ledgetree_node_offset;
int client_data;
};
struct
{
uint has_children_flag : 2;
int is_compact_flag : 2;
uint dummy : 4;
uint size_div_16 : 24;
};
short n_triangles;
short for_future_use;
};
struct compactedge_t
{
uint start_point_index : 16;
int opposite_index : 15;
uint is_virtual : 1;
};
struct compacttriangle_t
{
uint tri_index : 12;
uint pierce_index : 12;
uint material_index : 7;
uint is_virtual : 1;
compactedge_t c_three_edges[3];
};
struct compactledgenode_t
{
int offset_right_node;
int offset_compact_ledge;
float center[3];
float radius;
unsigned char box_sizes[3];
unsigned char free_0;
const compactledge_t *GetCompactLedge() const
{
VJoltAssert( this->offset_right_node == 0 );
return ( compactledge_t * )( ( char * )this + this->offset_compact_ledge );
}
const compactledgenode_t *GetLeftChild() const
{
VJoltAssert( this->offset_right_node );
return this + 1;
}
const compactledgenode_t *GetRightChild() const
{
VJoltAssert( this->offset_right_node );
return ( compactledgenode_t * )( ( char * )this + this->offset_right_node );
}
bool IsTerminal() const
{
return this->offset_right_node == 0;
}
const compactledge_t *GetCompactHull() const
{
if ( this->offset_compact_ledge )
return ( compactledge_t * )( ( char * )this + this->offset_compact_ledge );
else
return nullptr;
}
};
static constexpr int IVP_COMPACT_SURFACE_ID = MAKEID('I','V','P','S');
static constexpr int IVP_COMPACT_SURFACE_ID_SWAPPED = MAKEID('S','P','V','I');
static constexpr int IVP_COMPACT_MOPP_ID = MAKEID('M','O','P','P');
static constexpr int VPHYSICS_COLLISION_ID = MAKEID('V','P','H','Y');
static constexpr short VPHYSICS_COLLISION_VERSION = 0x0100;
enum
{
COLLIDE_POLY = 0,
COLLIDE_MOPP = 1,
COLLIDE_BALL = 2,
COLLIDE_VIRTUAL = 3,
};
JPH::ConvexShape *IVPLedgeToConvexShape( const compactledge_t *pLedge )
{
if ( !pLedge->n_triangles )
return nullptr;
const char *pVertices = reinterpret_cast< const char * >( pLedge ) + pLedge->c_point_offset;
const compacttriangle_t *pTriangles = reinterpret_cast< const compacttriangle_t * >( pLedge + 1 );
const int nVertCount = pLedge->n_triangles * 3;
std::unique_ptr< JPH::Vec3[] > verts = std::make_unique< JPH::Vec3[] >( nVertCount );
// Each triangle
for ( int i = 0; i < pLedge->n_triangles; i++ )
{
// For each point of the current triangle
for ( int j = 0; j < 3; j++ )
{
static constexpr size_t IVPAlignedVectorSize = 16;
const int nIndex = pTriangles[ i ].c_three_edges[ j ].start_point_index;
const float *pVertex = reinterpret_cast< const float * >( pVertices + ( nIndex * IVPAlignedVectorSize ) );
verts[ ( i * 3 ) + j ] = JPH::Vec3( pVertex[ 0 ], pVertex[ 2 ], -pVertex[ 1 ] );
}
}
JPH::ConvexHullShapeSettings settings{ verts.get(), nVertCount, kMaxConvexRadius, nullptr /* material */ };
settings.mHullTolerance = 0.0f;
JPH::ConvexShape *pConvexShape = ShapeSettingsToShape< JPH::ConvexShape >( settings );
if ( !pConvexShape )
return nullptr;
pConvexShape->SetUserData( pLedge->client_data );
return pConvexShape;
}
void GetAllIVPEdges( const compactledgenode_t *pNode, CUtlVector< const compactledge_t * >& vecOut )
{
if ( !pNode )
return;
if ( !pNode->IsTerminal() )
{
GetAllIVPEdges( pNode->GetRightChild(), vecOut );
GetAllIVPEdges( pNode->GetLeftChild(), vecOut );
}
else
vecOut.AddToTail( pNode->GetCompactLedge() );
}
CPhysCollide *DeserializeIVP_Poly( const collideheader_t *pCollideHeader )
{
const compactsurfaceheader_t *pSurfaceHeader = reinterpret_cast< const compactsurfaceheader_t* >( pCollideHeader + 1 );
const compactsurface_t *pSurface = reinterpret_cast< const compactsurface_t* >( pSurfaceHeader + 1 );
if ( pSurface->dummy[2] != IVP_COMPACT_SURFACE_ID )
return nullptr;
const compactledgenode_t *pFirstLedgeNode = reinterpret_cast< const compactledgenode_t * >(
reinterpret_cast< const char * >( pSurface ) + pSurface->offset_ledgetree_root );
CUtlVector< const compactledge_t * > ledges;
GetAllIVPEdges( pFirstLedgeNode, ledges );
VJoltAssert( !ledges.IsEmpty() );
if ( ledges.Count() != 1 )
{
JPH::StaticCompoundShapeSettings settings{};
// One compound convex per ledge.
for ( int i = 0; i < ledges.Count(); i++ )
{
const JPH::Shape* pShape = IVPLedgeToConvexShape( ledges[i] );
// Josh:
// Some models have degenerate convexes which fails to make
// a subshape in Jolt, so we need to ignore those ledges.
if ( pShape )
settings.AddShape( JPH::Vec3::sZero(), JPH::Quat::sIdentity(), pShape );
}
CPhysCollide* pCollide = ShapeSettingsToPhysCollide( settings );
return pCollide;
}
else
{
JPH::ConvexShape *pShape = IVPLedgeToConvexShape( ledges[ 0 ] );
return CPhysConvex::FromConvexShape( pShape )->ToPhysCollide();
}
}
}
void JoltPhysicsCollision::VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int size, bool swap /*= false*/ )
{
if ( swap )
{
Log_Error( LOG_VJolt, "If you got here. Tell me what you did!\n" );
return;
}
pOutput->solidCount = solidCount;
pOutput->solids = new CPhysCollide*[ solidCount ];
const char *pCursor = pBuffer;
for ( int i = 0; i < solidCount; i++ )
{
// Be safe ahead of time as so much can go wrong with
// this mess! :p
pOutput->solids[ i ] = nullptr;
const ivp_compat::collideheader_t *pCollideHeader = reinterpret_cast<const ivp_compat::collideheader_t *>( pCursor );
if ( pCollideHeader->vphysicsID != ivp_compat::VPHYSICS_COLLISION_ID ||
pCollideHeader->version != ivp_compat::VPHYSICS_COLLISION_VERSION )
{
Log_Warning( LOG_VJolt, "Skipped solid %d due to invalid header (id: %.4s, version: 0x%x)\n", i, &pCollideHeader->vphysicsID, pCollideHeader->version );
continue;
}
switch ( pCollideHeader->modelType )
{
case ivp_compat::COLLIDE_POLY:
pOutput->solids[ i ] = DeserializeIVP_Poly( pCollideHeader );
break;
case ivp_compat::COLLIDE_MOPP:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_MOPP on solid %d. Skipping...\n", i );
break;
case ivp_compat::COLLIDE_BALL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_BALL on solid %d. Skipping...\n", i );
break;
case ivp_compat::COLLIDE_VIRTUAL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_VIRTUAL on solid %d. Skipping...\n", i );
break;
default:
Log_Warning( LOG_VJolt, "Unsupported solid type 0x%x on solid %d. Skipping...\n", (int)pCollideHeader->modelType, i );
break;
}
// Size does not include the size of "size", ironically!
// add sizeof( int ) for that.
pCursor += pCollideHeader->size + sizeof( int );
}
// The rest of the buffer is KV.
const int keyValuesSize = size - ( uintp( pCursor ) - uintp( pBuffer ) );
pOutput->pKeyValues = new char[ keyValuesSize + 1 ];
V_memcpy( pOutput->pKeyValues, pCursor, keyValuesSize );
pOutput->pKeyValues[ keyValuesSize ] = '\0';
pOutput->descSize = keyValuesSize;
pOutput->isPacked = false;
#ifdef GAME_ASW_OR_NEWER
pOutput->pUserData = nullptr;
#endif
}
void JoltPhysicsCollision::VCollideUnload( vcollide_t *pVCollide )
{
VCollideFreeUserData( pVCollide );
for ( int i = 0; i < pVCollide->solidCount; i++ )
delete pVCollide->solids[ i ];
delete[] pVCollide->solids;
delete[] pVCollide->pKeyValues;
V_memset( pVCollide, 0, sizeof( *pVCollide ) );
}
//-------------------------------------------------------------------------------------------------
IVPhysicsKeyParser *JoltPhysicsCollision::VPhysicsKeyParserCreate( const char *pKeyData )
{
return CreateVPhysicsKeyParser( pKeyData, false );
}
IVPhysicsKeyParser *JoltPhysicsCollision::VPhysicsKeyParserCreate( vcollide_t *pVCollide )
{
return CreateVPhysicsKeyParser( pVCollide->pKeyValues, pVCollide->isPacked );
}
void JoltPhysicsCollision::VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser )
{
DestroyVPhysicsKeyParser( pParser );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsCollision::CreateDebugMesh( CPhysCollide const *pCollisionModel, Vector **outVerts )
{
const JPH::Shape *pShape = pCollisionModel->ToShape();
JPH::Shape::VisitedShapes visitedShapes;
JPH::Shape::Stats stats = pShape->GetStatsRecursive( visitedShapes );
const int nMaxTriCount = int( stats.mNumTriangles );
const int nRequestCount = Max( nMaxTriCount, JPH::Shape::cGetTrianglesMinTrianglesRequested );
const int nRequestVertCount = nRequestCount * 3;
Vector *pVerts = new Vector[ nRequestVertCount ];
JPH::AllHitCollisionCollector<JPH::TransformedShapeCollector> collector;
JPH::ShapeFilter filter;
pShape->CollectTransformedShapes( JPH::AABox::sBiggest(), pShape->GetCenterOfMass() * JoltToSource::Factor, JPH::Quat::sIdentity(), JPH::Vec3::sReplicate( JoltToSource::Factor ), JPH::SubShapeIDCreator(), collector, filter );
int nAccumTris = 0;
for ( auto &shape : collector.mHits )
{
JPH::Shape::GetTrianglesContext ctx;
shape.GetTrianglesStart( ctx, JPH::AABox::sBiggest() );
for ( ;; )
{
int nSubShapeTriCount = shape.GetTrianglesNext( ctx, nRequestCount, reinterpret_cast<JPH::Float3*>( &pVerts[ nAccumTris * 3 ] ), nullptr /* materials */);
if ( nSubShapeTriCount == 0 || nAccumTris + nSubShapeTriCount > nMaxTriCount )
break;
nAccumTris += nSubShapeTriCount;
}
}
*outVerts = pVerts;
return nAccumTris * 3;
}
void JoltPhysicsCollision::DestroyDebugMesh( int vertCount, Vector *outVerts )
{
delete[] outVerts;
}
//-------------------------------------------------------------------------------------------------
ICollisionQuery *JoltPhysicsCollision::CreateQueryModel( CPhysCollide *pCollide )
{
return new JoltCollisionQuery( pCollide->ToShape() );
}
void JoltPhysicsCollision::DestroyQueryModel( ICollisionQuery *pQuery )
{
delete pQuery;
}
//-------------------------------------------------------------------------------------------------
IPhysicsCollision *JoltPhysicsCollision::ThreadContextCreate()
{
return this;
}
void JoltPhysicsCollision::ThreadContextDestroy( IPhysicsCollision *pThreadContex )
{
// Does nothing in VPhysics.
}
//-------------------------------------------------------------------------------------------------
CPhysCollide *JoltPhysicsCollision::CreateVirtualMesh( const virtualmeshparams_t &params )
{
IVirtualMeshEvent *event = params.pMeshEventHandler;
virtualmeshlist_t meshList;
event->GetVirtualMesh( params.userData, &meshList );
JPH::VertexList vertexList;
vertexList.resize( meshList.vertexCount );
for ( int i = 0; i < meshList.vertexCount; ++i )
vertexList[i] = SourceToJolt::DistanceFloat3( meshList.pVerts[i] );
JPH::IndexedTriangleList indexedTriangleList;
indexedTriangleList.resize( meshList.indexCount * 2 );
for ( int i = 0; i < meshList.triangleCount; ++i )
{
// Add both windings to make this two-faced.
// Probably doesn't matter too much but matches what used to happen.
indexedTriangleList[i*2+0].mIdx[0] = meshList.indices[i*3+0];
indexedTriangleList[i*2+0].mIdx[1] = meshList.indices[i*3+1];
indexedTriangleList[i*2+0].mIdx[2] = meshList.indices[i*3+2];
indexedTriangleList[i*2+1].mIdx[2] = meshList.indices[i*3+0];
indexedTriangleList[i*2+1].mIdx[1] = meshList.indices[i*3+1];
indexedTriangleList[i*2+1].mIdx[0] = meshList.indices[i*3+2];
}
JPH::MeshShapeSettings settings( vertexList, indexedTriangleList );
return ShapeSettingsToPhysCollide( settings );
}
bool JoltPhysicsCollision::SupportsVirtualMesh()
{
return true;
}
//-------------------------------------------------------------------------------------------------
bool JoltPhysicsCollision::GetBBoxCacheSize( int *pCachedSize, int *pCachedCount )
{
// Josh: We don't use a bbox cache as we have box shapes directly,
// and this is only used for debug stats.
*pCachedSize = 0;
*pCachedCount = 0;
return true;
}
//-------------------------------------------------------------------------------------------------
CPolyhedron *JoltPhysicsCollision::PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron )
{
// This is vile
Log_Stub( LOG_VJolt );
return nullptr;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsCollision::OutputDebugInfo( const CPhysCollide *pCollide )
{
Log_Stub( LOG_VJolt );
}
unsigned int JoltPhysicsCollision::ReadStat( int statID )
{
// Josh:
// This always returns 0 in VPhysics.
// It was used by the HL2 Beta physgun at one point for...
// something...
return 0;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsCollision::CollideGetRadius( const CPhysCollide *pCollide )
{
return pCollide->ToShape()->GetInnerRadius();
}
//-------------------------------------------------------------------------------------------------
void *JoltPhysicsCollision::VCollideAllocUserData( vcollide_t *pVCollide, size_t userDataSize )
{
#ifdef GAME_ASW_OR_NEWER
VCollideFreeUserData( pVCollide );
if ( userDataSize )
pVCollide->pUserData = malloc( userDataSize );
return pVCollide->pUserData;
#else
return nullptr;
#endif
}
void JoltPhysicsCollision::VCollideFreeUserData( vcollide_t *pVCollide )
{
#ifdef GAME_ASW_OR_NEWER
if ( pVCollide->pUserData )
{
free( pVCollide->pUserData );
pVCollide->pUserData = nullptr;
}
#endif
}
void JoltPhysicsCollision::VCollideCheck( vcollide_t *pVCollide, const char *pName )
{
// Josh:
// A thing to spew warnings about non-optimal solids in IVP.
// Entirely useless for us.
}
bool JoltPhysicsCollision::TraceBoxAA( const Ray_t &ray, const CPhysCollide *pCollide, trace_t *ptr )
{
TraceBox( ray, pCollide, vec3_origin, vec3_angle, ptr );
return true;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsCollision::DuplicateAndScale( vcollide_t *pOut, const vcollide_t *pIn, float flScale )
{
CPhysCollide **pSolids = new CPhysCollide * [pIn->solidCount];
for ( unsigned short i = 0; i < pIn->solidCount; i++ )
{
const JPH::Shape* pShape = pIn->solids[i]->ToShape();
pSolids[i] = CPhysCollide::FromShape( ToDanglingRef( pShape->ScaleShape( JPH::Vec3::sReplicate( flScale ) ).Get() ) );
}
char *pKeyValues = new char[ pIn->descSize ];
V_memcpy( pKeyValues, pIn->pKeyValues, pIn->descSize );
*pOut = vcollide_t
{
.solidCount = pIn->solidCount,
.isPacked = pIn->isPacked,
.descSize = pIn->descSize,
.solids = pSolids,
.pKeyValues = pKeyValues,
#ifdef GAME_ASW_OR_NEWER
.pUserData = nullptr,
#endif
};
}
//-------------------------------------------------------------------------------------------------
const JPH::Shape *CreateCOMOverrideShape( const JPH::Shape* pShape, JPH::Vec3Arg comOverride )
{
JPH::Vec3 comOffset = comOverride - pShape->GetCenterOfMass();
if ( comOffset.IsNearZero() )
return pShape;
JPH::OffsetCenterOfMassShapeSettings settings( comOffset, pShape );
return ShapeSettingsToShape< JPH::OffsetCenterOfMassShape >( settings );
}

View File

@ -0,0 +1,185 @@
//=================================================================================================
//
// CPhysCollide and friends!
//
//=================================================================================================
#pragma once
//-------------------------------------------------------------------------------------------------
// Dummy helper class to go back and forth.
// Does not and will not contain *any* data.
class CPhysCollide
{
public:
JPH::Shape* ToShape()
{
return reinterpret_cast<JPH::Shape*>(this);
}
const JPH::Shape *ToShape() const
{
return reinterpret_cast< const JPH::Shape * >( this );
}
//-------------------------------------------------------------------------------------------------
static CPhysCollide *FromShape( JPH::Shape *pCollide )
{
return reinterpret_cast< CPhysCollide * >( pCollide );
}
static const CPhysCollide *FromShape( const JPH::Shape *pCollide )
{
return reinterpret_cast< const CPhysCollide * >( pCollide );
}
};
//-------------------------------------------------------------------------------------------------
// Dummy helper class to go back and forth.
// Does not and will not contain *any* data.
class CPhysConvex
{
public:
JPH::ConvexShape* ToConvexShape()
{
return reinterpret_cast< JPH::ConvexShape* >( this );
}
const JPH::ConvexShape *ToConvexShape() const
{
return reinterpret_cast< const JPH::ConvexShape * >( this );
}
//-------------------------------------------------------------------------------------------------
static CPhysConvex *FromConvexShape( JPH::ConvexShape *pCollide )
{
return reinterpret_cast< CPhysConvex * >( pCollide );
}
static const CPhysConvex *FromConvexShape( const JPH::ConvexShape *pCollide )
{
return reinterpret_cast< const CPhysConvex * >( pCollide );
}
//-------------------------------------------------------------------------------------------------
CPhysCollide *ToPhysCollide()
{
return reinterpret_cast< CPhysCollide * >( this );
}
};
//-------------------------------------------------------------------------------------------------
class CPhysPolysoup
{
public:
JPH::TriangleList Triangles;
};
//-------------------------------------------------------------------------------------------------
// Josh: Suprise! This is not an app system! Just an interface...
class JoltPhysicsCollision final : public IPhysicsCollision
{
public:
CPhysConvex *ConvexFromVerts( Vector **pVerts, int vertCount ) override;
CPhysConvex *ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance ) override;
float ConvexVolume( CPhysConvex *pConvex ) override;
float ConvexSurfaceArea( CPhysConvex *pConvex ) override;
void SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData ) override;
void ConvexFree( CPhysConvex *pConvex ) override;
CPhysConvex *BBoxToConvex( const Vector &mins, const Vector &maxs ) override;
CPhysConvex *ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron ) override;
void ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput ) override;
CPhysPolysoup *PolysoupCreate() override;
void PolysoupDestroy( CPhysPolysoup *pSoup ) override;
void PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits ) override;
CPhysCollide *ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP ) override;
CPhysCollide *ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount ) override;
CPhysCollide *ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams ) override;
void DestroyCollide( CPhysCollide *pCollide ) override;
int CollideSize( CPhysCollide *pCollide ) override;
int CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap = false ) override;
CPhysCollide *UnserializeCollide( char *pBuffer, int size, int index ) override;
float CollideVolume( CPhysCollide *pCollide ) override;
float CollideSurfaceArea( CPhysCollide *pCollide ) override;
Vector CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction ) override;
void CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles ) override;
void CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter ) override;
void CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter ) override;
Vector CollideGetOrthographicAreas( const CPhysCollide *pCollide ) override;
void CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas ) override;
int CollideIndex( const CPhysCollide *pCollide ) override;
CPhysCollide *BBoxToCollide( const Vector &mins, const Vector &maxs ) override;
int GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit ) override;
void TraceBox( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) override;
void TraceBox( const Ray_t &ray, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) override;
void TraceBox( const Ray_t &ray, unsigned int contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) override;
void TraceCollide( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) override;
bool IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone ) override;
void VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int size, bool swap = false ) override;
void VCollideUnload( vcollide_t *pVCollide ) override;
IVPhysicsKeyParser *VPhysicsKeyParserCreate( const char *pKeyData ) override;
IVPhysicsKeyParser *VPhysicsKeyParserCreate( vcollide_t *pVCollide ) override_asw;
void VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser ) override;
int CreateDebugMesh( CPhysCollide const *pCollisionModel, Vector **outVerts ) override;
void DestroyDebugMesh( int vertCount, Vector *outVerts ) override;
ICollisionQuery *CreateQueryModel( CPhysCollide *pCollide ) override;
void DestroyQueryModel( ICollisionQuery *pQuery ) override;
IPhysicsCollision *ThreadContextCreate() override;
void ThreadContextDestroy( IPhysicsCollision *pThreadContex ) override;
CPhysCollide *CreateVirtualMesh( const virtualmeshparams_t &params ) override;
bool SupportsVirtualMesh() override;
bool GetBBoxCacheSize( int *pCachedSize, int *pCachedCount ) override;
CPolyhedron *PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron ) override;
void OutputDebugInfo( const CPhysCollide *pCollide ) override;
unsigned int ReadStat( int statID ) override;
float CollideGetRadius( const CPhysCollide *pCollide ) override_asw;
void *VCollideAllocUserData( vcollide_t *pVCollide, size_t userDataSize ) override_asw;
void VCollideFreeUserData( vcollide_t *pVCollide ) override_asw;
void VCollideCheck( vcollide_t *pVCollide, const char *pName ) override_asw;
bool TraceBoxAA( const Ray_t &ray, const CPhysCollide *pCollide, trace_t *ptr ) override_csgo;
void DuplicateAndScale( vcollide_t *pOut, const vcollide_t *pIn, float flScale ) override_csgo;
public:
static JoltPhysicsCollision& GetInstance() { return s_PhysicsCollision; }
private:
static JoltPhysicsCollision s_PhysicsCollision;
};
const JPH::Shape *CreateCOMOverrideShape( const JPH::Shape* pShape, JPH::Vec3Arg comOverride );

View File

@ -0,0 +1,781 @@
//=================================================================================================
//
// Trace Logic
//
// Split out from vjolt_collide.cpp due to being a can of worms
//
//=================================================================================================
#include "cbase.h"
#include "coordsize.h" // DIST_EPSILON
#include "vjolt_debugrender.h"
#include "vjolt_util.h"
#include "vjolt_collide.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace VJoltTrace
{
static constexpr float kCollisionTolerance = 1.0e-3f;
static constexpr float kCharacterPadding = 0.02f;
// Also in vjolt_collide.cpp, should unify or just remove entirely
static constexpr float kMaxConvexRadius = JPH::cDefaultConvexRadius;
static ConVar vjolt_trace_debug( "vjolt_trace_debug", "0", FCVAR_CHEAT );
static ConVar vjolt_trace_debug_castray( "vjolt_trace_debug_castray", "0", FCVAR_CHEAT );
static ConVar vjolt_trace_debug_collidepoint( "vjolt_trace_debug_collidepoint", "0", FCVAR_CHEAT );
static ConVar vjolt_trace_debug_castbox( "vjolt_trace_debug_castbox", "0", FCVAR_CHEAT );
static ConVar vjolt_trace_debug_collidebox( "vjolt_trace_debug_collidebox", "0", FCVAR_CHEAT );
// Josh: Enables a hack to make portals work. For some reason when we enable colliding with
// backfaces, the player gets easily stuck in all sorts of things!
// Slart and I have not been able to determine the root cause of this problem and have tried for a long time...
//
// Slart: Portal 2 probably passes in a bad winding order in the polyhedron or something, dunno if it affects Portal 1
static ConVar vjolt_trace_portal_hack( "vjolt_trace_portal_hack", "0", FCVAR_NONE );
//-------------------------------------------------------------------------------------------------
//
// Collectors and helpers
//
//-------------------------------------------------------------------------------------------------
#ifdef JPH_DEBUG_RENDERER
static void PrintTrace( const trace_t *tr, JoltPhysicsDebugRenderer &debugRenderer )
{
Vector textPos = tr->endpos;
textPos.z += 4.0f;
constexpr float dur = 0.1f;
constexpr int r = 255;
constexpr int g = 255;
constexpr int b = 255;
constexpr int a = 255;
IVJoltDebugOverlay *pDebugOverlay = debugRenderer.GetDebugOverlay();
pDebugOverlay->AddTextOverlayRGB( textPos, 0, dur, r,g,b,a, "startpos : [ %g %g %g ]", tr->startpos.x, tr->startpos.y, tr->startpos.z );
pDebugOverlay->AddTextOverlayRGB( textPos, 1, dur, r,g,b,a, "endpos : [ %g %g %g ]", tr->endpos.x, tr->endpos.y, tr->endpos.z );
pDebugOverlay->AddTextOverlayRGB( textPos, 2, dur, r,g,b,a, "fraction : %g", tr->fraction );
pDebugOverlay->AddTextOverlayRGB( textPos, 3, dur, r,g,b,a, "contents : %d", tr->contents );
pDebugOverlay->AddTextOverlayRGB( textPos, 4, dur, r,g,b,a, "allsolid : %d", (int)tr->allsolid );
pDebugOverlay->AddTextOverlayRGB( textPos, 5, dur, r,g,b,a, "startsolid : %d", (int)tr->startsolid );
pDebugOverlay->AddTextOverlayRGB( textPos, 6, dur, r,g,b,a, "normal : [ %g %g %g ]", tr->plane.normal.x, tr->plane.normal.y, tr->plane.normal.z );
pDebugOverlay->AddTextOverlayRGB( textPos, 7, dur, r,g,b,a, "dist : %g", tr->plane.dist );
}
#endif
//
// Collector for VJolt_CastRay
// Returns the closest hit within the contents mask
//
class ContentsCollector_CastRay final : public JPH::CastRayCollector
{
public:
ContentsCollector_CastRay( const JPH::Shape *pShape, uint32 contentsMask, IConvexInfo *pConvexInfo )
: m_pShape( pShape ), m_ContentsMask( contentsMask ), m_pConvexInfo( pConvexInfo ) {}
void AddHit( const JPH::RayCastResult &inResult ) override
{
// Test if this collision is closer than the previous one
const float theirEarlyOut = inResult.GetEarlyOutFraction();
const float ourEarlyOut = GetEarlyOutFraction();
if ( !m_DidHit || theirEarlyOut < ourEarlyOut )
{
const uint32 gameData = static_cast<uint32>( m_pShape->GetSubShapeUserData( inResult.mSubShapeID2 ) );
const uint32 contents = m_pConvexInfo ? m_pConvexInfo->GetContents( gameData ) : CONTENTS_SOLID;
if ( contents & m_ContentsMask )
{
// Update our early out fraction
UpdateEarlyOutFraction( theirEarlyOut );
// Store hit info locally
m_Fraction = inResult.mFraction;
m_SubShapeID = inResult.mSubShapeID2;
m_ResultContents = contents;
m_DidHit = true;
}
}
}
private:
// Inputs
const JPH::Shape * m_pShape = nullptr;
uint32 m_ContentsMask = 0;
IConvexInfo * m_pConvexInfo = nullptr;
public:
// Outputs (only use if m_DidHit is true)
float m_Fraction = 1.0f; // Use this instead of GetEarlyOutFraction
JPH::SubShapeID m_SubShapeID; // Subshape hit
uint32 m_ResultContents = 0; // Contents of hit subshape
bool m_DidHit = false;
};
//
// Collector for VJolt_CollidePoint
// Returns the first hit within the contents mask
//
class ContentsCollector_CollidePoint final : public JPH::CollidePointCollector
{
public:
ContentsCollector_CollidePoint( const JPH::Shape *pShape, uint32 contentsMask, IConvexInfo *pConvexInfo )
: m_pShape( pShape ), m_ContentsMask( contentsMask ), m_pConvexInfo( pConvexInfo ) {}
void AddHit( const JPH::CollidePointResult &inResult ) override
{
// Return the first hit only
if ( m_DidHit )
return;
const uint32 gameData = static_cast<uint32>( m_pShape->GetSubShapeUserData( inResult.mSubShapeID2 ) );
const uint32 contents = m_pConvexInfo ? m_pConvexInfo->GetContents( gameData ) : CONTENTS_SOLID;
if ( contents & m_ContentsMask )
{
// Store hit info locally
m_SubShapeID = inResult.mSubShapeID2;
m_ResultContents = contents;
m_DidHit = true;
}
}
private:
// Inputs
const JPH::Shape * m_pShape = nullptr;
uint32 m_ContentsMask = 0;
IConvexInfo * m_pConvexInfo = nullptr;
public:
// Outputs (only use if m_DidHit is true)
JPH::SubShapeID m_SubShapeID; // Subshape hit
uint32 m_ResultContents = 0; // Contents of hit subshape
bool m_DidHit = false;
};
//
// Filter for generic shape casts or collides
// Ensures that objects which do not fit within the contents mask are excluded
//
class ContentsFilter_Shape final : public JPH::ShapeFilter
{
public:
ContentsFilter_Shape( const JPH::Shape *pShape, uint32 contentsMask, IConvexInfo *pConvexInfo )
: m_pShape( pShape ), m_ContentsMask( contentsMask ), m_pConvexInfo( pConvexInfo ) {}
bool ShouldCollide( const JPH::SubShapeID& inSubShapeID2 ) const override
{
const uint32 gameData = static_cast<uint32>( m_pShape->GetSubShapeUserData( inSubShapeID2 ) );
const uint32 contents = m_pConvexInfo ? m_pConvexInfo->GetContents( gameData ) : CONTENTS_SOLID;
return !!( contents & m_ContentsMask );
}
bool ShouldCollide( const JPH::SubShapeID &inSubShapeID1, const JPH::SubShapeID &inSubShapeID2 ) const override
{
return ShouldCollide( inSubShapeID2 );
}
public:
// Input
const JPH::Shape * m_pShape = nullptr;
uint32 m_ContentsMask = 0;
IConvexInfo * m_pConvexInfo = nullptr;
};
//
// Collector for generic shape casts
//
class ContentsCollector_CastShape final : public JPH::CastShapeCollector
{
public:
ContentsCollector_CastShape( const JPH::Shape *pShape, uint32 contentsMask, IConvexInfo *pConvexInfo )
: m_pShape( pShape ), m_ContentsMask( contentsMask ), m_pConvexInfo( pConvexInfo ) {}
void AddHit( const JPH::ShapeCastResult &inResult ) override
{
const uint32 gameData = static_cast<uint32>( m_pShape->GetSubShapeUserData( inResult.mSubShapeID2 ) );
const uint32 contents = m_pConvexInfo ? m_pConvexInfo->GetContents( gameData ) : CONTENTS_SOLID;
// Ensure that the contents filter was used
VJoltAssert( contents & m_ContentsMask );
// Test if this collision is closer than the previous one
const float theirEarlyOut = inResult.GetEarlyOutFraction();
const float ourEarlyOut = GetEarlyOutFraction();
if ( !m_DidHit || theirEarlyOut < ourEarlyOut )
{
// Update our early out fraction
UpdateEarlyOutFraction( theirEarlyOut );
m_Fraction = inResult.mFraction;
m_ResultContents = contents;
m_SubShapeID = inResult.mSubShapeID2;
m_ContactPoint = inResult.mContactPointOn2;
m_PenetrationAxis = inResult.mPenetrationAxis;
m_PenetrationDepth = inResult.mPenetrationDepth;
m_DidHit = true;
m_HitBackFace = inResult.mIsBackFaceHit;
}
}
private:
// Input
const JPH::Shape * m_pShape = nullptr;
uint32 m_ContentsMask = 0;
IConvexInfo * m_pConvexInfo = nullptr;
public:
// Outputs (only use if m_DidHit is true)
float m_Fraction = 1.0f;
uint32 m_ResultContents = 0; // Contents of the subshape that we hit
JPH::SubShapeID m_SubShapeID; // SubShapeID that we hit
JPH::Vec3 m_ContactPoint; // Point of impact relative to the COM of the object we hit
JPH::Vec3 m_PenetrationAxis;
float m_PenetrationDepth = 0.0f;
bool m_DidHit = false; // Set to true if we hit anything
bool m_HitBackFace = false; // Set to true if the hit was against a backface
};
//
// Collector for generic shape collides
//
class ContentsCollector_CollideShape final : public JPH::CollideShapeCollector
{
public:
ContentsCollector_CollideShape( const JPH::Shape *pShape, uint32 contentsMask, IConvexInfo *pConvexInfo )
: m_pShape( pShape ), m_ContentsMask( contentsMask ), m_pConvexInfo( pConvexInfo ) {}
// Called whenever a hit occurs, for compound objects this can be called multiple times
void AddHit( const JPH::CollideShapeResult &inResult ) override
{
// Get the contents of the subshape that we hit
const uint32 gameData = static_cast<uint32>( m_pShape->GetSubShapeUserData( inResult.mSubShapeID2 ) );
const uint32 contents = m_pConvexInfo ? m_pConvexInfo->GetContents( gameData ) : CONTENTS_SOLID;
VJoltAssert( contents & m_ContentsMask );
if ( inResult.GetEarlyOutFraction() < GetEarlyOutFraction() )
{
UpdateEarlyOutFraction( inResult.GetEarlyOutFraction() );
m_ResultContents = contents;
m_SubShapeID = inResult.mSubShapeID2;
m_ContactPoint = inResult.mContactPointOn2;
m_PenetrationAxis = inResult.mPenetrationAxis;
m_PenetrationDepth = inResult.mPenetrationDepth;
m_DidHit = true;
}
}
private:
// Input
const JPH::Shape * m_pShape = nullptr;
uint32 m_ContentsMask = 0;
IConvexInfo * m_pConvexInfo = nullptr;
public:
// Output, only valid if m_didHit is true
uint32 m_ResultContents = 0; // Contents of the subshape that we hit
JPH::SubShapeID m_SubShapeID; // SubShapeID that we hit
JPH::Vec3 m_ContactPoint; // Point of impact relative to the COM of the object we hit
JPH::Vec3 m_PenetrationAxis;
float m_PenetrationDepth = 0.0f;
bool m_DidHit = false; // Set to true if we hit anything
};
//
// Contents collector for simple collide operations (no contents)
//
class ContentsCollector_SimpleCollide final : public JPH::CollideShapeCollector
{
public:
void AddHit( const ResultType &inResult ) override
{
intersection = true;
}
bool intersection = false;
};
//-------------------------------------------------------------------------------------------------
//
// Tracing functions
//
//-------------------------------------------------------------------------------------------------
//
// Casts a ray against a shape
//
static void CastRay( const Ray_t &ray, uint32 contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
const JPH::Shape *pShape = pCollide->ToShape();
JPH::Vec3 position = SourceToJolt::Distance( collideOrigin );
JPH::Quat rotation = SourceToJolt::Angle( collideAngles );
JPH::Mat44 queryTransform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() );
// Create ray
JPH::RayCast joltRay = { SourceToJolt::Distance( ray.m_Start ), SourceToJolt::Distance( ray.m_Delta ) };
// Transform the ray by the inverse of the position/rotation
joltRay = joltRay.Transformed( queryTransform.InversedRotationTranslation() );
// Set-up the settings
JPH::RayCastSettings settings;
settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;
settings.mTreatConvexAsSolid = true;
//
// Hello!
// This code mimics IVP's behaviour where solids are solid, and the ray is totally clipped when inside.
// the BSP code (the gold standard) allows traces that start inside of solid objects to pass through backfaces on the convex.
// BSP sets traces that start inside of objects as startsolid 1, IVP doesn't do this :(
// So in the future, to mimic the BSP logic, we should make traces that begin inside of objects startsolid and only allsolid
// if they never leave the object
//
// Create our collector and cast away!
ContentsCollector_CastRay collector( pShape, contentsMask, pConvexInfo );
pShape->CastRay( joltRay, settings, JPH::SubShapeIDCreator(), collector );
if ( !collector.m_DidHit )
{
VJoltAssert( collector.m_Fraction == 1.0f );
}
// Populate pTrace's members
pTrace->fraction = collector.m_Fraction;
pTrace->startpos = ray.m_Start + ray.m_StartOffset;
pTrace->endpos = pTrace->startpos + ( ray.m_Delta * pTrace->fraction );
pTrace->allsolid = pTrace->fraction == 0.0f;
pTrace->startsolid = pTrace->fraction == 0.0f;
// If we hit, we fill in the plane and contents too
if ( collector.m_DidHit )
{
JPH::Vec3 normal = queryTransform.GetRotation() * pShape->GetSurfaceNormal( collector.m_SubShapeID, joltRay.GetPointOnRay( collector.m_Fraction ) );
pTrace->plane.normal = Vector( normal.GetX(), normal.GetY(), normal.GetZ() );
pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
pTrace->contents = collector.m_ResultContents;
}
#if defined JPH_DEBUG_RENDERER
// Debug trace visualizing
IVJoltDebugOverlay *pOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_trace_debug.GetBool() && vjolt_trace_debug_castray.GetBool() && pOverlay )
{
JoltPhysicsDebugRenderer &debugRenderer = JoltPhysicsDebugRenderer::GetInstance();
JPH::Color rayColor( 255, 0, 0, 255 );
if ( collector.m_DidHit )
{
rayColor.r = 0;
rayColor.g = 255;
JPH::Vec3 normal = queryTransform.GetRotation() * pShape->GetSurfaceNormal( collector.m_SubShapeID, joltRay.GetPointOnRay( collector.m_Fraction ) );
debugRenderer.DrawArrow( queryTransform * ( joltRay.mOrigin + ( joltRay.mDirection * collector.GetEarlyOutFraction() ) ), queryTransform * ( joltRay.mOrigin + joltRay.mDirection * collector.GetEarlyOutFraction() ) + normal, JPH::Color::sRed, 0.3f );
}
debugRenderer.DrawArrow( queryTransform * joltRay.mOrigin, queryTransform * ( joltRay.mOrigin + ( joltRay.mDirection * collector.GetEarlyOutFraction() ) ), rayColor, 0.3f );
}
#endif
}
//
// Collides a point against a shape
//
static void CollidePoint( const Ray_t &ray, uint32 contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
const JPH::Shape *pShape = pCollide->ToShape();
JPH::Vec3 position = SourceToJolt::Distance( collideOrigin );
JPH::Quat rotation = SourceToJolt::Angle( collideAngles );
JPH::Mat44 queryTransform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() );
JPH::Vec3 point = queryTransform.InversedRotationTranslation() * SourceToJolt::Distance(ray.m_Start);
ContentsCollector_CollidePoint collector( pShape, contentsMask, pConvexInfo );
pShape->CollidePoint( point, JPH::SubShapeIDCreator(), collector );
// Populate pTrace's members
pTrace->fraction = collector.m_DidHit ? 0.0f : 1.0f;
pTrace->startpos = ray.m_Start + ray.m_StartOffset;
pTrace->endpos = pTrace->startpos;
pTrace->allsolid = collector.m_DidHit;
pTrace->startsolid = collector.m_DidHit;
// This will be zero if we didn't hit
pTrace->contents = collector.m_ResultContents;
#if defined JPH_DEBUG_RENDERER
// Debug trace visualizing
IVJoltDebugOverlay *pOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_trace_debug.GetBool() && vjolt_trace_debug_collidepoint.GetBool() && pOverlay )
{
JoltPhysicsDebugRenderer &debugRenderer = JoltPhysicsDebugRenderer::GetInstance();
JPH::Color rayColor( 0, 255, 0, 0 );
if ( collector.m_DidHit )
{
rayColor.a = 255;
}
debugRenderer.DrawMarker( point, rayColor, 0.3f );
}
#endif
}
static float CalculateSourceFraction( const Vector &rayDelta, float fraction, const Vector &normal )
{
const float rayLength = rayDelta.Length();
float invBaseLength = 0.0f;
Vector rayDir = vec3_origin;
if ( rayLength )
{
rayDir = rayDelta.Normalized();
invBaseLength = 1.0f / rayLength;
}
float hitLength = rayLength * fraction;
float dot = DotProduct( rayDir, normal );
if ( dot < 0.0f )
hitLength += DIST_EPSILON / dot;
hitLength = Max( hitLength, 0.0f );
return hitLength * invBaseLength;
}
//
// Casts a box against a shape
//
static void CastBoxVsShape( const Ray_t &ray, uint32 contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
const JPH::Shape *pShape = pCollide->ToShape();
JPH::Vec3 origin = SourceToJolt::Distance( ray.m_Start ); // Origin of the box or ray we're casting
JPH::Vec3 direction = SourceToJolt::Distance( ray.m_Delta ); // Direction and distance of the cast
JPH::Vec3 halfExtent = SourceToJolt::Distance( ray.m_Extents );
JPH::Vec3 position = SourceToJolt::Distance( collideOrigin ); // Position of the object we're casting against
JPH::Quat rotation = SourceToJolt::Angle( collideAngles ); // Rotation of the object we're casting against
JPH::Mat44 queryTransform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() );
JPH::BoxShape boxShape( halfExtent, kMaxConvexRadius );
JPH::ShapeCast shapeCast( &boxShape, JPH::Vec3::sReplicate( 1.0f ), JPH::Mat44::sTranslation( origin ), direction );
JPH::ShapeCastSettings settings;
//settings.mBackFaceModeTriangles = JPH::EBackFaceMode::CollideWithBackFaces;
// Josh: Had to re-enable CollideWithBackFaces to allow triggers for the Portal Environment to work.
// Come back here if we start getting stuck on things again...
if ( vjolt_trace_portal_hack.GetBool() )
settings.mBackFaceModeConvex = JPH::EBackFaceMode::CollideWithBackFaces;
//settings.mCollisionTolerance = kCollisionTolerance;
settings.mUseShrunkenShapeAndConvexRadius = true;
settings.mReturnDeepestPoint = true;
ContentsFilter_Shape filter( pShape, contentsMask, pConvexInfo );
ContentsCollector_CastShape collector( pShape, contentsMask, pConvexInfo );
JPH::CollisionDispatch::sCastShapeVsShapeWorldSpace( shapeCast, settings, pShape, JPH::Vec3::sReplicate( 1.0f ), filter, queryTransform, JPH::SubShapeIDCreator(), JPH::SubShapeIDCreator(), collector );
if ( collector.m_DidHit )
{
JPH::Vec3 normal = -( collector.m_PenetrationAxis.Normalized() );
pTrace->plane.normal = Vector( normal.GetX(), normal.GetY(), normal.GetZ() );
pTrace->fraction = CalculateSourceFraction( ray.m_Delta, collector.m_Fraction, pTrace->plane.normal );
//Log_Msg( LOG_VJolt, "Depth: %g, InitialFraction = %g, NewFraction = %g\n", collector.m_PenetrationDepth, flInitialFraction, pTrace->fraction );
pTrace->startpos = ray.m_Start + ray.m_StartOffset;
pTrace->endpos = pTrace->startpos + ( ray.m_Delta * pTrace->fraction );
pTrace->endpos -= pTrace->plane.normal * collector.m_PenetrationDepth;
pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
pTrace->contents = collector.m_ResultContents;
// If penetrating more than DIST_EPSILON, consider it an intersection
//constexpr float PenetrationEpsilon = DIST_EPSILON;
static constexpr float kMinRequiredPenetration = 0.005f + kCharacterPadding;
pTrace->allsolid = collector.m_PenetrationDepth > kMinRequiredPenetration && pTrace->fraction == 0.0f;
pTrace->startsolid = collector.m_PenetrationDepth > kMinRequiredPenetration && pTrace->fraction == 0.0f;
}
else
{
pTrace->fraction = 1.0f;
pTrace->startpos = ray.m_Start + ray.m_StartOffset;
pTrace->endpos = pTrace->startpos + ray.m_Delta;
// We didn't hit anything, so we must be completely free right?
pTrace->allsolid = false;
pTrace->startsolid = false;
}
#if defined JPH_DEBUG_RENDERER
// Debug trace visualizing
IVJoltDebugOverlay *pOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_trace_debug.GetBool() && vjolt_trace_debug_castbox.GetBool() && pOverlay )
{
JoltPhysicsDebugRenderer &debugRenderer = JoltPhysicsDebugRenderer::GetInstance();
JPH::Color color( 255, 64, 64, 255 );
if ( collector.m_DidHit )
{
color.r = 64;
color.g = 255;
JPH::Vec3 hitPos = origin + ( direction * collector.m_Fraction );
debugRenderer.DrawArrow( hitPos, hitPos + collector.m_PenetrationAxis, JPH::Color::sRed, 0.3f );
}
boxShape.Draw( &debugRenderer, queryTransform, JPH::Vec3::sReplicate( 1.0f ), color, false, false );
}
#endif
}
//
// Collides a box against a shape
//
static void CollideBoxVsShape( const Ray_t &ray, uint32 contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
const JPH::Shape *pShape = pCollide->ToShape();
JPH::Vec3 origin = SourceToJolt::Distance( ray.m_Start ); // Origin of the box or ray we're casting
JPH::Vec3 direction = SourceToJolt::Distance( ray.m_Delta ); // Direction and distance of the cast
JPH::Vec3 halfExtent = SourceToJolt::Distance( ray.m_Extents );
JPH::Vec3 position = SourceToJolt::Distance( collideOrigin ); // Position of the object we're casting against
JPH::Quat rotation = SourceToJolt::Angle( collideAngles ); // Rotation of the object we're casting against
JPH::Mat44 queryTransform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() );
JPH::BoxShape boxShape( halfExtent, kMaxConvexRadius );
JPH::CollideShapeSettings settings;
//settings.mMaxSeparationDistance = DIST_EPSILON;
//settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;
ContentsCollector_CollideShape collector( pShape, contentsMask, pConvexInfo );
JPH::CollisionDispatch::sCollideShapeVsShape(
&boxShape, pShape,
JPH::Vec3::sReplicate( 1.0f ), JPH::Vec3::sReplicate( 1.0f ),
JPH::Mat44::sIdentity(), queryTransform,
JPH::SubShapeIDCreator(), JPH::SubShapeIDCreator(),
settings, collector );
pTrace->fraction = collector.m_DidHit ? 0.0f : 1.0f;
pTrace->startpos = ray.m_Start + ray.m_StartOffset;
pTrace->endpos = pTrace->startpos;
pTrace->allsolid = collector.m_DidHit;
pTrace->startsolid = collector.m_DidHit;
if ( collector.m_DidHit )
{
JPH::Vec3 normal = -( ( queryTransform.GetRotation() * collector.m_PenetrationAxis ).Normalized() );
pTrace->plane.normal = Vector( normal.GetX(), normal.GetY(), normal.GetZ() );
pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
pTrace->contents = collector.m_ResultContents;
}
#if defined JPH_DEBUG_RENDERER
// Debug trace visualizing
IVJoltDebugOverlay *pOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_trace_debug.GetBool() && vjolt_trace_debug_collidebox.GetBool() && pOverlay )
{
JoltPhysicsDebugRenderer &debugRenderer = JoltPhysicsDebugRenderer::GetInstance();
JPH::Color color( 255, 64, 64, 255 );
if ( collector.m_DidHit )
{
color.r = 64;
color.g = 255;
}
boxShape.Draw( &debugRenderer, queryTransform, JPH::Vec3::sReplicate( 1.0f ), color, false, false );
}
#endif
}
//
// Collides a shape against a shape
//
// Slart: This sucks, it could be implemented better in the environment interface using a bool and two physics objects instead... Basically all the code using this wants it to see
// if a shape is inside another shape.
//
static void CollideShapeVsShape( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
ClearTrace( pTrace );
// Are we sweeping, or just doing a point test?
bool isSweeping = false;
if ( !VectorCompare( start, end ) )
{
isSweeping = true;
// Slart: Exactly one piece of code "may" want a sweeping trace, ffs
Log_Warning( LOG_VJolt, "Attemping an unsupported TraceCollide\n" );
return;
}
// Glue variable
const Vector &sweepOrigin = start;
const JPH::Shape *pSweepShape = pSweepCollide->ToShape();
JPH::Vec3 sweepJoltPosition = SourceToJolt::Distance( sweepOrigin );
JPH::Quat sweepJoltRotation = SourceToJolt::Angle( sweepAngles );
JPH::Mat44 sweepTransform = JPH::Mat44::sRotationTranslation( sweepJoltRotation, sweepJoltPosition + sweepJoltRotation * pSweepShape->GetCenterOfMass() );
const JPH::Shape *pCollideShape = pCollide->ToShape();
JPH::Vec3 collideJoltPosition = SourceToJolt::Distance( collideOrigin );
JPH::Quat collideJoltRotation = SourceToJolt::Angle( collideAngles );
JPH::Mat44 collideTransform = JPH::Mat44::sRotationTranslation( collideJoltRotation, collideJoltPosition + collideJoltRotation * pCollideShape->GetCenterOfMass() );
//JPH::Mat44 collideTransform = JPH::Mat44::sTranslation( collideJoltPosition );
if ( !isSweeping )
{
JPH::AABox sweepAABB = pSweepShape->GetWorldSpaceBounds( sweepTransform, JPH::Vec3::sReplicate( 1.0f ) );
JPH::AABox collideAABB = pCollideShape->GetWorldSpaceBounds( collideTransform, JPH::Vec3::sReplicate( 1.0f ) );
// Debug trace visualizing
IVJoltDebugOverlay *pOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_trace_debug.GetBool() && pOverlay && !pTrace->allsolid )
{
Vector mins, maxs;
mins = JoltToSource::Distance( sweepAABB.mMin );
maxs = JoltToSource::Distance( sweepAABB.mMax );
pOverlay->AddBoxOverlay( vec3_origin, mins, maxs, vec3_angle, 255, 64, 64, 255, -1.0f );
mins = JoltToSource::Distance( collideAABB.mMin );
maxs = JoltToSource::Distance( collideAABB.mMax );
pOverlay->AddBoxOverlay( vec3_origin, mins, maxs, vec3_angle, 255, 64, 64, 255, -1.0f );
}
// Don't bother doing any work if we don't contain the thing to be tested
if ( !collideAABB.Contains( sweepAABB ) )
{
// TODO(Slart): Uncomment this
//return;
}
ContentsCollector_SimpleCollide collector;
JPH::CollisionDispatch::sCollideShapeVsShape(
pSweepShape, pCollideShape,
JPH::Vec3::sReplicate( 1.0f ), JPH::Vec3::sReplicate( 1.0f ),
sweepTransform, collideTransform,
JPH::SubShapeIDCreator(), JPH::SubShapeIDCreator(),
JPH::CollideShapeSettings(), collector );
if ( collector.intersection )
{
pTrace->allsolid = true;
pTrace->startsolid = true;
}
}
}
//-------------------------------------------------------------------------------------------------
//
// Where we figure out which type this trace actually is
//
//-------------------------------------------------------------------------------------------------
static void TraceBase( const Ray_t &ray, uint32 contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
VJoltAssert( pCollide && pTrace );
// Default out our trace
ClearTrace( pTrace );
// We can't trust Ray_t's settings because after conversion from Source > Jolt the coordinates might become tiny
bool isPoint = SourceToJolt::Distance( ray.m_Extents ).ReduceMin() < kMaxConvexRadius;
bool isCollide = !ray.m_IsSwept;
if ( isPoint )
{
if ( isCollide )
{
CollidePoint( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, pTrace );
}
else
{
CastRay( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, pTrace );
}
}
else
{
if ( isCollide )
{
// TODO(Slart): This should be CollideBoxVsShape, but I can't remember why it wasn't good enough...
CastBoxVsShape( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, pTrace );
}
else
{
CastBoxVsShape( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, pTrace );
}
}
}
} // namespace VJoltTrace
//-------------------------------------------------------------------------------------------------
//
// IPhysicsCollision Interface
//
//-------------------------------------------------------------------------------------------------
void JoltPhysicsCollision::TraceBox( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr )
{
Ray_t ray;
ray.Init( start, end, mins, maxs );
VJoltTrace::TraceBase( ray, MASK_ALL, nullptr, pCollide, collideOrigin, collideAngles, ptr );
}
void JoltPhysicsCollision::TraceBox( const Ray_t &ray, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr )
{
VJoltTrace::TraceBase( ray, MASK_ALL, nullptr, pCollide, collideOrigin, collideAngles, ptr );
}
void JoltPhysicsCollision::TraceBox( const Ray_t &ray, unsigned int contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr )
{
VJoltTrace::TraceBase( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, ptr );
}
void JoltPhysicsCollision::TraceCollide( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *pTrace )
{
VJoltTrace::CollideShapeVsShape( start, end, pSweepCollide, sweepAngles, pCollide, collideOrigin, collideAngles, pTrace );
}

View File

@ -0,0 +1,625 @@
//=================================================================================================
//
// Constraints
//
//=================================================================================================
#include "cbase.h"
#include "vjolt_environment.h"
#include "vjolt_layers.h"
#include "vjolt_object.h"
#include "vjolt_constraints.h"
#include "vjolt_layers.h"
//-------------------------------------------------------------------------------------------------
static ConVar vjolt_ragdoll_hinge_optimization( "vjolt_ragdoll_hinge_optimization", "1", FCVAR_REPLICATED,
"Optimizes ragdolls to use hinge constraints for joints with 1 degree of freedom. Additionally fixes legs going back on themselves. Currently breaks ragdolls of NPCs killed in a pose (they inherit the pose).");
//-------------------------------------------------------------------------------------------------
JoltPhysicsConstraintGroup::JoltPhysicsConstraintGroup()
{
}
JoltPhysicsConstraintGroup::~JoltPhysicsConstraintGroup()
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraintGroup::Activate()
{
for ( JoltPhysicsConstraint *pConstraint : m_pConstraints )
pConstraint->Activate();
}
bool JoltPhysicsConstraintGroup::IsInErrorState()
{
return false;
}
void JoltPhysicsConstraintGroup::ClearErrorState()
{
}
void JoltPhysicsConstraintGroup::GetErrorParams( constraint_groupparams_t *pParams )
{
if ( pParams )
*pParams = m_ErrorParams;
}
void JoltPhysicsConstraintGroup::SetErrorParams( const constraint_groupparams_t &params )
{
m_ErrorParams = params;
}
void JoltPhysicsConstraintGroup::SolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1 )
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraintGroup::AddConstraint( JoltPhysicsConstraint *pConstraint )
{
m_pConstraints.push_back( pConstraint );
}
void JoltPhysicsConstraintGroup::RemoveConstraint( JoltPhysicsConstraint *pConstraint )
{
m_pConstraints.erase(
std::remove_if( m_pConstraints.begin(), m_pConstraints.end(), [&]( JoltPhysicsConstraint *pOther ) { return pOther == pConstraint; } ) );
}
//-------------------------------------------------------------------------------------------------
JoltPhysicsConstraint::JoltPhysicsConstraint( JoltPhysicsEnvironment *pPhysicsEnvironment, IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, constraintType_t Type, JPH::Constraint* pConstraint, void *pGameData )
: m_pPhysicsEnvironment( pPhysicsEnvironment )
, m_pPhysicsSystem( pPhysicsEnvironment->GetPhysicsSystem() )
, m_pObjReference( static_cast<JoltPhysicsObject*>( pReferenceObject ) )
, m_pObjAttached( static_cast<JoltPhysicsObject*>( pAttachedObject ) )
, m_ConstraintType( Type )
, m_pConstraint( pConstraint )
, m_pGameData( pGameData )
{
m_pObjReference->AddDestroyedListener( this );
m_pObjAttached->AddDestroyedListener( this );
}
JoltPhysicsConstraint::~JoltPhysicsConstraint()
{
if ( m_pGroup )
{
m_pGroup->RemoveConstraint( this );
m_pGroup = nullptr;
}
DestroyConstraint();
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::Activate()
{
if ( m_pConstraint )
m_pConstraint->SetEnabled( true );
}
void JoltPhysicsConstraint::Deactivate()
{
if ( m_pConstraint )
m_pConstraint->SetEnabled( false );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::SetGameData( void *gameData )
{
m_pGameData = gameData;
}
void *JoltPhysicsConstraint::GetGameData() const
{
return m_pGameData;
}
//-------------------------------------------------------------------------------------------------
IPhysicsObject *JoltPhysicsConstraint::GetReferenceObject() const
{
return m_pObjReference;
}
IPhysicsObject *JoltPhysicsConstraint::GetAttachedObject() const
{
return m_pObjAttached;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::SetLinearMotor( float speed, float maxLinearImpulse )
{
if ( !m_pConstraint )
return;
speed = SourceToJolt::Distance( speed );
maxLinearImpulse = SourceToJolt::Distance( maxLinearImpulse );
switch ( m_ConstraintType )
{
case CONSTRAINT_SLIDING:
{
JPH::SliderConstraint *pConstraint = static_cast<JPH::SliderConstraint *>( m_pConstraint );
pConstraint->SetMotorState( speed ? JPH::EMotorState::Velocity : JPH::EMotorState::Off );
pConstraint->SetTargetVelocity( speed );
JPH::MotorSettings &motorSettings = pConstraint->GetMotorSettings();
motorSettings.SetForceLimits( -maxLinearImpulse, maxLinearImpulse );
break;
}
}
}
void JoltPhysicsConstraint::SetAngularMotor( float rotSpeed, float maxAngularImpulse )
{
if ( !m_pConstraint )
return;
rotSpeed = DEG2RAD( rotSpeed );
maxAngularImpulse = DEG2RAD( maxAngularImpulse );
switch ( m_ConstraintType )
{
case CONSTRAINT_RAGDOLL:
{
// Josh:
// If you change the hinge optimization stuff, remember to
// check this! m_ConstraintType is CONSTRAINT_HINGE for that! (same with normal vphysics)
//
// Something else to note is... does the below code for friction vs angular impulse work on
// ragdolls -> hinges correctly? This happens in Source, but this may not necessarily be correct.
// :/
VJoltAssert( m_pConstraint->GetSubType() == JPH::EConstraintSubType::SixDOF );
JPH::SixDOFConstraint *pConstraint = static_cast<JPH::SixDOFConstraint *>( m_pConstraint );
pConstraint->SetTargetAngularVelocityCS( JPH::Vec3( rotSpeed, rotSpeed, rotSpeed ) );
pConstraint->SetMaxFriction( JPH::SixDOFConstraint::EAxis::RotationX, maxAngularImpulse );
pConstraint->SetMaxFriction( JPH::SixDOFConstraint::EAxis::RotationY, maxAngularImpulse );
pConstraint->SetMaxFriction( JPH::SixDOFConstraint::EAxis::RotationZ, maxAngularImpulse );
break;
}
case CONSTRAINT_HINGE:
{
JPH::HingeConstraint *pConstraint = static_cast<JPH::HingeConstraint *>( m_pConstraint );
pConstraint->SetMotorState( rotSpeed ? JPH::EMotorState::Velocity : JPH::EMotorState::Off );
pConstraint->SetTargetAngularVelocity( rotSpeed );
JPH::MotorSettings &motorSettings = pConstraint->GetMotorSettings();
motorSettings.SetForceLimits( -fabsf( maxAngularImpulse ), fabsf( maxAngularImpulse ) );
break;
}
}
}
//-------------------------------------------------------------------------------------------------
// Slart: This is never called anywhere in our codebase
void JoltPhysicsConstraint::UpdateRagdollTransforms( const matrix3x4_t &constraintToReference, const matrix3x4_t &constraintToAttached )
{
}
// Slart: This is only used for visual debugging, which we don't *really* need since we have Jolt's debugger
bool JoltPhysicsConstraint::GetConstraintTransform( matrix3x4_t *pConstraintToReference, matrix3x4_t *pConstraintToAttached ) const
{
if ( m_pObjReference && pConstraintToReference )
m_pObjReference->GetPositionMatrix( pConstraintToReference );
if ( m_pObjAttached && pConstraintToAttached )
m_pObjAttached->GetPositionMatrix( pConstraintToAttached );
return true;
}
// Slart: Yet another debugging thing
bool JoltPhysicsConstraint::GetConstraintParams( constraint_breakableparams_t *pParams ) const
{
return false;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::OutputDebugInfo()
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject )
{
DestroyConstraint();
// Normal VPhysics calls ConstraintBroken when an object being killed destroys the constraint.
m_pPhysicsEnvironment->NotifyConstraintDisabled( this );
}
//-------------------------------------------------------------------------------------------------
// Ragdoll
//-------------------------------------------------------------------------------------------------
static Vector DOFToAxis( uint32 uDOF )
{
return Vector(
uDOF == 0 ? 1.0f : 0.0f,
uDOF == 1 ? 1.0f : 0.0f,
uDOF == 2 ? 1.0f : 0.0f );
}
static uint32 NextDOF( uint32 uDOF )
{
return ( uDOF + 1 ) % 3;
}
static bool IsFixedAxis( const constraint_axislimit_t &axis )
{
return axis.minRotation == axis.maxRotation;
}
// Returns a bitmask of degrees of freedom for a ragdoll.
static uint32 GetDegreesOfFreedom( const constraint_ragdollparams_t &ragdoll )
{
uint32 uDOFMask = 0;
for ( int i = 0; i < 3; i++ )
{
if ( !IsFixedAxis( ragdoll.axes[i] ) )
uDOFMask |= 1u << i;
}
return uDOFMask;
}
bool JoltPhysicsConstraint::InitialiseHingeFromRagdoll( IPhysicsConstraintGroup* pGroup, const constraint_ragdollparams_t& ragdoll )
{
const uint32 uDOFMask = GetDegreesOfFreedom( ragdoll );
const uint32 uDOFCount = popcnt( uDOFMask );
if ( uDOFCount != 1 )
return false;
const uint32 uDOF = tzcnt( uDOFMask );
const Vector vecNextDOFAxis = DOFToAxis( NextDOF( uDOF ) );
matrix3x4_t refObjToWorld;
m_pObjReference->GetPositionMatrix( &refObjToWorld );
matrix3x4_t constraintToWorld;
ConcatTransforms( refObjToWorld, ragdoll.constraintToReference, constraintToWorld );
Vector perpAxisDir;
VectorIRotate( vecNextDOFAxis, ragdoll.constraintToReference, perpAxisDir );
constraint_limitedhingeparams_t hinge;
hinge.Defaults();
hinge.constraint = ragdoll.constraint;
hinge.worldPosition = GetColumn( constraintToWorld, MatrixAxis::Origin );
hinge.worldAxisDirection = GetColumn( constraintToWorld, static_cast< JoltMatrixAxes >( uDOF ) );
hinge.referencePerpAxisDirection = vecNextDOFAxis;
hinge.attachedPerpAxisDirection = Rotate( perpAxisDir, ragdoll.constraintToAttached );
hinge.hingeAxis = ragdoll.axes[ uDOF ];
if ( !ragdoll.useClockwiseRotations )
{
const float minLimit = hinge.hingeAxis.minRotation;
const float maxLimit = hinge.hingeAxis.maxRotation;
hinge.hingeAxis.minRotation = -maxLimit;
hinge.hingeAxis.maxRotation = -minLimit;
}
InitialiseHinge( pGroup, hinge );
return true;
}
void JoltPhysicsConstraint::InitialiseRagdoll( IPhysicsConstraintGroup *pGroup, const constraint_ragdollparams_t &ragdoll )
{
// Josh:
// Optimize to a hinge constraint if we can -- avoids a bunch of useless computation
// and additionally fixes the fact that we can only specify disjoint min/max rotations
// on the X axis with Jolt 6DOF
// Currently breaks killing NPCs that are in a pose for some reason -- they stay in the pose. Needs investigation.
if ( vjolt_ragdoll_hinge_optimization.GetBool() && InitialiseHingeFromRagdoll( pGroup, ragdoll ) )
return;
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_RAGDOLL;
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::Mat44 constraintToReference = SourceToJolt::Matrix( ragdoll.constraintToReference );
JPH::Mat44 constraintToAttached = SourceToJolt::Matrix( ragdoll.constraintToAttached );
JPH::SixDOFConstraintSettings settings;
settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
settings.mPosition1 = constraintToReference.GetTranslation() - refBody->GetShape()->GetCenterOfMass();
settings.mAxisX1 = constraintToReference.GetAxisX();
settings.mAxisY1 = constraintToReference.GetAxisY();
settings.mPosition2 = constraintToAttached.GetTranslation() - attBody->GetShape()->GetCenterOfMass();
settings.mAxisX2 = constraintToAttached.GetAxisX();
settings.mAxisY2 = constraintToAttached.GetAxisY();
for ( int i = 0; i < 3; i++ )
{
JPH::SixDOFConstraintSettings::EAxis positionalAxis = static_cast< JPH::SixDOFConstraintSettings::EAxis >(
JPH::SixDOFConstraintSettings::EAxis::TranslationX + i );
JPH::SixDOFConstraintSettings::EAxis rotationalAxis = static_cast< JPH::SixDOFConstraintSettings::EAxis >(
JPH::SixDOFConstraintSettings::EAxis::RotationX + i );
// Make positional axes fixed, unless otherwise stated; the airboat needs them unlocked for a funny hack.
if ( !ragdoll.onlyAngularLimits )
settings.MakeFixedAxis( positionalAxis );
if ( ragdoll.axes[i].minRotation == ragdoll.axes[i].maxRotation )
{
//Log_Msg( LOG_VJolt, "Creating ragdoll-fixed constraint\n" );
settings.MakeFixedAxis( rotationalAxis );
}
else
{
if ( i == 0 )
{
//Log_Msg( LOG_VJolt, "Creating X ragdoll constraint with %g min and %g max\n", -ragdoll.axes[i].maxRotation, -ragdoll.axes[i].minRotation );
settings.SetLimitedAxis( rotationalAxis, DEG2RAD( ragdoll.axes[i].minRotation ), DEG2RAD( ragdoll.axes[i].maxRotation ) );
}
else
{
// Josh:
// Jolt uses a Swing Twist for part of this, which means we have to find the max movement allowed
// to contrain it by, I think. This results in legs that kind of flop both ways... From the Jolt code
// "The swing twist constraint part requires symmetrical rotations around Y and Z"
//
// This is kind of 'worked around' by the code above that converts 1DOF -> hinges.
const float maxMovement = Max( fabsf( ragdoll.axes[i].maxRotation ), fabsf( ragdoll.axes[i].minRotation ) );
//Log_Msg( LOG_VJolt, "Creating Y/Z ragdoll constraint with %g min and %g max\n", -maxMovement, maxMovement );
settings.SetLimitedAxis( rotationalAxis, -DEG2RAD( maxMovement ), DEG2RAD( maxMovement ) );
}
}
// Swap the limits if we are using clockwise rotations,
// this is only not true if we are saving/loading.
if ( ragdoll.useClockwiseRotations )
{
const float minLimit = settings.mLimitMin[rotationalAxis];
const float maxLimit = settings.mLimitMax[rotationalAxis];
settings.mLimitMin[rotationalAxis] = -maxLimit;
settings.mLimitMax[rotationalAxis] = -minLimit;
}
// TODO(Josh): What is .torque on a ragdoll in Source? I want to understand what it is
// before setting random values on the Jolt side.
//
//if ( ragdoll.axes[ i ].torque != 0.0f )
// settings.mMotorSettings[ rotationalAxis ].SetTorqueLimit( ragdoll.axes[ i ].torque );
}
m_pConstraint = settings.Create( *refBody, *attBody );
m_pConstraint->SetEnabled( !pGroup && ragdoll.constraint.isActive );
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Hinge
//-------------------------------------------------------------------------------------------------
static JPH::Vec3 HingePerpendicularVector( JPH::Vec3Arg dir )
{
return fabsf( dir.GetX() ) < 0.57f
? JPH::Vec3::sAxisX().Cross( dir ).Normalized()
: JPH::Vec3::sAxisY().Cross( dir ).Normalized();
}
void JoltPhysicsConstraint::InitialiseHinge( IPhysicsConstraintGroup *pGroup, const constraint_hingeparams_t &hinge )
{
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_HINGE;
// Get our bodies
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::HingeConstraintSettings settings;
settings.mPoint1 = SourceToJolt::Distance( hinge.worldPosition );
settings.mPoint2 = SourceToJolt::Distance( hinge.worldPosition );
settings.mHingeAxis1 = JPH::Vec3( hinge.worldAxisDirection.x, hinge.worldAxisDirection.y, hinge.worldAxisDirection.z );
settings.mHingeAxis2 = JPH::Vec3( hinge.worldAxisDirection.x, hinge.worldAxisDirection.y, hinge.worldAxisDirection.z );
settings.mNormalAxis1 = HingePerpendicularVector( settings.mHingeAxis1 );
settings.mNormalAxis2 = HingePerpendicularVector( settings.mHingeAxis2 );
if ( hinge.hingeAxis.minRotation != hinge.hingeAxis.maxRotation )
{
settings.mLimitsMin = DEG2RAD( -hinge.hingeAxis.maxRotation );
settings.mLimitsMax = DEG2RAD( -hinge.hingeAxis.minRotation );
}
// TODO(Josh): Fix this... I have no idea what this should be.
//settings.mMaxFrictionTorque = hinge.hingeAxis.torque;
m_pConstraint = settings.Create( *refBody, *attBody );
m_pConstraint->SetEnabled( !pGroup && hinge.constraint.isActive );
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Sliding
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::InitialiseSliding( IPhysicsConstraintGroup *pGroup, const constraint_slidingparams_t &sliding )
{
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_SLIDING;
// Get our bodies
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::SliderConstraintSettings settings;
settings.SetPoint( *refBody, *attBody );
settings.SetSliderAxis( JPH::Vec3( sliding.slideAxisRef.x, sliding.slideAxisRef.y, sliding.slideAxisRef.z ) );
if ( sliding.limitMin != sliding.limitMax )
{
settings.mLimitsMin = SourceToJolt::Distance( sliding.limitMin );
settings.mLimitsMax = SourceToJolt::Distance( sliding.limitMax );
}
settings.mMaxFrictionForce = sliding.friction;
m_pConstraint = settings.Create( *refBody, *attBody );
m_pConstraint->SetEnabled( !pGroup && sliding.constraint.isActive );
if ( sliding.velocity )
{
JPH::SliderConstraint *pConstraint = static_cast<JPH::SliderConstraint *>( m_pConstraint );
pConstraint->SetMotorState( JPH::EMotorState::Velocity );
pConstraint->SetTargetVelocity( SourceToJolt::Distance( sliding.velocity ) );
}
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Ballsocket
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::InitialiseBallsocket( IPhysicsConstraintGroup *pGroup, const constraint_ballsocketparams_t &ballsocket )
{
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_BALLSOCKET;
// Get our bodies
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::PointConstraintSettings settings;
settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
settings.mPoint1 = SourceToJolt::Distance( ballsocket.constraintPosition[0] ) - refBody->GetShape()->GetCenterOfMass();
settings.mPoint2 = SourceToJolt::Distance( ballsocket.constraintPosition[1] ) - attBody->GetShape()->GetCenterOfMass();
m_pConstraint = settings.Create( *refBody, *attBody );
m_pConstraint->SetEnabled( !pGroup && ballsocket.constraint.isActive );
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Fixed
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::InitialiseFixed( IPhysicsConstraintGroup *pGroup, const constraint_fixedparams_t &fixed )
{
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_FIXED;
// Get our bodies
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::FixedConstraintSettings settings;
settings.SetPoint( *refBody, *attBody );
m_pConstraint = settings.Create( *refBody, *attBody );
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Length
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::InitialiseLength( IPhysicsConstraintGroup *pGroup, const constraint_lengthparams_t &length )
{
SetGroup( pGroup );
m_ConstraintType = CONSTRAINT_LENGTH;
// Get our bodies
JPH::Body *refBody = m_pObjReference->GetBody();
JPH::Body *attBody = m_pObjAttached->GetBody();
JPH::DistanceConstraintSettings settings;
settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
settings.mPoint1 = SourceToJolt::Distance( length.objectPosition[0] ) - refBody->GetShape()->GetCenterOfMass();
settings.mPoint2 = SourceToJolt::Distance( length.objectPosition[1] ) - attBody->GetShape()->GetCenterOfMass();
settings.mMinDistance = SourceToJolt::Distance( length.minLength );
settings.mMaxDistance = SourceToJolt::Distance( length.totalLength );
// Josh: UNDONE! Nothing seems to use strength on length ever
// after analysing the codebase.
//
//settings.mFrequency = 1.0f - length.constraint.strength;
//if ( settings.mFrequency )
// settings.mDamping = 1.0f;
m_pConstraint = settings.Create( *refBody, *attBody );
m_pConstraint->SetEnabled( !pGroup && length.constraint.isActive );
m_pPhysicsSystem->AddConstraint( m_pConstraint );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::SaveConstraintSettings( JPH::StateRecorder &recorder )
{
recorder.Write( m_ConstraintType );
auto settings = m_pConstraint->GetConstraintSettings();
settings->SaveBinaryState( recorder );
m_pConstraint->SaveState( recorder );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsConstraint::SetGroup( IPhysicsConstraintGroup *pGroup )
{
if ( m_pGroup )
m_pGroup->RemoveConstraint( this );
m_pGroup = static_cast< JoltPhysicsConstraintGroup * >( pGroup );
if ( m_pGroup )
m_pGroup->AddConstraint( this );
}
void JoltPhysicsConstraint::DestroyConstraint()
{
if ( m_pObjAttached )
{
m_pObjAttached->RemoveDestroyedListener( this );
m_pObjAttached = nullptr;
}
if ( m_pObjReference )
{
m_pObjReference->RemoveDestroyedListener( this );
m_pObjReference = nullptr;
}
if ( m_pConstraint )
{
m_pPhysicsSystem->RemoveConstraint( m_pConstraint );
m_pConstraint->Release();
m_pConstraint = nullptr;
}
}

View File

@ -0,0 +1,101 @@
//=================================================================================================
//
// Constraints
//
//=================================================================================================
#pragma once
#include "vjolt_internal_listeners.h"
enum constraintType_t
{
CONSTRAINT_UNKNOWN = 0,
CONSTRAINT_RAGDOLL,
CONSTRAINT_HINGE,
CONSTRAINT_FIXED,
CONSTRAINT_BALLSOCKET,
CONSTRAINT_SLIDING,
CONSTRAINT_PULLEY,
CONSTRAINT_LENGTH,
};
class JoltPhysicsConstraint;
class JoltPhysicsEnvironment;
class JoltPhysicsConstraintGroup final : public IPhysicsConstraintGroup
{
public:
JoltPhysicsConstraintGroup();
~JoltPhysicsConstraintGroup() override;
void Activate() override;
bool IsInErrorState() override;
void ClearErrorState() override;
void GetErrorParams( constraint_groupparams_t *pParams ) override;
void SetErrorParams( const constraint_groupparams_t &params ) override;
void SolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1 ) override;
void AddConstraint( JoltPhysicsConstraint *pConstraint );
void RemoveConstraint( JoltPhysicsConstraint *pConstraint );
private:
std::vector< JoltPhysicsConstraint * > m_pConstraints;
constraint_groupparams_t m_ErrorParams = {};
};
class JoltPhysicsConstraint final : public IPhysicsConstraint, public IJoltObjectDestroyedListener
{
public:
JoltPhysicsConstraint( JoltPhysicsEnvironment *pPhysicsEnvironment, IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, constraintType_t Type = CONSTRAINT_UNKNOWN, JPH::Constraint* pConstraint = nullptr, void *pGameData = nullptr );
~JoltPhysicsConstraint() override;
void Activate() override;
void Deactivate() override;
void SetGameData( void *gameData ) override;
void * GetGameData() const override;
IPhysicsObject *GetReferenceObject() const override;
IPhysicsObject *GetAttachedObject() const override;
void SetLinearMotor( float speed, float maxLinearImpulse ) override;
void SetAngularMotor( float rotSpeed, float maxAngularImpulse ) override;
void UpdateRagdollTransforms( const matrix3x4_t &constraintToReference, const matrix3x4_t &constraintToAttached ) override;
bool GetConstraintTransform( matrix3x4_t *pConstraintToReference, matrix3x4_t *pConstraintToAttached ) const override;
bool GetConstraintParams( constraint_breakableparams_t *pParams ) const override;
void OutputDebugInfo() override;
// IJoltObjectDestroyedListener
void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override;
public:
bool InitialiseHingeFromRagdoll( IPhysicsConstraintGroup* pGroup, const constraint_ragdollparams_t& ragdoll );
void InitialiseRagdoll( IPhysicsConstraintGroup *pGroup, const constraint_ragdollparams_t &ragdoll );
void InitialiseHinge( IPhysicsConstraintGroup *pGroup, const constraint_hingeparams_t &hinge );
void InitialiseSliding( IPhysicsConstraintGroup *pGroup, const constraint_slidingparams_t &sliding );
void InitialiseBallsocket( IPhysicsConstraintGroup *pGroup, const constraint_ballsocketparams_t &ballsocket );
void InitialiseFixed( IPhysicsConstraintGroup *pGroup, const constraint_fixedparams_t &fixed );
void InitialiseLength( IPhysicsConstraintGroup *pGroup, const constraint_lengthparams_t &length );
void SaveConstraintSettings( JPH::StateRecorder &recorder );
private:
void SetGroup( IPhysicsConstraintGroup *pGroup );
void DestroyConstraint();
JoltPhysicsObject *m_pObjReference = nullptr;
JoltPhysicsObject *m_pObjAttached = nullptr;
JPH::Constraint *m_pConstraint = nullptr;
constraintType_t m_ConstraintType = CONSTRAINT_UNKNOWN;
JoltPhysicsConstraintGroup *m_pGroup = nullptr;
void *m_pGameData = nullptr;
JoltPhysicsEnvironment *m_pPhysicsEnvironment = nullptr;
JPH::PhysicsSystem *m_pPhysicsSystem = nullptr;
};

View File

@ -0,0 +1,227 @@
#include "cbase.h"
#include "vjolt_layers.h"
#include "vjolt_collide.h"
#include "vjolt_object.h"
#include "vjolt_environment.h"
#include "vjolt_surfaceprops.h"
#include "vjolt_controller_fluid.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
// Josh: The surfacePlane in fluidparams_t is specified in world-space, but we need to translate
// that to the pFluidObject's local-space so we can handle the fluid object moving
// for eg. func_water_analog.
static cplane_t PlaneToLocalSpace( JoltPhysicsObject* pFluidObject, Vector4D worldSurfacePlane )
{
const cplane_t worldPlane = { worldSurfacePlane.AsVector3D(), worldSurfacePlane[3] };
matrix3x4_t objectToWorld;
pFluidObject->GetPositionMatrix( &objectToWorld );
cplane_t localPlane;
MatrixITransformPlane( objectToWorld, worldPlane, localPlane );
return localPlane;
}
//-------------------------------------------------------------------------------------------------
JoltPhysicsFluidController::JoltPhysicsFluidController( JPH::PhysicsSystem *pPhysicsSystem, JoltPhysicsObject *pFluidObject, const fluidparams_t *pParams )
: m_pPhysicsSystem( pPhysicsSystem )
, m_pFluidObject( pFluidObject )
, m_Params( *pParams )
, m_LocalPlane( PlaneToLocalSpace( pFluidObject, pParams->surfacePlane ) )
{
m_pFluidObject->BecomeTrigger();
m_pFluidObject->SetFluidController( this );
m_pFluidObject->AddDestroyedListener( this );
}
JoltPhysicsFluidController::~JoltPhysicsFluidController()
{
ClearCachedObjectsInShape();
if ( m_pFluidObject )
{
m_pFluidObject->RemoveDestroyedListener( this );
m_pFluidObject->SetFluidController( nullptr );
m_pFluidObject->RemoveTrigger();
}
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFluidController::SetGameData( void *pGameData )
{
m_Params.pGameData = pGameData;
}
void *JoltPhysicsFluidController::GetGameData() const
{
return m_Params.pGameData;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFluidController::GetSurfacePlane( Vector *pNormal, float *pDist ) const
{
const cplane_t worldPlane = GetSurfacePlane();
if ( pNormal )
*pNormal = worldPlane.normal;
if ( pDist )
*pDist = worldPlane.dist;
}
float JoltPhysicsFluidController::GetDensity() const
{
// TODO(Josh): We know the material density, but what units should this be in?
return 1.0f;
}
void JoltPhysicsFluidController::WakeAllSleepingObjects()
{
for ( JoltPhysicsObject *pObject : m_ObjectsInShape )
pObject->Wake();
}
int JoltPhysicsFluidController::GetContents() const
{
return m_Params.contents;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFluidController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject )
{
if ( pObject == m_pFluidObject )
m_pFluidObject = nullptr;
std::erase_if( m_ObjectsInShape, [pObject]( JoltPhysicsObject *pCachedObject ) { return pObject == pCachedObject; } );
}
//-------------------------------------------------------------------------------------------------
// Applies buoyancy to any body that intersects with the water shape
class SourceFluidCollector : public JPH::CollideShapeCollector
{
public:
SourceFluidCollector( JPH::PhysicsSystem *inSystem, std::vector<JoltPhysicsObject *> &objectsInShape, const JPH::Plane &inSurface, float inDeltaTime, const fluidparams_t &params, float flDensity )
: m_pPhysicsSystem ( inSystem )
, m_ObjectsInShape ( objectsInShape )
, m_Surface ( inSurface )
, m_DeltaTime ( inDeltaTime )
, m_Params ( params )
, m_flDensity ( flDensity ) {}
void AddHit( const ResultType &inResult ) override
{
const JPH::BodyID inBodyID = inResult.mBodyID2;
JPH::BodyLockWrite lock( m_pPhysicsSystem->GetBodyLockInterface(), inBodyID );
JPH::Body &body = lock.GetBody();
JoltPhysicsObject *pObject = reinterpret_cast<JoltPhysicsObject *>( body.GetUserData() );
// Don't do fluid simulation for these.
if ( pObject->GetShadowController() || !( pObject->GetCallbackFlags() & CALLBACK_DO_FLUID_SIMULATION ) )
return;
m_ObjectsInShape.push_back( pObject );
// The original VPhysics ignores m_Params.torqueFactor and always has 0.01.
// But I think 0.05 looks better.
static constexpr float flTorqueFactor = 0.05f;
// Josh:
// The buoyancy ratio in Source works like this:
// fluid_density = m_flDensity * pObject->GetBuoyancyRatio()
// but in Jolt, it gets the fluid density like this (you pass in inBuoyancy)
// fluid_density = inBuoyancy / (total_volume * inverse_mass);
// so with some rearranging...
// inBuoyancy = fluid_density * (total_volume * inverse_mass);
const float flFluidDensity = m_flDensity * pObject->GetBuoyancyRatio();
float inBuoyancy = flFluidDensity * pObject->GetBody()->GetShape()->GetVolume() * pObject->GetInvMass();
if ( body.IsActive() )
body.ApplyBuoyancyImpulse( m_Surface, inBuoyancy, 0.3f /* m_Params.damping */, flTorqueFactor, SourceToJolt::Distance( m_Params.currentVelocity ), m_pPhysicsSystem->GetGravity(), m_DeltaTime);
}
private:
JPH::PhysicsSystem * m_pPhysicsSystem;
std::vector<JoltPhysicsObject *> &m_ObjectsInShape;
JPH::Plane m_Surface;
float m_DeltaTime;
const fluidparams_t &m_Params;
float m_flDensity;
};
void JoltPhysicsFluidController::OnPreSimulate( float deltaTime )
{
// Clear out our last list of items
ClearCachedObjectsInShape();
if ( !m_pFluidObject )
return;
const cplane_t surfacePlane = GetSurfacePlane();
JPH::Plane surface = JPH::Plane(
SourceToJolt::Unitless( surfacePlane.normal ),
SourceToJolt::Distance( -surfacePlane.dist ) );
const float flDensity = m_pFluidObject->GetMaterialDensity();
SourceFluidCollector collector( m_pPhysicsSystem, m_ObjectsInShape, surface, deltaTime, m_Params, flDensity );
// Apply buoyancy to all bodies that intersect with the water
JPH::CollideShapeSettings collideSettings;
collideSettings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideOnlyWithActive;
// Ignore my own body
// TODO(Josh): Should we use the SourceHitFilter from the player controller here?
// May make more sense and respect ShouldCollide. Not sure if regular VPhysics does here.
JPH::IgnoreSingleBodyFilter body_filter( m_pFluidObject->GetBodyID() );
JPH::BodyInterface &bodyInterface = m_pPhysicsSystem->GetBodyInterfaceNoLock();
JPH::Mat44 queryTransform = bodyInterface.GetCenterOfMassTransform( m_pFluidObject->GetBodyID() );
const JPH::Shape *pShape = m_pFluidObject->GetCollide()->ToShape();
m_pPhysicsSystem->GetNarrowPhaseQueryNoLock().CollideShape(
pShape, JPH::Vec3::sReplicate( 1.0f ), queryTransform, collideSettings, collector,
JPH::SpecifiedBroadPhaseLayerFilter( BroadPhaseLayers::MOVING ), JPH::SpecifiedObjectLayerFilter( Layers::MOVING ), body_filter );
for ( JoltPhysicsObject *pObject : m_ObjectsInShape )
pObject->AddDestroyedListener( this );
}
//-------------------------------------------------------------------------------------------------
cplane_t JoltPhysicsFluidController::GetSurfacePlane() const
{
if ( !m_pFluidObject )
return cplane_t{};
matrix3x4_t objectToWorld;
m_pFluidObject->GetPositionMatrix( &objectToWorld );
cplane_t worldPlane;
MatrixTransformPlane( objectToWorld, m_LocalPlane, worldPlane );
return worldPlane;
}
void JoltPhysicsFluidController::ClearCachedObjectsInShape()
{
// TODO(Josh): This could maybe be made more efficient by having two vectors
// and only updating the listeners on the ones we need to, then std::move-ing.
for ( JoltPhysicsObject *pObject : m_ObjectsInShape )
pObject->RemoveDestroyedListener( this );
m_ObjectsInShape.clear();
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "vjolt_internal_listeners.h"
class JoltPhysicsFluidController final : public IPhysicsFluidController, public IJoltObjectDestroyedListener, public IJoltPhysicsController
{
public:
JoltPhysicsFluidController( JPH::PhysicsSystem *pPhysicsSystem, JoltPhysicsObject *pFluidObject, const fluidparams_t *pParams );
~JoltPhysicsFluidController() override;
void SetGameData( void *pGameData ) override;
void * GetGameData() const override;
void GetSurfacePlane( Vector *pNormal, float *pDist ) const override;
float GetDensity() const override;
void WakeAllSleepingObjects() override;
int GetContents() const override;
public:
// IJoltObjectDestroyedListener
void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override;
// IJoltPhysicsController
void OnPreSimulate( float flDeltaTime ) override;
private:
cplane_t GetSurfacePlane() const;
void ClearCachedObjectsInShape();
JPH::PhysicsSystem * m_pPhysicsSystem;
JoltPhysicsObject * m_pFluidObject;
std::vector<JoltPhysicsObject *> m_ObjectsInShape;
fluidparams_t m_Params;
cplane_t m_LocalPlane;
};

View File

@ -0,0 +1,152 @@
#include "cbase.h"
#include "vjolt_controller_motion.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
JoltPhysicsMotionController::JoltPhysicsMotionController( IMotionEvent *pHandler )
: m_pMotionEvent( pHandler )
{
}
JoltPhysicsMotionController::~JoltPhysicsMotionController()
{
for ( JoltPhysicsObject *pObject : m_pObjects )
pObject->RemoveDestroyedListener( this );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsMotionController::SetEventHandler( IMotionEvent *pHandler )
{
m_pMotionEvent = pHandler;
}
void JoltPhysicsMotionController::AttachObject( IPhysicsObject *pObject, bool bCheckIfAlreadyAttached )
{
if ( !pObject || pObject->IsStatic() )
return;
JoltPhysicsObject *pPhysicsObject = static_cast< JoltPhysicsObject * >( pObject );
if ( bCheckIfAlreadyAttached && m_pObjects.HasElement( pPhysicsObject ) )
return;
pPhysicsObject->AddDestroyedListener( this );
m_pObjects.AddToTail( pPhysicsObject );
}
void JoltPhysicsMotionController::DetachObject( IPhysicsObject *pObject )
{
if ( !pObject )
return;
JoltPhysicsObject *pPhysicsObject = static_cast< JoltPhysicsObject * >( pObject );
m_pObjects.FindAndRemove( pPhysicsObject );
pPhysicsObject->RemoveDestroyedListener( this );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsMotionController::CountObjects( void )
{
return m_pObjects.Count();
}
void JoltPhysicsMotionController::GetObjects( IPhysicsObject **pObjectList )
{
for ( int i = 0; i < m_pObjects.Count(); i++ )
pObjectList[ i ] = m_pObjects[ i ];
}
void JoltPhysicsMotionController::ClearObjects( void )
{
m_pObjects.RemoveAll();
}
void JoltPhysicsMotionController::WakeObjects( void )
{
for ( JoltPhysicsObject *pObject : m_pObjects )
pObject->Wake();
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsMotionController::SetPriority( priority_t priority )
{
// Not relevant to us.
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsMotionController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject )
{
JoltPhysicsObject *pPhysicsObject = static_cast< JoltPhysicsObject * >( pObject );
m_pObjects.FindAndRemove( pPhysicsObject );
}
void JoltPhysicsMotionController::OnPreSimulate( float flDeltaTime )
{
if ( !m_pMotionEvent )
return;
for ( JoltPhysicsObject *pObject : m_pObjects )
{
if ( !pObject->IsMoveable() )
return;
Vector vecLocalVelocity = vec3_origin;
Vector vecAngularVelocity = vec3_origin;
IMotionEvent::simresult_e simResult = m_pMotionEvent->Simulate( this, pObject, flDeltaTime, vecLocalVelocity, vecAngularVelocity );
vecLocalVelocity *= flDeltaTime;
vecAngularVelocity *= flDeltaTime;
// Convert linear velocity to world space
Vector vecWorldLinearVelocity = vec3_origin;
pObject->LocalToWorldVector( &vecWorldLinearVelocity, vecLocalVelocity );
switch ( simResult )
{
case IMotionEvent::SIM_NOTHING:
{
break;
}
case IMotionEvent::SIM_LOCAL_ACCELERATION:
{
pObject->AddVelocity( &vecWorldLinearVelocity, &vecAngularVelocity );
break;
}
case IMotionEvent::SIM_LOCAL_FORCE:
{
pObject->ApplyForceCenter( vecWorldLinearVelocity );
pObject->ApplyTorqueCenter( vecAngularVelocity );
break;
}
case IMotionEvent::SIM_GLOBAL_ACCELERATION:
{
pObject->AddVelocity( &vecLocalVelocity, &vecAngularVelocity );
break;
}
case IMotionEvent::SIM_GLOBAL_FORCE:
{
pObject->ApplyForceCenter( vecLocalVelocity );
pObject->ApplyTorqueCenter( vecAngularVelocity );
break;
}
default:
{
Log_Warning( LOG_VJolt, "Invalid motion event\n" );
break;
}
}
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "vjolt_object.h"
#include "vjolt_environment.h"
class JoltPhysicsMotionController : public IPhysicsMotionController, public IJoltObjectDestroyedListener, public IJoltPhysicsController
{
public:
JoltPhysicsMotionController( IMotionEvent *pHandler );
~JoltPhysicsMotionController() override;
void SetEventHandler( IMotionEvent *handler ) override;
void AttachObject( IPhysicsObject *pObject, bool checkIfAlreadyAttached ) override;
void DetachObject( IPhysicsObject *pObject ) override;
int CountObjects( void ) override;
void GetObjects( IPhysicsObject **pObjectList ) override;
void ClearObjects( void ) override;
void WakeObjects( void ) override;
void SetPriority( priority_t priority ) override;
public:
void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override;
void OnPreSimulate( float flDeltaTime ) override;
private:
IMotionEvent *m_pMotionEvent;
CUtlVector< JoltPhysicsObject * > m_pObjects;
};

View File

@ -0,0 +1,437 @@
#include "cbase.h"
#include "vjolt_layers.h"
#include "vjolt_controller_player.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
static ConVar vjolt_player_collision_tolerance( "vjolt_player_collision_tolerance", "0.05" );
//-------------------------------------------------------------------------------------------------
JoltPhysicsPlayerController::JoltPhysicsPlayerController( JoltPhysicsObject *pObject )
{
SetObjectInternal( pObject );
}
JoltPhysicsPlayerController::~JoltPhysicsPlayerController()
{
SetObjectInternal( nullptr );
SetGround( nullptr );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsPlayerController::Update( const Vector &position, const Vector &velocity, float secondsToArrival, bool onground, IPhysicsObject *ground )
{
// timeOffset == secondsToArrival
const JPH::Vec3 targetPosition = SourceToJolt::Distance( position );
if ( targetPosition.IsClose( m_targetPosition ) )
return;
const JPH::Vec3 targetVelocity = SourceToJolt::Distance( velocity );
m_targetPosition = targetPosition;
m_targetVelocity = targetVelocity;
m_secondsToArrival = secondsToArrival;
// Bogus assertion: onground can be true and ground can be null when touching the world. That is okay
//VJoltAssert( ( onground && ground ) || ( !onground && !ground ) );
SetGround( static_cast<JoltPhysicsObject *>( ground ) );
}
void JoltPhysicsPlayerController::SetEventHandler( IPhysicsPlayerControllerEvent *handler )
{
m_pHandler = handler;
}
bool JoltPhysicsPlayerController::IsInContact()
{
uint32 nState = GetContactState( 0 );
return !!( nState & PLAYER_CONTACT_PHYSICS );
}
void JoltPhysicsPlayerController::MaxSpeed( const Vector &velocity )
{
// Do we have to care about this? IVP used a rigid body for the player shadow because it didn't
// have the concept of kinematic objects, our Jolt shadow follows the player exactly and
// is kinematic so if the game follows this max speed limit we don't need to care.
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsPlayerController::SetObject( IPhysicsObject *pObject )
{
SetObjectInternal( static_cast<JoltPhysicsObject *>( pObject ) );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsPlayerController::GetShadowPosition( Vector *position, QAngle *angles )
{
return m_pObject->GetShadowPosition( position, angles );
}
void JoltPhysicsPlayerController::StepUp( float height )
{
if ( height == 0.0f )
return;
// Since the player is a kinematic object that slides around the world using velocity, when
// stepping up onto a platform we need to go there instantly, AddToPosition does that.
m_pObject->AddToPosition( JPH::Vec3( 0.0f, 0.0f, SourceToJolt::Distance( height ) ) );
}
void JoltPhysicsPlayerController::Jump()
{
// This does nothing in VPhysics.
}
void JoltPhysicsPlayerController::GetShadowVelocity( Vector *velocity )
{
if ( !velocity )
return;
JPH::Vec3 jphVelocity = m_pObject->GetBody()->GetLinearVelocity();
if ( m_pGround )
{
jphVelocity -= m_pGround->GetBody()->GetPointVelocity( m_groundPos );
}
*velocity = JoltToSource::Distance( jphVelocity );
}
IPhysicsObject *JoltPhysicsPlayerController::GetObject()
{
return m_pObject;
}
void JoltPhysicsPlayerController::GetLastImpulse( Vector *pOut )
{
VectorClear( *pOut );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsPlayerController::SetPushMassLimit( float maxPushMass )
{
m_flPushableMassLimit = maxPushMass;
}
void JoltPhysicsPlayerController::SetPushSpeedLimit( float maxPushSpeed )
{
m_flPushableSpeedLimit = maxPushSpeed;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsPlayerController::GetPushMassLimit()
{
return m_flPushableMassLimit;
}
float JoltPhysicsPlayerController::GetPushSpeedLimit()
{
return m_flPushableSpeedLimit;
}
//-------------------------------------------------------------------------------------------------
bool JoltPhysicsPlayerController::WasFrozen()
{
// I think here the code is referring to IVP freezing objects after inactivity (sleeping),
// our objects are forced to never sleep, so we don't need to care?
return false;
}
//-------------------------------------------------------------------------------------------------
static void CheckCollision( JoltPhysicsObject *pObject, JPH::CollideShapeCollector &ioCollector, JPH::BodyFilter &ioFilter )
{
JPH::PhysicsSystem *pSystem = pObject->GetEnvironment()->GetPhysicsSystem();
// TODO(Josh): Make a PLAYER ONLY layer that will only collide with MOVING ONLY annd
// NOTHING ELSE tomorrow.
// Create query broadphase layer filter
JPH::DefaultBroadPhaseLayerFilter broadphase_layer_filter = pSystem->GetDefaultBroadPhaseLayerFilter( Layers::MOVING );
// Create query object layer filter
JPH::DefaultObjectLayerFilter object_layer_filter = pSystem->GetDefaultLayerFilter( Layers::MOVING );
// Determine position to test
JPH::Vec3 position;
JPH::Quat rotation;
JPH::BodyInterface &bi = pSystem->GetBodyInterfaceNoLock();
bi.GetPositionAndRotation( pObject->GetBodyID(), position, rotation );
JPH::Mat44 query_transform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pObject->GetBody()->GetShape()->GetCenterOfMass() );
// Settings for collide shape
JPH::CollideShapeSettings settings;
settings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideOnlyWithActive;
settings.mActiveEdgeMovementDirection = bi.GetLinearVelocity( pObject->GetBodyID() );
settings.mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces;
settings.mMaxSeparationDistance = vjolt_player_collision_tolerance.GetFloat();
pSystem->GetNarrowPhaseQueryNoLock().CollideShape( pObject->GetBody()->GetShape(), JPH::Vec3::sReplicate( 1.0f ), query_transform, settings, ioCollector, broadphase_layer_filter, object_layer_filter, ioFilter );
}
// Slart: This is a version of CheckCollision that projects the player by their velocity, to attempt to push objects that we'll walk into soon
#if 0
static void CheckCollision2( JoltPhysicsObject *pObject, JPH::CollideShapeCollector &ioCollector, const JPH::Vec3Arg targetVelocity, float flDeltaTime )
{
JPH::PhysicsSystem *pSystem = pObject->GetEnvironment()->GetPhysicsSystem();
// TODO(Josh): Make a PLAYER ONLY layer that will only collide with MOVING ONLY annd
// NOTHING ELSE tomorrow.
// Create query broadphase layer filter
JPH::DefaultBroadPhaseLayerFilter broadphase_layer_filter = pSystem->GetDefaultBroadPhaseLayerFilter( Layers::MOVING );
// Create query object layer filter
JPH::DefaultObjectLayerFilter object_layer_filter = pSystem->GetDefaultLayerFilter( Layers::MOVING );
// Ignore my own body
JPH::IgnoreSingleBodyFilter body_filter( pObject->GetBodyID() );
// Determine position to test
JPH::Vec3 position;
JPH::Quat rotation;
JPH::BodyInterface &bi = pSystem->GetBodyInterfaceNoLock();
bi.GetPositionAndRotation( pObject->GetBodyID(), position, rotation );
position += targetVelocity * ( flDeltaTime * 2.0f );
JPH::Mat44 query_transform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pObject->GetBody()->GetShape()->GetCenterOfMass() );
// Settings for collide shape
JPH::CollideShapeSettings settings;
settings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideOnlyWithActive;
settings.mActiveEdgeMovementDirection = bi.GetLinearVelocity( pObject->GetBodyID() );
settings.mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces;
settings.mMaxSeparationDistance = vjolt_player_collision_tolerance.GetFloat();
pSystem->GetNarrowPhaseQueryNoLock().CollideShape( pObject->GetBody()->GetShape(), JPH::Vec3::sReplicate( 1.0f ), query_transform, settings, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter );
}
#endif
template <bool MoveablesOnly>
class SourceHitFilter : public JPH::BodyFilter
{
public:
SourceHitFilter( JPH::PhysicsSystem* pPhysicsSystem, JoltPhysicsObject* pSelfObject )
: m_pPhysicsSystem( pPhysicsSystem )
, m_pSelfObject( pSelfObject )
{
}
bool ShouldCollideLocked( const JPH::Body &inBody ) const override
{
JoltPhysicsObject* pObject = reinterpret_cast<JoltPhysicsObject*>( inBody.GetUserData() );
// Ignore self if specified. This can be nullptr if you don't want this.
if ( pObject == m_pSelfObject )
return false;
if constexpr ( MoveablesOnly )
{
if ( pObject->IsTrigger() || !pObject->IsMoveable() )
return false;
}
if ( !pObject->GetEnvironment()->GetContactListener()->ShouldCollide( m_pSelfObject, pObject ) )
return false;
return true;
}
private:
JPH::PhysicsSystem *m_pPhysicsSystem;
JoltPhysicsObject *m_pSelfObject;
};
uint32 JoltPhysicsPlayerController::GetContactState( uint16 nGameFlags )
{
// This does not seem to affect much, we should aspire to have our physics be as 1:1 to brush collisions as possible anyway
#ifdef GAME_PORTAL2_OR_NEWER
if ( !m_pObject->IsCollisionEnabled() )
return 0;
// Collector that finds the hit with the normal that is the most 'up'
class ContactStateCollector : public JPH::CollideShapeCollector
{
public:
ContactStateCollector( JPH::PhysicsSystem *pPhysicsSystem, JoltPhysicsObject *pPlayerObject, uint16 nGameFlags )
: m_pPhysicsSystem( pPhysicsSystem )
, m_pPlayerObject( pPlayerObject )
, m_nGameFlags( nGameFlags )
{
}
void AddHit( const JPH::CollideShapeResult &inResult ) override
{
JPH::BodyLockRead lock( m_pPhysicsSystem->GetBodyLockInterfaceNoLock(), inResult.mBodyID2 );
const JPH::Body &body = lock.GetBody();
JoltPhysicsObject *pObject = reinterpret_cast<JoltPhysicsObject *>( body.GetUserData() );
if ( !pObject->IsControlledByGame() )
m_nFlagsOut |= PLAYER_CONTACT_PHYSICS;
if ( pObject->GetGameFlags() & m_nGameFlags )
m_nFlagsOut |= PLAYER_CONTACT_GAMEOBJECT;
}
uint32 m_nFlagsOut = 0;
private:
JPH::PhysicsSystem *m_pPhysicsSystem;
JoltPhysicsObject *m_pPlayerObject;
uint16 m_nGameFlags;
};
JPH::PhysicsSystem *pSystem = m_pObject->GetEnvironment()->GetPhysicsSystem();
ContactStateCollector collector( pSystem, m_pObject, nGameFlags );
SourceHitFilter<true> filter( pSystem, m_pObject );
CheckCollision( m_pObject, collector, filter );
return collector.m_nFlagsOut;
#else
return 0;
#endif
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsPlayerController::OnPreSimulate( float flDeltaTime )
{
VJoltAssertMsg( m_pObject->GetBody()->GetMotionType() == JPH::EMotionType::Kinematic, "Shadow controllers must be kinematic!" );
#if 0
if ( m_pGround )
{
JPH::Mat44 matrix = JPH::Mat44::sRotationTranslation( m_pObject->GetBody()->GetRotation(), m_pObject->GetBody()->GetPosition() ).Transposed3x3();
m_groundPos = -matrix.Multiply3x3( m_targetPosition );
matrix3x4_t mat;
m_pGround->GetPositionMatrix( &mat );
m_targetPosition = mat.TransformVector( m_GroundPos );
m_pGround->GetVelocityAtPoint( m_GroundPos, &groundVelocity );
m_pObject->AddVelocity( -groundVelocity );
}
#else
/*if ( m_pGround )
{
JPH::Mat44 matrix = JPH::Mat44::sRotationTranslation( m_pObject->GetBody()->GetRotation(), m_pObject->GetBody()->GetPosition() ).Transposed3x3();
JPH::Vec3 groundPos = -matrix.Multiply3x3( m_targetPosition );
int g = 5;
m_targetPosition = matrix * groundPos;
}*/
#endif
// Apply downwards force to the ground
// This code mimics JoltObject::ApplyForceOffset but without Source > Jolt conversions
/*if ( m_pGround && m_pGround->IsMoveable() )
{
JPH::PhysicsSystem *pPhysicsSystem = m_pGround->GetEnvironment()->GetPhysicsSystem();
JPH::BodyInterface &bodyInterface = pPhysicsSystem->GetBodyInterfaceNoLock();
bodyInterface.AddImpulse( m_pGround->GetBodyID(), pPhysicsSystem->GetGravity() * m_pObject->GetMass() * flDeltaTime, m_pObject->GetBody()->GetPosition() );
}*/
JPH::PhysicsSystem *pPhysicsSystem = m_pObject->GetEnvironment()->GetPhysicsSystem();
JPH::BodyInterface &bodyInterface = pPhysicsSystem->GetBodyInterfaceNoLock();
// Project ourselves towards our velocity
JPH::AnyHitCollisionCollector<JPH::CollideShapeCollector> collector;
SourceHitFilter<true> filter( pPhysicsSystem, m_pObject );
CheckCollision( m_pObject, collector, filter );
if ( collector.HadHit() )
{
JPH::BodyID otherID = collector.mHit.mBodyID2;
//bodyInterface.AddImpulse( otherID, m_pObject->GetMass() * m_targetVelocity * flDeltaTime, m_pObject->GetBody()->GetPosition() );
bodyInterface.AddImpulse( otherID, m_pObject->GetMass() * pPhysicsSystem->GetGravity() * flDeltaTime, m_pObject->GetBody()->GetPosition());
}
if ( m_secondsToArrival > 0.0f )
bodyInterface.MoveKinematic( m_pObject->GetBodyID(), m_targetPosition, JPH::Quat::sIdentity(), m_secondsToArrival );
else
{
bodyInterface.SetPositionAndRotation( m_pObject->GetBodyID(), m_targetPosition, JPH::Quat::sIdentity(), JPH::EActivation::Activate );
bodyInterface.SetLinearAndAngularVelocity( m_pObject->GetBodyID(), JPH::Vec3::sZero(), JPH::Vec3::sZero() );
}
m_secondsToArrival = Max( m_secondsToArrival - flDeltaTime, 0.0f );
}
void JoltPhysicsPlayerController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject )
{
if ( pObject == m_pObject )
{
SetObjectInternal( nullptr );
}
if ( pObject == m_pGround )
{
SetGround( nullptr );
}
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsPlayerController::SetObjectInternal( JoltPhysicsObject *pObject )
{
if ( m_pObject == pObject )
return;
// Reset the last object
if ( m_pObject )
{
// Don't bother resetting kinematic or sleep state, it does not matter because
// any object tied to a player controller was created to be a player object
m_pObject->RemoveDestroyedListener( this );
m_pObject->RemoveCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER );
}
// Set our new object
m_pObject = pObject;
// Adjust the new object
if ( m_pObject )
{
// Set kinematic
m_pObject->GetBody()->SetMotionType( JPH::EMotionType::Kinematic );
m_pObject->GetBody()->SetAllowSleeping( false );
m_pObject->AddDestroyedListener( this );
m_pObject->AddCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER );
}
}
void JoltPhysicsPlayerController::SetGround( JoltPhysicsObject *pGround )
{
if ( m_pGround == pGround )
return;
if ( m_pGround )
{
m_pGround->RemoveDestroyedListener( this );
}
// Set our new ground
m_pGround = pGround;
if ( m_pGround )
{
m_pGround->AddDestroyedListener( this );
}
}

View File

@ -0,0 +1,70 @@
#pragma once
#include "vjolt_object.h"
#include "vjolt_environment.h"
class JoltPhysicsPlayerController : public IPhysicsPlayerController, public IJoltObjectDestroyedListener, public IJoltPhysicsController
{
public:
JoltPhysicsPlayerController( JoltPhysicsObject *pObject );
~JoltPhysicsPlayerController() override;
void Update( const Vector &position, const Vector &velocity, float secondsToArrival, bool onground, IPhysicsObject *ground ) override;
void SetEventHandler( IPhysicsPlayerControllerEvent *handler ) override;
bool IsInContact( void ) override;
void MaxSpeed( const Vector &maxVelocity ) override;
void SetObject( IPhysicsObject *pObject ) override;
int GetShadowPosition( Vector *position, QAngle *angles ) override;
void StepUp( float height ) override;
void Jump() override;
void GetShadowVelocity( Vector *velocity ) override;
IPhysicsObject *GetObject() override;
void GetLastImpulse( Vector *pOut ) override;
void SetPushMassLimit( float maxPushMass ) override;
void SetPushSpeedLimit( float maxPushSpeed ) override;
float GetPushMassLimit() override;
float GetPushSpeedLimit() override;
bool WasFrozen() override;
uint32 GetContactState( uint16 nGameFlags ) override_portal2;
// IJoltObjectDestroyedListener
void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override;
// IJoltPhysicsController
void OnPreSimulate( float flDeltaTime ) override;
private:
void SetObjectInternal( JoltPhysicsObject *pObject );
void SetGround( JoltPhysicsObject *pObject );
private:
JoltPhysicsObject *m_pObject = nullptr;
IPhysicsPlayerControllerEvent *m_pHandler = nullptr;
JoltPhysicsObject *m_pGround = nullptr;
JPH::Vec3 m_groundPos = JPH::Vec3::sZero();
JPH::Vec3 m_targetPosition = JPH::Vec3::sZero(); // Where we want to be
JPH::Vec3 m_targetVelocity = JPH::Vec3::sZero(); // How we want to be
float m_secondsToArrival = FLT_EPSILON; // When we want to be
float m_maxSpeed = 0.0f;
float m_maxDampSpeed = 0.0f;
float m_maxAngular = 0.0f;
float m_maxDampAngular = 0.0f;
float m_teleportDistance = 0.0f;
bool m_isPhysicallyControlled = false; // If true we're a bone follower on an NPC or something...
bool m_allowTranslation = false; // Should we translate?
bool m_allowRotation = false; // Should we rotate?
float m_flPushableMassLimit = 1e4f;
float m_flPushableSpeedLimit = 1e4f;
uint16 m_savedMaterialIndex = 0;
};

View File

@ -0,0 +1,171 @@
#include "cbase.h"
#include "vjolt_controller_shadow.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
JoltPhysicsShadowController::JoltPhysicsShadowController( JoltPhysicsObject *pObject, bool allowTranslation, bool allowRotation )
: m_pObject( pObject ), m_allowTranslation( allowTranslation ), m_allowRotation( allowRotation )
{
// Make our object kinematic
m_pObject->GetBody()->SetMotionType( JPH::EMotionType::Kinematic );
m_savedCallbackFlags = m_pObject->GetCallbackFlags();
m_pObject->SetCallbackFlags( m_savedCallbackFlags | CALLBACK_SHADOW_COLLISION );
}
JoltPhysicsShadowController::~JoltPhysicsShadowController()
{
if ( !( m_pObject->GetCallbackFlags() & CALLBACK_MARKED_FOR_DELETE ) )
{
m_pObject->SetCallbackFlags( m_savedCallbackFlags );
}
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsShadowController::Update( const Vector &position, const QAngle &angles, float timeOffset )
{
// timeOffset == secondsToArrival
JPH::Vec3 targetPosition = SourceToJolt::Distance( position );
JPH::Quat targetRotation = SourceToJolt::Angle( angles );
if ( targetPosition.IsClose( m_targetPosition, 1e-8f ) && targetRotation.IsClose( m_targetRotation, 1e-8f ) )
return;
m_targetPosition = targetPosition;
m_targetRotation = targetRotation;
m_secondsToArrival = Max( timeOffset, 0.0f );
m_enabled = true;
}
void JoltPhysicsShadowController::MaxSpeed( float maxSpeed, float maxAngularSpeed )
{
m_maxSpeed = maxSpeed;
m_maxDampSpeed = maxSpeed;
m_maxAngular = maxAngularSpeed;
m_maxDampAngular = maxAngularSpeed;
}
void JoltPhysicsShadowController::StepUp( float height )
{
if ( height == 0.0f )
return;
m_pObject->AddToPosition( JPH::Vec3( 0.0f, 0.0f, SourceToJolt::Distance( height ) ) );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsShadowController::SetTeleportDistance( float teleportDistance )
{
m_teleportDistance = SourceToJolt::Distance( teleportDistance );
}
bool JoltPhysicsShadowController::AllowsTranslation()
{
return m_allowTranslation;
}
bool JoltPhysicsShadowController::AllowsRotation()
{
return m_allowRotation;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsShadowController::SetPhysicallyControlled( bool isPhysicallyControlled )
{
m_isPhysicallyControlled = isPhysicallyControlled;
}
bool JoltPhysicsShadowController::IsPhysicallyControlled()
{
return m_isPhysicallyControlled;
}
void JoltPhysicsShadowController::GetLastImpulse( Vector *pOut )
{
//*pOut = JoltToSource::Distance( m_lastImpulse );
VectorClear( *pOut );
}
// HACK HACK HACK WE MIGHT WANT TO CHANGE THIS
// IMPLEMENT ME!
static constexpr int ShadowMaterialIndex = 0xF000;
void JoltPhysicsShadowController::UseShadowMaterial( bool bUseShadowMaterial )
{
if ( !m_pObject )
return;
#if 0
int current = m_pObject->GetMaterialIndex();
int target = bUseShadowMaterial ? ShadowMaterialIndex : m_savedMaterialIndex;
if ( target != current )
m_pObject->SetMaterialIndex( target );
#endif
}
void JoltPhysicsShadowController::ObjectMaterialChanged( int materialIndex )
{
if ( !m_pObject )
return;
m_savedMaterialIndex = materialIndex;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsShadowController::GetTargetPosition( Vector *pPositionOut, QAngle *pAnglesOut )
{
if ( pPositionOut )
*pPositionOut = JoltToSource::Distance( m_targetPosition );
if ( pAnglesOut )
*pAnglesOut = JoltToSource::Angle( m_targetRotation );
return m_secondsToArrival;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsShadowController::GetTeleportDistance()
{
return JoltToSource::Distance( m_teleportDistance );
}
void JoltPhysicsShadowController::GetMaxSpeed( float *pMaxSpeedOut, float *pMaxAngularSpeedOut )
{
if ( pMaxSpeedOut )
*pMaxSpeedOut = m_maxSpeed;
if ( pMaxAngularSpeedOut )
*pMaxAngularSpeedOut = m_maxAngular;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsShadowController::OnPreSimulate( float flDeltaTime )
{
if (!m_enabled)
return;
VJoltAssertMsg( m_pObject->GetBody()->GetMotionType() == JPH::EMotionType::Kinematic, "Shadow controllers must be kinematic!" );
JPH::BodyInterface &bodyInterface = m_pObject->GetEnvironment()->GetPhysicsSystem()->GetBodyInterfaceNoLock();
if ( m_secondsToArrival > 0.0f )
bodyInterface.MoveKinematic( m_pObject->GetBodyID(), m_targetPosition, m_targetRotation, m_secondsToArrival );
else
{
bodyInterface.SetPositionAndRotation( m_pObject->GetBodyID(), m_targetPosition, m_targetRotation, JPH::EActivation::Activate );
bodyInterface.SetLinearAndAngularVelocity( m_pObject->GetBodyID(), JPH::Vec3::sZero(), JPH::Vec3::sZero() );
m_enabled = false;
}
m_secondsToArrival = Max( m_secondsToArrival - flDeltaTime, 0.0f );
}

View File

@ -0,0 +1,55 @@
#pragma once
#include "vjolt_object.h"
#include "vjolt_environment.h"
class JoltPhysicsShadowController final : public IPhysicsShadowController, public IJoltPhysicsController
{
public:
JoltPhysicsShadowController( JoltPhysicsObject *pObject, bool allowTranslation, bool allowRotation );
~JoltPhysicsShadowController() override;
void Update( const Vector &position, const QAngle &angles, float timeOffset ) override;
void MaxSpeed( float maxSpeed, float maxAngularSpeed ) override;
void StepUp( float height ) override;
void SetTeleportDistance( float teleportDistance ) override;
bool AllowsTranslation() override;
bool AllowsRotation() override;
void SetPhysicallyControlled( bool isPhysicallyControlled ) override;
bool IsPhysicallyControlled() override;
void GetLastImpulse( Vector *pOut ) override;
void UseShadowMaterial( bool bUseShadowMaterial ) override;
void ObjectMaterialChanged( int materialIndex ) override;
float GetTargetPosition( Vector *pPositionOut, QAngle *pAnglesOut ) override;
float GetTeleportDistance() override;
void GetMaxSpeed( float *pMaxSpeedOut, float *pMaxAngularSpeedOut ) override;
// IJoltPhysicsController
void OnPreSimulate( float flDeltaTime ) override;
private:
JoltPhysicsObject *m_pObject = nullptr;
JPH::Vec3 m_targetPosition = JPH::Vec3::sZero(); // Where we want to be
JPH::Quat m_targetRotation = JPH::Quat::sIdentity(); // How we want to be
float m_secondsToArrival = 0; // When we want to be
float m_maxSpeed = 0.0f;
float m_maxDampSpeed = 0.0f;
float m_maxAngular = 0.0f;
float m_maxDampAngular = 0.0f;
float m_teleportDistance = 0.0f;
bool m_isPhysicallyControlled = false; // If true we're a bone follower on an NPC or something...
bool m_allowTranslation = false; // Should we translate?
bool m_allowRotation = false; // Should we rotate?
bool m_enabled = false;
uint16 m_savedMaterialIndex = 0;
uint16 m_savedCallbackFlags = 0;
};

View File

@ -0,0 +1,534 @@

#include "cbase.h"
#include "vjolt_layers.h"
#include "vjolt_controller_vehicle.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//------------------------------------------------------------------------------------------------
static ConVar vjolt_vehicle_wheel_debug( "vjolt_vehicle_wheel_debug", "0", FCVAR_CHEAT );
static ConVar vjolt_vehicle_throttle_opposition_limit( "vjolt_vehicle_throttle_opposition_limit", "5", FCVAR_NONE,
"Below what speed should we be attempting to drive/climb with handbrake on to avoid falling down." );
//------------------------------------------------------------------------------------------------
static const JPH::Vec3 VehicleUpVector = JPH::Vec3( 0, 0, 1 );
static const JPH::Vec3 VehicleForwardVector = JPH::Vec3( 0, 1, 0 );
static const char *VehicleTypeToName( unsigned int VehicleType )
{
switch ( VehicleType )
{
case VEHICLE_TYPE_CAR_WHEELS: return "Car Wheels";
case VEHICLE_TYPE_CAR_RAYCAST: return "Car Raycast";
case VEHICLE_TYPE_JETSKI_RAYCAST: return "Jetski Raycast";
case VEHICLE_TYPE_AIRBOAT_RAYCAST: return "Airboat Raycast";
default: return "Unknown";
}
}
JPH::Ref< JPH::VehicleCollisionTester > CreateVehicleCollisionTester( unsigned int VehicleType, float LargestWheelRadius )
{
switch ( VehicleType )
{
default:
Log_Warning( LOG_VJolt, "Don't know how to make vehicle type: %s (%u).\n", VehicleTypeToName( VehicleType ), VehicleType );
[[ fallthrough ]];
case VEHICLE_TYPE_CAR_WHEELS:
return new JPH::VehicleCollisionTesterCastSphere( Layers::MOVING, LargestWheelRadius, VehicleUpVector );
}
}
//------------------------------------------------------------------------------------------------
JoltPhysicsVehicleController::JoltPhysicsVehicleController( JoltPhysicsEnvironment* pEnvironment, JPH::PhysicsSystem* pPhysicsSystem, JoltPhysicsObject* pVehicleBodyObject, const vehicleparams_t& params, unsigned int nVehicleType, IPhysicsGameTrace* pGameTrace )
: m_pEnvironment( pEnvironment )
, m_pPhysicsSystem( pPhysicsSystem )
, m_pCarBodyObject( pVehicleBodyObject )
, m_VehicleType( nVehicleType )
, m_VehicleParams( params )
{
JPH::VehicleConstraintSettings vehicle;
vehicle.mUp = VehicleUpVector;
vehicle.mForward = VehicleForwardVector;
vehicle.mDrawConstraintSize = 0.1f;
CreateWheels( vehicle );
vehicle.mController = CreateVehicleController();
m_Tester = CreateVehicleCollisionTester( nVehicleType, m_InternalState.LargestWheelRadius );
m_pCarBodyObject->AddDestroyedListener( this );
m_VehicleConstraint = new JPH::VehicleConstraint( *m_pCarBodyObject->GetBody(), vehicle );
m_pPhysicsSystem->AddConstraint( m_VehicleConstraint );
m_pPhysicsSystem->AddStepListener( m_VehicleConstraint );
}
JoltPhysicsVehicleController::~JoltPhysicsVehicleController()
{
DetachObject();
for ( auto &wheel : m_Wheels )
m_pEnvironment->DestroyObject( wheel.pObject );
m_Wheels.clear();
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::Update( float dt, vehicle_controlparams_t &controls )
{
m_ControlParams = controls;
UpdateBooster( dt );
HandleBoostKey();
}
const vehicle_operatingparams_t &JoltPhysicsVehicleController::GetOperatingParams()
{
return m_OperatingParams;
}
const vehicleparams_t &JoltPhysicsVehicleController::GetVehicleParams()
{
return m_VehicleParams;
}
vehicleparams_t &JoltPhysicsVehicleController::GetVehicleParamsForChange()
{
return m_VehicleParams;
}
float JoltPhysicsVehicleController::UpdateBooster( float dt )
{
m_InternalState.BoostDelay = Max( m_InternalState.BoostDelay - dt, 0.0f );
m_InternalState.BoosterRemainingTime = Max( m_InternalState.BoosterRemainingTime - dt, 0.0f );
return m_InternalState.BoostDelay;
}
int JoltPhysicsVehicleController::GetWheelCount()
{
return int( m_Wheels.size() );
}
IPhysicsObject *JoltPhysicsVehicleController::GetWheel( int index )
{
if ( index >= int( m_Wheels.size() ) )
return nullptr;
return m_Wheels[ index ].pObject;
}
bool JoltPhysicsVehicleController::GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps )
{
if ( index < int( m_Wheels.size() ) && m_VehicleConstraint->GetWheels()[ index ]->HasContact() )
{
if ( pContactPoint )
*pContactPoint = JoltToSource::Distance( m_VehicleConstraint->GetWheels()[ index ]->GetContactPosition() );
// TODO(Josh): This!
if ( pSurfaceProps )
*pSurfaceProps = 0;
return true;
}
else
{
if ( pContactPoint )
*pContactPoint = vec3_origin;
if ( pSurfaceProps )
*pSurfaceProps = 0;
return false;
}
}
void JoltPhysicsVehicleController::SetSpringLength( int wheelIndex, float length )
{
}
void JoltPhysicsVehicleController::SetWheelFriction( int wheelIndex, float friction )
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::OnVehicleEnter()
{
// Undo any damping we may have set to slow the boat when
// we got out.
if ( m_VehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
{
float flDampSpeed = 0.0f;
float flDampRotSpeed = 0.0f;
m_pCarBodyObject->SetDamping( &flDampSpeed, &flDampRotSpeed );
}
}
void JoltPhysicsVehicleController::OnVehicleExit()
{
// If we are an airboat, set a bunch of damping to slow us down.
if ( m_VehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
{
float flDampSpeed = 1.0f;
float flDampRotSpeed = 1.0f;
m_pCarBodyObject->SetDamping( &flDampSpeed, &flDampRotSpeed );
}
SetEngineDisabled( false );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::SetEngineDisabled( bool bDisable )
{
m_InternalState.EngineDisabled = bDisable;
}
bool JoltPhysicsVehicleController::IsEngineDisabled()
{
return m_InternalState.EngineDisabled;
}
void JoltPhysicsVehicleController::GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem )
{
}
void JoltPhysicsVehicleController::VehicleDataReload()
{
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsVehicleController::GetSpeed()
{
const Vector orientation = GetColumn( GetBodyMatrix(), MatrixAxis::Left );
return orientation.Dot( m_pCarBodyObject->GetVelocity() );
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::HandleBoostKey()
{
// Handle triggering boosting if the key is pressed and we aren't currently boosting or in cooldown.
if ( m_ControlParams.boost && !m_InternalState.BoostDelay && !m_InternalState.BoosterRemainingTime )
{
m_InternalState.BoosterRemainingTime = m_VehicleParams.engine.boostDuration;
m_InternalState.BoostDelay = m_VehicleParams.engine.boostDuration + m_VehicleParams.engine.boostDelay;
}
}
void JoltPhysicsVehicleController::HandleBoostDecay()
{
// Decay the boost time if we are currently boosting or have a delay.
if ( m_VehicleParams.engine.boostDuration || m_VehicleParams.engine.boostDelay )
{
m_OperatingParams.boostTimeLeft = m_InternalState.BoostDelay
? 100.0f - ( 100.0f * ( m_InternalState.BoostDelay / ( m_VehicleParams.engine.boostDuration + m_VehicleParams.engine.boostDelay ) ) )
: 100.0f;
}
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::OnPreSimulate( float flDeltaTime )
{
JPH::BodyInterface &bodyInterface = m_pPhysicsSystem->GetBodyInterfaceNoLock();
// With any user input, assure that the car is active
if ( m_ControlParams.steering != 0.0f || m_ControlParams.throttle != 0.0f || m_ControlParams.brake != 0.0f || m_ControlParams.handbrake )
bodyInterface.ActivateBody( m_pCarBodyObject->GetBodyID() );
bool bHandbrake = m_ControlParams.handbrake;
// Don't throttle when holding handbrake (like Source)
float flThrottle = bHandbrake ? 0.0f : m_ControlParams.throttle;
// Apply a little brake without throttle to stop the vehicle from coasting (like Source).
const bool bCoasting = flThrottle == 0.0f && m_ControlParams.brake == 0.0f && !bHandbrake;
const float flBrake = bCoasting ? 0.1f : m_ControlParams.brake;
const float ThrottleOpositionSpeed = vjolt_vehicle_throttle_opposition_limit.GetFloat();
// Enable the handbrake when going at low speeds to avoid slipping when going up hill.
if ( ( flThrottle < 0.0f && m_OperatingParams.speed > ThrottleOpositionSpeed ) ||
( flThrottle > 0.0f && m_OperatingParams.speed < -ThrottleOpositionSpeed ) )
bHandbrake = true;
// Are we boosting?
float flTotalTorqueMultiplier = 1.0f;
if ( m_InternalState.BoosterRemainingTime != 0.0f )
{
GetWheeledVehicleController()->GetEngine().SetCurrentRPM(m_VehicleParams.engine.maxRPM);
// Slam the throttle to 1, neeeowm!
m_ControlParams.throttle = 1.0f;
flThrottle = 1.0f;
const float flSpeedFactor = RemapValClamped( fabsf( m_OperatingParams.speed ), 0, m_VehicleParams.engine.maxSpeed, 0.1f, 1.0f );
const float flTurnFactor = 1.0f - ( fabsf( m_ControlParams.steering ) * 0.95f );
// Josh: * 2 as the original torque stuff in Source was based around 0.5 being the max, and 1.0 being boost.
const float flDampedBoost = 2.0f * m_VehicleParams.engine.boostForce * flSpeedFactor * flTurnFactor;
if ( flDampedBoost > flTotalTorqueMultiplier )
flTotalTorqueMultiplier = flDampedBoost;
}
// Update the torque factors as we may be boosting and be > 1.
// TODO(Josh): More than 2 wheels per axle.
VJoltAssert( m_VehicleParams.wheelsPerAxle == 2 );
for ( int i = 0; i < m_VehicleParams.axleCount; i++ )
GetWheeledVehicleController()->GetDifferentials()[i].mEngineTorqueRatio = flTotalTorqueMultiplier * m_VehicleParams.axles[i].torqueFactor;
// Pass the input on to the constraint
GetWheeledVehicleController()->SetDriverInput( flThrottle, m_ControlParams.steering, flBrake, bHandbrake ? 1.0f : 0.0f );
// Set the collision tester
m_VehicleConstraint->SetVehicleCollisionTester( m_Tester );
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::OnPostSimulate( float flDeltaTime )
{
// Draw our wheels (this needs to be done in the pre update since we draw the bodies too in the state before the step)
float flSteeringAngle = 0.0f;
m_OperatingParams.wheelsInContact = 0;
m_OperatingParams.wheelsNotInContact = 0;
for ( int w = 0; w < GetWheelCount(); w++ )
{
const JPH::WheelSettings *settings = m_VehicleConstraint->GetWheels()[w]->GetSettings();
// The cyclinder we draw is aligned with Y so we specify that as rotational axis
JPH::Mat44 wheelTransform = m_VehicleConstraint->GetWheelWorldTransform( w, JPH::Vec3( 1, 0, 0 ), JPH::Vec3( 0, 0, 1 ) );
// Find our greatest steering angle.
float flWheelSteeringAngle = JoltToSource::Angle( m_VehicleConstraint->GetWheels()[w]->GetSteerAngle() );
if ( fabsf( flWheelSteeringAngle ) > fabsf( flSteeringAngle ) )
flSteeringAngle = flWheelSteeringAngle;
Vector newPos = JoltToSource::Distance( wheelTransform.GetTranslation() );
// TODO(Josh): This triggers JPH_ASSERT(mCol[3] == Vec4(0, 0, 0, 1));
// what to do about that?..
// We just want the local rotation, and this seems to work (?)
QAngle newQuat = JoltToSource::Angle( wheelTransform.GetQuaternion() );
m_Wheels[ w ].pObject->EnableCollisions( false );
// Set dummy wheel object pos/angles so the game code can update pose positions for wheels.
m_Wheels[ w ].pObject->SetPosition( newPos, newQuat, true );
// Wake it up so that the game bothers to do pose positions.
m_Wheels[ w ].pObject->Wake();
if ( m_VehicleConstraint->GetWheels()[w]->HasContact() )
m_OperatingParams.wheelsInContact++;
else
m_OperatingParams.wheelsNotInContact++;
IVJoltDebugOverlay *pDebugOverlay = JoltPhysicsInterface::GetInstance().GetDebugOverlay();
if ( vjolt_vehicle_wheel_debug.GetBool() && pDebugOverlay )
{
const Vector vecWheelPos = JoltToSource::Distance( wheelTransform.GetTranslation() );
const Vector vecWheelSize = JoltToSource::Distance( JPH::Vec3( settings->mWidth / 2.0f, settings->mRadius, settings->mRadius ) );
pDebugOverlay->AddBoxOverlay(
vecWheelPos,
-vecWheelSize, vecWheelSize,
newQuat,
255, 0, 255, 100,
-1.0f );
}
}
m_OperatingParams.gear = GetWheeledVehicleController()->GetTransmission().GetCurrentGear();
m_OperatingParams.engineRPM = GetWheeledVehicleController()->GetEngine().GetCurrentRPM();
m_OperatingParams.speed = GetSpeed();
m_OperatingParams.steeringAngle = -flSteeringAngle;
m_OperatingParams.boostDelay = m_InternalState.BoostDelay;
HandleBoostDecay();
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::CreateWheel( JPH::VehicleConstraintSettings &vehicleSettings, matrix3x4_t& bodyMatrix, int axleIdx, int wheelIdx )
{
const vehicle_axleparams_t &axle = m_VehicleParams.axles[ axleIdx ];
const Vector wheelPositionLocal = axle.offset +
( ( wheelIdx % 2 == 1 ) ? axle.wheelOffset : -axle.wheelOffset );
Vector wheelPositionWorld;
VectorTransform( wheelPositionLocal, bodyMatrix, wheelPositionWorld );
// Josh: Good enough heuristic.
const float wheelRadius = axle.wheels.radius;
const float wheelWidth = wheelRadius / 2.0f;
// Josh: Area of a cylinder = π.h.r^2
// Using radius in terms of Source units as we pass this to CreateSphereObject.
const float wheelVolume = M_PI * wheelWidth * Cube( wheelRadius );
{
objectparams_t wheelParams =
{
.mass = axle.wheels.mass,
.inertia = axle.wheels.inertia,
.damping = axle.wheels.damping,
.rotdamping = axle.wheels.rotdamping,
.pName = "VehicleWheel",
.pGameData = m_pCarBodyObject->GetGameData(),
.volume = wheelVolume,
};
IPhysicsObject *pWheelObject = m_pEnvironment->CreateSphereObject(
wheelRadius, axle.wheels.materialIndex,
wheelPositionWorld, QAngle(),
&wheelParams, false );
JoltPhysicsObject *pJoltWheelObject = static_cast< JoltPhysicsObject * >( pWheelObject );
pJoltWheelObject->SetGameFlags( m_pCarBodyObject->GetGameFlags() );
pJoltWheelObject->SetCallbackFlags( CALLBACK_IS_VEHICLE_WHEEL );
// Josh: The wheel is a fake object, so disable collisions on it.
pJoltWheelObject->EnableCollisions( false );
m_Wheels.push_back( JoltPhysicsWheel{ .pObject = pJoltWheelObject } );
}
const float steeringAngle = DEG2RAD( Max( m_VehicleParams.steering.degreesSlow, m_VehicleParams.steering.degreesFast ) );
const float additionalLength = SourceToJolt::Distance( axle.wheels.springAdditionalLength );
JPH::WheelSettingsWV *wheelSettings = new JPH::WheelSettingsWV;
wheelSettings->mPosition = SourceToJolt::Distance( wheelPositionLocal );
wheelSettings->mDirection = JPH::Vec3( 0, 0, -1 );
wheelSettings->mAngularDamping = axle.wheels.rotdamping;
// TODO(Josh): What about more than 4 wheels?
wheelSettings->mMaxSteerAngle = axleIdx == 0 ? steeringAngle : 0.0f;
wheelSettings->mRadius = SourceToJolt::Distance( axle.wheels.radius );
wheelSettings->mWidth = SourceToJolt::Distance( wheelWidth );
wheelSettings->mInertia = 0.5f * axle.wheels.mass * ( wheelSettings->mRadius * wheelSettings->mRadius );
wheelSettings->mSuspensionMinLength = 0;
wheelSettings->mSuspensionMaxLength = additionalLength;
wheelSettings->mSuspensionDamping = axle.suspension.springDamping;
// Josh:
// so to go from K (Spring Constant) -> freq we do
// sqrtf( K / Mass ) / ( 2.0f * PI )
// but it seems like it already has mass divided in Source so...
// sqrtf( K ) / ( 2.0f * PI )
wheelSettings->mSuspensionFrequency = sqrtf( axle.suspension.springConstant ) / ( 2.0f * M_PI_F );
// Josh: I don't know why but it looks and feels really wrong without this:
// TODO(Josh): Investigate more later, doesn't make much sense.
// May be related to mass of wheel or something.
wheelSettings->mSuspensionFrequency *= M_PI_F;
if ( axle.wheels.frictionScale )
{
wheelSettings->mLateralFriction.AddPoint( 1.0f, axle.wheels.frictionScale );
wheelSettings->mLongitudinalFriction.AddPoint( 1.0f, axle.wheels.frictionScale );
}
vehicleSettings.mWheels.push_back( wheelSettings );
m_InternalState.LargestWheelRadius = Max( m_InternalState.LargestWheelRadius, SourceToJolt::Distance( wheelWidth ) );
}
void JoltPhysicsVehicleController::CreateWheels( JPH::VehicleConstraintSettings &vehicleSettings )
{
matrix3x4_t carBodyMtx = GetBodyMatrix();
m_Wheels.reserve( m_VehicleParams.axleCount * m_VehicleParams.wheelsPerAxle );
vehicleSettings.mAntiRollBars.reserve( m_VehicleParams.axleCount );
for ( int axle = 0; axle < m_VehicleParams.axleCount; axle++ )
{
for ( int wheel = 0; wheel < m_VehicleParams.wheelsPerAxle; wheel++ )
CreateWheel( vehicleSettings, carBodyMtx, axle, wheel );
// TODO(Josh): More than 2 wheels per axle.
VJoltAssert( m_VehicleParams.wheelsPerAxle == 2 );
JPH::VehicleAntiRollBar rollbar;
rollbar.mLeftWheel = ( axle * m_VehicleParams.wheelsPerAxle );
rollbar.mRightWheel = ( axle * m_VehicleParams.wheelsPerAxle ) + 1;
vehicleSettings.mAntiRollBars.push_back( rollbar );
}
}
JPH::WheeledVehicleControllerSettings *JoltPhysicsVehicleController::CreateVehicleController()
{
static constexpr float HorsePowerToWatts = 745.7f;
JPH::WheeledVehicleControllerSettings *pController = new JPH::WheeledVehicleControllerSettings;
// Josh:
// T = ( 745.7 * P ) / ( 2 * PI * ( RPM / 60 ) )
pController->mEngine.mMaxTorque = ( HorsePowerToWatts * m_VehicleParams.engine.horsepower ) / ( 2.0f * M_PI * ( m_VehicleParams.engine.maxRPM / 60.0f ) );
// Josh: Fudge
pController->mEngine.mMinRPM = Max( m_VehicleParams.engine.shiftDownRPM - 300, 0.0f );
pController->mEngine.mMaxRPM = m_VehicleParams.engine.maxRPM;
pController->mEngine.mAngularDamping = 0.0f;
pController->mTransmission.mMode = m_VehicleParams.engine.isAutoTransmission ? JPH::ETransmissionMode::Auto : JPH::ETransmissionMode::Manual;
pController->mTransmission.mGearRatios.clear();
for ( int i = 0; i < m_VehicleParams.engine.gearCount; i++ )
pController->mTransmission.mGearRatios.push_back( m_VehicleParams.engine.gearRatio[ i ] );
pController->mTransmission.mReverseGearRatios.clear();
pController->mTransmission.mReverseGearRatios.push_back( -m_VehicleParams.engine.gearRatio[0] );
pController->mTransmission.mShiftUpRPM = m_VehicleParams.engine.shiftUpRPM;
pController->mTransmission.mShiftDownRPM = m_VehicleParams.engine.shiftDownRPM;
pController->mDifferentials.reserve( m_VehicleParams.axleCount );
for ( int i = 0; i < m_VehicleParams.axleCount; i++ )
{
// TODO(Josh): More than 2 wheels per axle.
VJoltAssert( m_VehicleParams.wheelsPerAxle == 2 );
JPH::VehicleDifferentialSettings differential;
differential.mLeftWheel = ( i * m_VehicleParams.wheelsPerAxle );
differential.mRightWheel = ( i * m_VehicleParams.wheelsPerAxle ) + 1;
differential.mEngineTorqueRatio = m_VehicleParams.axles[ i ].torqueFactor;
pController->mDifferentials.push_back( differential );
}
return pController;
}
JPH::WheeledVehicleController *JoltPhysicsVehicleController::GetWheeledVehicleController()
{
return static_cast<JPH::WheeledVehicleController *>( m_VehicleConstraint->GetController() );
}
//------------------------------------------------------------------------------------------------
matrix3x4_t JoltPhysicsVehicleController::GetBodyMatrix() const
{
matrix3x4_t value;
m_pCarBodyObject->GetPositionMatrix( &value );
return value;
}
//------------------------------------------------------------------------------------------------
void JoltPhysicsVehicleController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject )
{
if ( m_pCarBodyObject == pObject )
DetachObject();
}
void JoltPhysicsVehicleController::DetachObject()
{
if ( m_pCarBodyObject )
{
m_pCarBodyObject->RemoveDestroyedListener( this );
// Remove the listeners and constraint now, we can never
// attach to another body.
m_pPhysicsSystem->RemoveConstraint( m_VehicleConstraint );
m_pPhysicsSystem->RemoveStepListener( m_VehicleConstraint );
m_pCarBodyObject = nullptr;
}
}

View File

@ -0,0 +1,92 @@
#pragma once
#include "vjolt_object.h" // IJoltObjectDestroyedListener
#include "vjolt_environment.h" // IJoltPhysicsController
struct JoltPhysicsWheel
{
JoltPhysicsObject* pObject = nullptr;
bool InWater = false;
float Depth = 0.0f;
};
struct JoltPhysicsInternalVehicleState
{
bool EngineDisabled = false;
float BoostDelay = 0.0f;
float BoosterRemainingTime = 0.0f;
float LargestWheelRadius = 0.0f;
};
class JoltPhysicsVehicleController final : public IPhysicsVehicleController, public IJoltObjectDestroyedListener, public IJoltPhysicsController
{
public:
static constexpr int MaxWheels = VEHICLE_MAX_WHEEL_COUNT;
JoltPhysicsVehicleController( JoltPhysicsEnvironment *pEnvironment, JPH::PhysicsSystem *pPhysicsSystem, JoltPhysicsObject *pVehicleBodyObject, const vehicleparams_t &params, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace );
~JoltPhysicsVehicleController() override;
void Update( float dt, vehicle_controlparams_t &controls ) override;
const vehicle_operatingparams_t & GetOperatingParams() override;
const vehicleparams_t & GetVehicleParams() override;
vehicleparams_t & GetVehicleParamsForChange() override;
float UpdateBooster( float dt ) override;
int GetWheelCount( void ) override;
IPhysicsObject * GetWheel( int index ) override;
bool GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps ) override;
void SetSpringLength( int wheelIndex, float length ) override;
void SetWheelFriction( int wheelIndex, float friction ) override;
void OnVehicleEnter( void ) override;
void OnVehicleExit( void ) override;
void SetEngineDisabled( bool bDisable ) override;
bool IsEngineDisabled( void ) override;
void GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem ) override;
void VehicleDataReload() override;
public:
// IJoltObjectDestroyedListener
void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override;
float GetSpeed();
// IJoltPhysicsController
void OnPreSimulate( float flDeltaTime ) override;
void OnPostSimulate( float flDeltaTime ) override;
private:
void HandleBoostKey();
void HandleBoostDecay();
void CreateWheel( JPH::VehicleConstraintSettings &vehicleSettings, matrix3x4_t &bodyMatrix, int axleIdx, int wheelIdx );
void CreateWheels( JPH::VehicleConstraintSettings& vehicleSettings );
JPH::WheeledVehicleControllerSettings *CreateVehicleController();
JPH::WheeledVehicleController *GetWheeledVehicleController();
matrix3x4_t GetBodyMatrix() const;
void DetachObject();
JoltPhysicsEnvironment *m_pEnvironment = nullptr;
JPH::PhysicsSystem *m_pPhysicsSystem = nullptr;
JoltPhysicsObject *m_pCarBodyObject = nullptr;
vehicleparams_t m_VehicleParams = {};
unsigned int m_VehicleType = 0u;
vehicle_operatingparams_t m_OperatingParams = {};
vehicle_controlparams_t m_ControlParams = {};
std::vector< JoltPhysicsWheel > m_Wheels;
JoltPhysicsInternalVehicleState m_InternalState;
JPH::Ref< JPH::VehicleConstraint > m_VehicleConstraint;
JPH::Ref< JPH::VehicleCollisionTester > m_Tester;
};

View File

@ -0,0 +1,289 @@
//=================================================================================================
//
// Jolt Debug Renderer Implementation
//
//=================================================================================================
#include "cbase.h"
#ifdef JPH_DEBUG_RENDERER
#include <Jolt/Renderer/DebugRenderer.h>
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imesh.h"
#include "vjolt_debugrender.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Slart: Not sure if this is still relevant,
// it's the amount of time a debugoverlay element should stay on-screen
#define ONE_SINGLE_FRAME 0.0f
#define JOLT_VERTEX_BUFFER_NAME "Jolt Debug Renderer Vertices"
#define JOLT_INDEX_BUFFER_NAME "Jolt Debug Renderer Indices"
static ConVar vjolt_debugrender( "vjolt_debugrender", "0", FCVAR_CHEAT );
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
static ConVar vjolt_debugrender_picture_in_picture( "vjolt_debugrender_picture_in_picture", "1" );
static ConVar vjolt_debugrender_clear_rt( "vjolt_debugrender_clear_rt", "1" );
static ConVar vjolt_debugrender_clear_depth( "vjolt_debugrender_clear_depth", "1" );
static ConVar vjolt_debugrender_wireframe( "vjolt_debugrender_wireframe", "0" );
static ConVar vjolt_debugrender_color_mode( "vjolt_debugrender_color_mode", "instance", 0, "One of instance, shape_type, motion_type, sleep, island, material." );
#endif
//-------------------------------------------------------------------------------------------------
JoltPhysicsDebugRenderer::JoltPhysicsDebugRenderer()
{
DebugRenderer::Initialize();
}
JoltPhysicsDebugRenderer::~JoltPhysicsDebugRenderer()
{
}
void JoltPhysicsDebugRenderer::DrawLine( const JPH::Float3& inFrom, const JPH::Float3& inTo, JPH::ColorArg inColor )
{
Vector v1 = JoltToSource::Distance( inFrom );
Vector v2 = JoltToSource::Distance( inTo );
#ifdef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
GetDebugOverlay()->AddLineOverlay( v1, v2, inColor.r, inColor.g, inColor.b, true, ONE_SINGLE_FRAME );
#else
GetDebugOverlay()->AddLineOverlay( v1, v2, inColor.r, inColor.g, inColor.b, inColor.a, true, 0.2f );
#endif
}
void JoltPhysicsDebugRenderer::DrawTriangle( JPH::Vec3Arg inV1, JPH::Vec3Arg inV2, JPH::Vec3Arg inV3, JPH::ColorArg inColor )
{
//DrawTriangle_Internal( JPH::Float3( inV1.GetX(), inV1.GetY(), inV1.GetZ() ), JPH::Float3( inV2.GetX(), inV2.GetY(), inV2.GetZ() ), JPH::Float3( inV3.GetX(), inV3.GetY(), inV3.GetZ() ), inColor );
}
JoltPhysicsDebugRenderer::Batch JoltPhysicsDebugRenderer::CreateTriangleBatch( const Triangle* inTriangles, int inTriangleCount )
{
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
const Vertex* inVertices = reinterpret_cast<const Vertex*>( inTriangles );
int inVertexCount = inTriangleCount * 3;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
constexpr VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR | VERTEX_TEXCOORD_SIZE(0, 2);
IMesh* pMesh = pRenderContext->CreateStaticMesh( fmt, JOLT_VERTEX_BUFFER_NAME );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, inTriangleCount );
{
for (int i = 0; i < inVertexCount; ++i)
{
meshBuilder.Position3f ( inVertices[i].mPosition.x * JoltToSource::Factor, inVertices[i].mPosition.y * JoltToSource::Factor, inVertices[i].mPosition.z * JoltToSource::Factor );
meshBuilder.Normal3f ( inVertices[i].mNormal.x, inVertices[i].mNormal.y, inVertices[i].mNormal.z );
meshBuilder.TexCoord2f ( 0, inVertices[i].mUV.x, inVertices[i].mUV.y );
meshBuilder.Color4Packed( inVertices[i].mColor.mU32 );
meshBuilder.AdvanceVertex();
}
}
meshBuilder.End();
return new BatchImpl( pMesh );
#else
return nullptr;
#endif
}
JoltPhysicsDebugRenderer::Batch JoltPhysicsDebugRenderer::CreateTriangleBatch( const Vertex* inVertices, int inVertexCount, const uint32* inIndices, int inIndexCount )
{
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
constexpr VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR | VERTEX_TEXCOORD_SIZE(0, 2);
IMesh* pMesh = pRenderContext->CreateStaticMesh( fmt, JOLT_VERTEX_BUFFER_NAME );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, inVertexCount, inIndexCount );
{
for (int i = 0; i < inVertexCount; ++i)
{
meshBuilder.Position3f ( inVertices[i].mPosition.x * JoltToSource::Factor, inVertices[i].mPosition.y * JoltToSource::Factor, inVertices[i].mPosition.z * JoltToSource::Factor );
meshBuilder.Normal3f ( inVertices[i].mNormal.x, inVertices[i].mNormal.y, inVertices[i].mNormal.z );
meshBuilder.TexCoord2f ( 0, inVertices[i].mUV.x, inVertices[i].mUV.y );
meshBuilder.Color4Packed( inVertices[i].mColor.mU32 );
meshBuilder.AdvanceVertex();
}
for (int i = 0; i < inIndexCount; ++i)
{
meshBuilder.Index( static_cast<unsigned short>( inIndices[i] ) );
meshBuilder.AdvanceIndex();
}
}
meshBuilder.End();
return new BatchImpl( pMesh );
#else
return nullptr;
#endif
}
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
MaterialCullMode_t ConvertCullMode( JPH::DebugRenderer::ECullMode mode )
{
switch (mode)
{
case JPH::DebugRenderer::ECullMode::CullFrontFace:
return MATERIAL_CULLMODE_CCW;
case JPH::DebugRenderer::ECullMode::CullBackFace:
return MATERIAL_CULLMODE_CW;
default: // case JPH::DebugRenderer::ECullMode::Off:
return MATERIAL_CULLMODE_NONE;
}
}
#endif
void JoltPhysicsDebugRenderer::DrawGeometry( JPH::Mat44Arg inModelMatrix, const JPH::AABox& inWorldSpaceBounds, float inLODScaleSq, JPH::ColorArg inModelColor, const GeometryRef& inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode )
{
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
matrix3x4_t sourceMatrix = JoltToSource::Matrix( inModelMatrix );
const BatchImpl* batch = reinterpret_cast< const BatchImpl * >( inGeometry->mLODs[0].mTriangleBatch.GetPtr() );
color32 clearColor = { .r = 20, .g = 20, .b = 20, .a = 255 };
color32 modulateColor = JoltToSource::Color( inModelColor ).ToColor32();
bool bForceWireFrame = vjolt_debugrender_wireframe.GetInt() == 2;
DebugOverlayMeshDesc_t desc =
{
.pMesh = batch->GetMesh(),
.matTransform = sourceMatrix,
.flDuration = -1.0f,
.bIgnoreZ = false,
.bWireframe = inDrawMode == EDrawMode::Wireframe || bForceWireFrame,
.bClearRT = vjolt_debugrender_clear_rt.GetBool(),
.bClearDepth = vjolt_debugrender_clear_depth.GetBool(),
.colClearColor = clearColor,
.colModulateColor = modulateColor,
.eCullMode = ConvertCullMode( inCullMode ),
.bPip = vjolt_debugrender_picture_in_picture.GetBool()
};
GetDebugOverlay()->DrawMesh( desc );
#endif
}
void JoltPhysicsDebugRenderer::DrawText3D( JPH::Vec3Arg inPosition, const std::string_view &inString, JPH::ColorArg inColor, float inHeight )
{
// Josh:
// Doing a copy of 1024, the max size allowed by a debug overlay
// because AddTextOverlayRGB takes in a c_str and a string view
// is not necessarily null terminated.
char text[1024];
V_strncpy( text, inString.data(), sizeof( text ) );
Vector origin = JoltToSource::Distance( inPosition );
GetDebugOverlay()->AddTextOverlayRGB( origin, 0, 0.5f, inColor.r, inColor.g, inColor.b, inColor.a, "%s", text );
}
void JoltPhysicsDebugRenderer::DrawJoltTVText()
{
GetDebugOverlay()->AddScreenTextOverlay( 0.75f, 0.55f, 0.5f, 255, 0, 255, 255, "Live Jolt Reaction" ); // "Jolt TV"
}
//-------------------------------------------------------------------------------------------------
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
static JPH::BodyManager::EShapeColor GetColorMode()
{
const char *pszString = vjolt_debugrender_color_mode.GetString();
if ( !V_stricmp( pszString, "instance" ) )
return JPH::BodyManager::EShapeColor::InstanceColor;
else if ( !V_stricmp( pszString, "shape_type" ) )
return JPH::BodyManager::EShapeColor::ShapeTypeColor;
else if ( !V_stricmp( pszString, "motion_type" ) )
return JPH::BodyManager::EShapeColor::MotionTypeColor;
else if ( !V_stricmp( pszString, "sleep" ) )
return JPH::BodyManager::EShapeColor::SleepColor;
else if ( !V_stricmp( pszString, "island" ) )
return JPH::BodyManager::EShapeColor::IslandColor;
else if ( !V_stricmp( pszString, "material" ) )
return JPH::BodyManager::EShapeColor::MaterialColor;
Log_Msg( LOG_VJolt, "Unknown color mode: %s\n", pszString );
return JPH::BodyManager::EShapeColor::InstanceColor;
}
#endif
void JoltPhysicsDebugRenderer::RenderPhysicsSystem( JPH::PhysicsSystem &physicsSystem )
{
if ( !GetDebugOverlay() || !vjolt_debugrender.GetBool())
return;
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
const bool wireframe = vjolt_debugrender_wireframe.GetBool();
JPH::BodyManager::DrawSettings drawSettings
{
.mDrawGetSupportFunction = false, ///< Draw the GetSupport() function, used for convex collision detection
.mDrawSupportDirection = false, ///< When drawing the support function, also draw which direction mapped to a specific support point
.mDrawGetSupportingFace = false, ///< Draw the faces that were found colliding during collision detection
.mDrawShape = true, ///< Draw the shapes of all bodies
.mDrawShapeWireframe = wireframe, ///< When mDrawShape is true and this is true, the shapes will be drawn in wireframe instead of solid.
.mDrawShapeColor = GetColorMode(), ///< Coloring scheme to use for shapes
.mDrawBoundingBox = false, ///< Draw a bounding box per body
.mDrawCenterOfMassTransform = false, ///< Draw the center of mass for each body
.mDrawWorldTransform = false, ///< Draw the world transform (which can be different than the center of mass) for each body
.mDrawVelocity = false, ///< Draw the velocity vector for each body
.mDrawMassAndInertia = false, ///< Draw the mass and inertia (as the box equivalent) for each body
.mDrawSleepStats = false, ///< Draw stats regarding the sleeping algorithm of each body
};
#else
JPH::BodyManager::DrawSettings drawSettings
{
.mDrawGetSupportFunction = false, ///< Draw the GetSupport() function, used for convex collision detection
.mDrawSupportDirection = false, ///< When drawing the support function, also draw which direction mapped to a specific support point
.mDrawGetSupportingFace = false, ///< Draw the faces that were found colliding during collision detection
.mDrawShape = false, ///< Draw the shapes of all bodies
.mDrawShapeWireframe = false, ///< When mDrawShape is true and this is true, the shapes will be drawn in wireframe instead of solid.
.mDrawBoundingBox = true, ///< Draw a bounding box per body
.mDrawCenterOfMassTransform = false, ///< Draw the center of mass for each body
.mDrawWorldTransform = false, ///< Draw the world transform (which can be different than the center of mass) for each body
.mDrawVelocity = true, ///< Draw the velocity vector for each body
.mDrawMassAndInertia = false, ///< Draw the mass and inertia (as the box equivalent) for each body
.mDrawSleepStats = false, ///< Draw stats regarding the sleeping algorithm of each body
};
#endif
physicsSystem.DrawBodies( drawSettings, this );
#ifndef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
// :frog:
if ( vjolt_debugrender_picture_in_picture.GetBool() )
DrawJoltTVText();
#endif
}
//-------------------------------------------------------------------------------------------------
JoltPhysicsDebugRenderer &JoltPhysicsDebugRenderer::GetInstance()
{
static JoltPhysicsDebugRenderer s_DebugRenderer;
return s_DebugRenderer;
}
IVJoltDebugOverlay *JoltPhysicsDebugRenderer::GetDebugOverlay()
{
return JoltPhysicsInterface::GetInstance().GetDebugOverlay();
}
//-------------------------------------------------------------------------------------------------
#endif // JPH_DEBUG_RENDERER

View File

@ -0,0 +1,65 @@
#pragma once
class IMesh;
#ifdef JPH_DEBUG_RENDERER
class JoltPhysicsDebugRenderer final : public JPH::DebugRenderer
{
public:
JoltPhysicsDebugRenderer();
~JoltPhysicsDebugRenderer() override;
///////////////////////////////////////////
// JPH::DebugRenderer + Draw Implementation
///////////////////////////////////////////
void DrawLine( const JPH::Float3 &inFrom, const JPH::Float3 &inTo, JPH::ColorArg inColor ) override;
void DrawTriangle( JPH::Vec3Arg inV1, JPH::Vec3Arg inV2, JPH::Vec3Arg inV3, JPH::ColorArg inColor ) override;
Batch CreateTriangleBatch( const Triangle *inTriangles, int inTriangleCount ) override;
Batch CreateTriangleBatch( const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount ) override;
// This parameter list sucks
void DrawGeometry( JPH::Mat44Arg inModelMatrix, const JPH::AABox &inWorldSpaceBounds, float inLODScaleSq, JPH::ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid ) override;
void DrawText3D( JPH::Vec3Arg inPosition, const std::string_view &inString, JPH::ColorArg inColor = JPH::Color::sWhite, float inHeight = 0.5f ) override;
///////////////////////////////////////////
// Hehe
///////////////////////////////////////////
void DrawJoltTVText();
///////////////////////////////////////////
// Main Interface
///////////////////////////////////////////
void RenderPhysicsSystem( JPH::PhysicsSystem &physicsSystem );
static JoltPhysicsDebugRenderer& GetInstance();
static IVJoltDebugOverlay *GetDebugOverlay();
private:
class BatchImpl final : public JPH::RefTargetVirtual, public JPH::RefTarget<BatchImpl>
{
public:
BatchImpl( IMesh *pMesh )
: m_pMesh( pMesh ) { }
void AddRef() override { JPH::RefTarget<BatchImpl>::AddRef(); }
void Release() override { JPH::RefTarget<BatchImpl>::Release(); }
IMesh* GetMesh() const { return m_pMesh; }
private:
IMesh *m_pMesh;
};
bool m_bShouldClear = false;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,217 @@
//=================================================================================================
//
// Interface to a physics scene
//
//=================================================================================================
#pragma once
#include "vjolt_interface.h"
#include "vjolt_object.h"
#include "vjolt_constraints.h"
#include "vjolt_listener_contact.h"
class JoltBPLayerInterfaceImpl;
// StateRecorder implementation that saves to a fixed buffer
class VJoltStateRecorder final : public JPH::StateRecorder, public CUtlBuffer
{
public:
using CUtlBuffer::CUtlBuffer;
// StreamIn
void ReadBytes( void* outData, size_t inNumBytes ) override
{
Get( outData, static_cast<int>( inNumBytes ) );
}
bool IsEOF() const override { return false; }
// StreamOut
void WriteBytes( const void* inData, size_t inNumBytes )
{
Put( inData, static_cast<int>( inNumBytes ) );
}
// Both
bool IsFailed() const override { return false; }
};
class JoltPhysicsEnvironment final : public IPhysicsEnvironment
{
public:
JoltPhysicsEnvironment();
~JoltPhysicsEnvironment() override;
void SetDebugOverlay( CreateInterfaceFn debugOverlayFactory ) override;
IVPhysicsDebugOverlay* GetDebugOverlay( void ) override;
void SetGravity( const Vector& gravityVector ) override;
void GetGravity( Vector* pGravityVector ) const override;
void SetAirDensity( float density ) override;
float GetAirDensity() const override;
IPhysicsObject* CreatePolyObject( const CPhysCollide* pCollisionModel, int materialIndex, const Vector& position, const QAngle& angles, objectparams_t* pParams ) override;
IPhysicsObject* CreatePolyObjectStatic( const CPhysCollide* pCollisionModel, int materialIndex, const Vector& position, const QAngle& angles, objectparams_t* pParams ) override;
IPhysicsObject* CreateSphereObject( float radius, int materialIndex, const Vector& position, const QAngle& angles, objectparams_t* pParams, bool isStatic ) override;
void DestroyObject( IPhysicsObject* ) override;
IPhysicsFluidController* CreateFluidController( IPhysicsObject* pFluidObject, fluidparams_t* pParams ) override;
void DestroyFluidController( IPhysicsFluidController* ) override;
IPhysicsSpring* CreateSpring( IPhysicsObject* pObjectStart, IPhysicsObject* pObjectEnd, springparams_t* pParams ) override;
void DestroySpring( IPhysicsSpring* ) override;
IPhysicsConstraint* CreateRagdollConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_ragdollparams_t& ragdoll ) override;
IPhysicsConstraint* CreateHingeConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_hingeparams_t& hinge ) override;
IPhysicsConstraint* CreateFixedConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_fixedparams_t& fixed ) override;
IPhysicsConstraint* CreateSlidingConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_slidingparams_t& sliding ) override;
IPhysicsConstraint* CreateBallsocketConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_ballsocketparams_t& ballsocket ) override;
IPhysicsConstraint* CreatePulleyConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_pulleyparams_t& pulley ) override;
IPhysicsConstraint* CreateLengthConstraint( IPhysicsObject* pReferenceObject, IPhysicsObject* pAttachedObject, IPhysicsConstraintGroup* pGroup, const constraint_lengthparams_t& length ) override;
void DestroyConstraint( IPhysicsConstraint* ) override;
IPhysicsConstraintGroup* CreateConstraintGroup( const constraint_groupparams_t& groupParams ) override;
void DestroyConstraintGroup( IPhysicsConstraintGroup* pGroup ) override;
IPhysicsShadowController* CreateShadowController( IPhysicsObject* pObject, bool allowTranslation, bool allowRotation ) override;
void DestroyShadowController( IPhysicsShadowController* ) override;
IPhysicsPlayerController* CreatePlayerController( IPhysicsObject* pObject ) override;
void DestroyPlayerController( IPhysicsPlayerController* ) override;
IPhysicsMotionController* CreateMotionController( IMotionEvent* pHandler ) override;
void DestroyMotionController( IPhysicsMotionController* pController ) override;
IPhysicsVehicleController* CreateVehicleController( IPhysicsObject* pVehicleBodyObject, const vehicleparams_t& params, unsigned int nVehicleType, IPhysicsGameTrace* pGameTrace ) override;
void DestroyVehicleController( IPhysicsVehicleController* ) override;
void SetCollisionSolver( IPhysicsCollisionSolver* pSolver ) override;
void Simulate( float deltaTime ) override;
bool IsInSimulation() const override;
float GetSimulationTimestep() const override;
void SetSimulationTimestep( float timestep ) override;
float GetSimulationTime() const override;
void ResetSimulationClock() override;
float GetNextFrameTime() const override;
void SetCollisionEventHandler( IPhysicsCollisionEvent* pCollisionEvents ) override;
void SetObjectEventHandler( IPhysicsObjectEvent* pObjectEvents ) override;
virtual void SetConstraintEventHandler( IPhysicsConstraintEvent* pConstraintEvents ) override;
void SetQuickDelete( bool bQuick ) override;
int GetActiveObjectCount() const override;
void GetActiveObjects( IPhysicsObject** pOutputObjectList ) const override;
const IPhysicsObject** GetObjectList( int* pOutputObjectCount ) const override;
bool TransferObject( IPhysicsObject* pObject, IPhysicsEnvironment* pDestinationEnvironment ) override;
void CleanupDeleteList() override;
void EnableDeleteQueue( bool enable ) override;
bool Save( const physsaveparams_t& params ) override;
void PreRestore( const physprerestoreparams_t& params ) override;
bool Restore( const physrestoreparams_t& params ) override;
void PostRestore() override;
bool IsCollisionModelUsed( CPhysCollide* pCollide ) const override;
void TraceRay( const Ray_t& ray, unsigned int fMask, IPhysicsTraceFilter* pTraceFilter, trace_t* pTrace ) override;
void SweepCollideable( const CPhysCollide* pCollide, const Vector& vecAbsStart, const Vector& vecAbsEnd,
const QAngle& vecAngles, unsigned int fMask, IPhysicsTraceFilter* pTraceFilter, trace_t* pTrace ) override;
void GetPerformanceSettings( physics_performanceparams_t* pOutput ) const override;
void SetPerformanceSettings( const physics_performanceparams_t* pSettings ) override;
void ReadStats( physics_stats_t* pOutput ) override;
void ClearStats() override;
unsigned int GetObjectSerializeSize( IPhysicsObject* pObject ) const override;
void SerializeObjectToBuffer( IPhysicsObject* pObject, unsigned char* pBuffer, unsigned int bufferSize ) override;
IPhysicsObject* UnserializeObjectFromBuffer( void* pGameData, unsigned char* pBuffer, unsigned int bufferSize, bool enableCollisions ) override;
void EnableConstraintNotify( bool bEnable ) override;
void DebugCheckContacts() override;
void SetAlternateGravity( const Vector& gravityVector ) override_asw;
void GetAlternateGravity( Vector* pGravityVector ) const override_asw;
float GetDeltaFrameTime( int maxTicks ) const override_asw;
void ForceObjectsToSleep( IPhysicsObject** pList, int listCount ) override_asw;
void SetPredicted( bool bPredicted ) override_portal2;
bool IsPredicted() override_portal2;
void SetPredictionCommandNum( int iCommandNum ) override_portal2;
int GetPredictionCommandNum() override_portal2;
void DoneReferencingPreviousCommands( int iCommandNum ) override_portal2;
void RestorePredictedSimulation() override_portal2;
void DestroyCollideOnDeadObjectFlush( CPhysCollide* ) override_portal2;
public:
JPH::PhysicsSystem* GetPhysicsSystem() { return &m_PhysicsSystem; }
void ObjectTransferHandOver( JoltPhysicsObject* pObject );
JoltPhysicsContactListener* GetContactListener() { return &m_ContactListener; }
IPhysicsConstraintEvent* GetConstraintEvents() { return m_pConstraintListener; }
void NotifyConstraintDisabled( JoltPhysicsConstraint* pConstraint );
private:
void RemoveBodyAndDeleteObject( JoltPhysicsObject* pObject );
void DeleteDeadObjects();
template <typename T>
void AddPhysicsSaveRestorePointer( uintp oldPtr, T* newPtr );
template <typename T>
T* LookupPhysicsSaveRestorePointer( uintp oldPtr );
void HandleDebugDumpingEnvironment( void* pReturnAddress );
bool m_bSimulating = false;
bool m_bEnableDeleteQueue = false;
bool m_bWakeObjectsOnConstraintDeletion = false;
bool m_bOptimizedBroadPhase = false;
bool m_bUseLinearCast = true;
float m_flStepTime = 1.0f / 60.0f;
float m_flAirDensity = 2.0f;
static JoltBPLayerInterfaceImpl s_BPLayerInterface;
// For GetObjectList
mutable JPH::BodyIDVector m_CachedBodies;
mutable std::vector< const IPhysicsObject * > m_CachedObjects;
// For GetActiveObjectCount and GetActiveObjects
mutable JPH::BodyIDVector m_CachedActiveBodies;
JPH::PhysicsSystem m_PhysicsSystem;
std::vector< JoltPhysicsObject * > m_pDeadObjects;
std::vector< JoltPhysicsConstraint * > m_pDeadConstraints;
std::vector< CPhysCollide * > m_pDeadObjectCollides;
CUtlVector< IJoltPhysicsController * > m_pPhysicsControllers;
std::unordered_map< uintp, void * > m_SaveRestorePointerMap;
// The physics system that simulates the world
// The debug overlay to render with (if it was ever passed to us)
IVJoltDebugOverlay *m_pDebugOverlay = nullptr;
JoltPhysicsContactListener m_ContactListener;
IPhysicsConstraintEvent *m_pConstraintListener = nullptr;
bool m_EnableConstraintNotify = false;
mutable bool m_bActiveObjectCountFirst = true;
};

View File

@ -0,0 +1,99 @@
#include "cbase.h"
#include "vjolt_friction.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Josh:
// Friction snapshots are used for the following:
// - Making objects on NPCs phase through them after some time
// - Something something physics damage, but that seems to work anyway... :think:
// - Game code callback for freezing objects if too many objects (IVP performance backdoor) -- not relevant for us
// - Disabling last portal stuff for objects after teleporting, but our trigger callbacks are robust so this game code hack doesn't seem to matter *touch wood*
// - Something to do with changing paint powers
// - Train physics blockers
// All in all, it doesn't seem to be *that* important, most things still work
// without it implemented.
// Right now, we do not have an efficient way to implement this with Jolt.
// Hence it is just stubby.
//-------------------------------------------------------------------------------------------------
bool JoltPhysicsFrictionSnapshot::IsValid()
{
return false;
}
//-------------------------------------------------------------------------------------------------
IPhysicsObject *JoltPhysicsFrictionSnapshot::GetObject( int index )
{
return nullptr;
}
int JoltPhysicsFrictionSnapshot::GetMaterial( int index )
{
return 0;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFrictionSnapshot::GetContactPoint( Vector &out )
{
out.Zero();
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFrictionSnapshot::GetSurfaceNormal( Vector &out )
{
out.Zero();
}
float JoltPhysicsFrictionSnapshot::GetNormalForce()
{
return 0.0f;
}
float JoltPhysicsFrictionSnapshot::GetEnergyAbsorbed()
{
return 0.0f;
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFrictionSnapshot::RecomputeFriction()
{
}
void JoltPhysicsFrictionSnapshot::ClearFrictionForce()
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFrictionSnapshot::MarkContactForDelete()
{
}
void JoltPhysicsFrictionSnapshot::DeleteAllMarkedContacts( bool wakeObjects )
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsFrictionSnapshot::NextFrictionData()
{
}
float JoltPhysicsFrictionSnapshot::GetFrictionCoefficient()
{
return 0.0f;
}

View File

@ -0,0 +1,26 @@
#pragma once
class JoltPhysicsFrictionSnapshot final : public IPhysicsFrictionSnapshot
{
public:
bool IsValid() override;
IPhysicsObject *GetObject( int index ) override;
int GetMaterial( int index ) override;
void GetContactPoint( Vector &out ) override;
void GetSurfaceNormal( Vector &out ) override;
float GetNormalForce() override;
float GetEnergyAbsorbed() override;
void RecomputeFriction() override;
void ClearFrictionForce() override;
void MarkContactForDelete() override;
void DeleteAllMarkedContacts( bool wakeObjects ) override;
void NextFrictionData() override;
float GetFrictionCoefficient() override;
};

View File

@ -0,0 +1,201 @@
//=================================================================================================
//
// The base physics DLL interface
//
//=================================================================================================
#include "cbase.h"
#include "vjolt_environment.h"
#include "vjolt_collide.h"
#include "vjolt_surfaceprops.h"
#include "vjolt_objectpairhash.h"
#include "vjolt_interface.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
// Slart:
// Pre-allocate 64 megabytes for physics allocations.
// I don't think we've tuned this value. It's just a big number that we probably won't ever hit.
static constexpr uint kTempAllocSize = 64 * 1024 * 1024;
// Josh:
// We cannot support more than 64 threads doing physics work because
// of the code I wrote in vjolt_listener_contact to dispatch events.
// It uses a single uint64_t bitmask that is iterated on for the thread-local
// event vectors.
// This isn't an issue, the benefits of more threads tends to trail off between
// 8-16 threads anyway.
static constexpr uint kMaxPhysicsThreads = 64;
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_VJolt, "VJolt", 0, LS_MESSAGE, Color( 205, 142, 212, 255 ) );
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_JoltInternal, "Jolt" );
JoltPhysicsInterface JoltPhysicsInterface::s_PhysicsInterface;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( JoltPhysicsInterface, IPhysics, VPHYSICS_INTERFACE_VERSION, JoltPhysicsInterface::GetInstance() );
//-------------------------------------------------------------------------------------------------
// Slart:
// Instead of using Jolt's allocator override functionality, we disable it and just define the
// functions here, all of Jolt's memory allocation goes through here, besides new and delete
// which use the Valve overrides in memoverride.cpp.
// For Desolation we use mi-malloc rather than dlmalloc, that also gets built into the statically
// linked releases for gmod (along with all of tier0 and vstdlib).
namespace JPH {
void *Allocate( size_t inSize )
{
return MemAlloc_Alloc( inSize );
}
void Free( void *inBlock )
{
MemAlloc_Free( inBlock );
}
void *AlignedAllocate( size_t inSize, size_t inAlignment )
{
return MemAlloc_AllocAligned( inSize, inAlignment );
}
void AlignedFree( void *inBlock )
{
MemAlloc_FreeAligned( inBlock );
}
}
//-------------------------------------------------------------------------------------------------
InitReturnVal_t JoltPhysicsInterface::Init()
{
const InitReturnVal_t nRetVal = BaseClass::Init();
if ( nRetVal != INIT_OK )
{
return nRetVal;
}
MathLib_Init();
// Install callbacks
JPH::Trace = JoltPhysicsInterface::OnTrace;
JPH_IF_ENABLE_ASSERTS( JPH::AssertFailed = JoltPhysicsInterface::OnAssert; )
// Create a factory
JPH::Factory::sInstance = new JPH::Factory();
// Register all Jolt physics types
JPH::RegisterTypes();
// Create an allocator for temporary allocations during physics simulations
m_pTempAllocator = new JPH::TempAllocatorImpl( kTempAllocSize );
// Josh:
// We may want to replace this with a better heuristic, or add a launch arg for this in future.
// Right now, this does what -1 does in Jolt, but limits it to 64 threads, as we cannot support
// more than this (see above).
const uint32 threadCount = Min( std::thread::hardware_concurrency() - 1, kMaxPhysicsThreads );
m_pJobSystem = new JPH::JobSystemThreadPool( JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, threadCount );
return INIT_OK;
}
void JoltPhysicsInterface::Shutdown()
{
delete m_pJobSystem;
delete m_pTempAllocator;
delete JPH::Factory::sInstance;
BaseClass::Shutdown();
}
void *JoltPhysicsInterface::QueryInterface( const char *pInterfaceName )
{
CreateInterfaceFn factory = Sys_GetFactoryThis();
return factory( pInterfaceName, NULL );
}
//-------------------------------------------------------------------------------------------------
IPhysicsEnvironment *JoltPhysicsInterface::CreateEnvironment()
{
return new JoltPhysicsEnvironment();
}
void JoltPhysicsInterface::DestroyEnvironment( IPhysicsEnvironment *pEnvironment )
{
delete static_cast<JoltPhysicsEnvironment *>( pEnvironment );
}
IPhysicsEnvironment *JoltPhysicsInterface::GetActiveEnvironmentByIndex( int index )
{
// Josh: Nothing uses this... ever.
Log_Stub( LOG_VJolt );
return nullptr;
}
//-------------------------------------------------------------------------------------------------
IPhysicsObjectPairHash *JoltPhysicsInterface::CreateObjectPairHash()
{
return new JoltPhysicsObjectPairHash;
}
void JoltPhysicsInterface::DestroyObjectPairHash( IPhysicsObjectPairHash *pHash )
{
delete static_cast<JoltPhysicsObjectPairHash *>( pHash );
}
//-------------------------------------------------------------------------------------------------
IPhysicsCollisionSet *JoltPhysicsInterface::FindOrCreateCollisionSet( unsigned int id, int maxElementCount )
{
if ( maxElementCount > 32 )
return nullptr;
if ( IPhysicsCollisionSet *pSet = FindCollisionSet( id ) )
return pSet;
auto result = m_CollisionSets.emplace( id, JoltPhysicsCollisionSet{} );
return &result.first->second;
}
IPhysicsCollisionSet *JoltPhysicsInterface::FindCollisionSet( unsigned int id )
{
auto iter = m_CollisionSets.find( id );
if ( iter != m_CollisionSets.end() )
return &iter->second;
return nullptr;
}
void JoltPhysicsInterface::DestroyAllCollisionSets()
{
m_CollisionSets.clear();
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsInterface::OnTrace( const char *fmt, ... )
{
va_list args;
char msg[MAX_LOGGING_MESSAGE_LENGTH];
va_start( args, fmt );
V_vsnprintf( msg, sizeof( msg ), fmt, args );
va_end( args );
Log_Msg( LOG_JoltInternal, "%s\n", msg );
}
bool JoltPhysicsInterface::OnAssert( const char *inExpression, const char *inMessage, const char *inFile, uint inLine )
{
const char *message = inMessage ? inMessage : inExpression;
(void) message;
AssertMsg_Internal( false, inLine, inFile, message );
return false;
}

View File

@ -0,0 +1,130 @@
//=================================================================================================
//
// The base physics DLL interface
//
//=================================================================================================
#pragma once
//-------------------------------------------------------------------------------------------------
// Whether to use the VPhysics debug overlay (legacy compatible) or the fully featured IVDebugOverlay
// In Desolation, IVDebugOverlay has new members that allow us to draw IMesh objects, without this
// debugoverlay rendering is incredibly inefficient (and may run the materialsystem mempool out of memory)
#if !defined( GAME_DESOLATION ) || defined( BUILD_FOR_EXTERNAL_GAME )
#define VJOLT_USE_PHYSICS_DEBUG_OVERLAY
#endif
#ifdef VJOLT_USE_PHYSICS_DEBUG_OVERLAY
class IVPhysicsDebugOverlay;
using IVJoltDebugOverlay = IVPhysicsDebugOverlay;
#define VJOLT_DEBUG_OVERLAY_VERSION VPHYSICS_DEBUG_OVERLAY_INTERFACE_VERSION
#else
class IVDebugOverlay;
using IVJoltDebugOverlay = IVDebugOverlay;
#define VJOLT_DEBUG_OVERLAY_VERSION VDEBUG_OVERLAY_INTERFACE_VERSION
#endif
// Call this in stubbed functions to spew when they're hit
#if 1 // DEVELOPMENT_ONLY
#define Log_Stub( Channel )
#else
#define Log_Stub( Channel ) \
Log_Warning( Channel, "Stub: %s\n", __FUNCTION__ )
#endif
// So we can toggle assertions in this module at our discretion
#if DEVELOPMENT_ONLY
#define VJoltAssert DevAssert
#define VJoltAssertMsg DevAssertMsg
#else
#define VJoltAssert Assert
#define VJoltAssertMsg AssertMsg
#endif
DECLARE_LOGGING_CHANNEL( LOG_VJolt ); // For our vphysics_jolt code
DECLARE_LOGGING_CHANNEL( LOG_JoltInternal ); // For Jolt's traces/assertions. Do NOT use for our code.
//-------------------------------------------------------------------------------------------------
class JoltPhysicsCollisionSet final : public IPhysicsCollisionSet
{
public:
void EnableCollisions( int index0, int index1 ) override
{
m_Bits[ index0 ] |= 1u << index1;
m_Bits[ index1 ] |= 1u << index0;
}
void DisableCollisions( int index0, int index1 ) override
{
m_Bits[ index0 ] &= ~( 1u << index1 );
m_Bits[ index1 ] &= ~( 1u << index0 );
}
bool ShouldCollide( int index0, int index1 ) override
{
return !!( m_Bits[ index0 ] & ( 1u << index1 ) );
}
private:
std::array< uint32, 32 > m_Bits = {};
};
//-------------------------------------------------------------------------------------------------
class JoltPhysicsInterface final : public CTier1AppSystem<IPhysics>
{
private:
using BaseClass = CTier1AppSystem<IPhysics>;
public:
InitReturnVal_t Init() override;
void Shutdown() override;
void *QueryInterface( const char *pInterfaceName ) override;
IPhysicsEnvironment *CreateEnvironment() override;
void DestroyEnvironment( IPhysicsEnvironment *pEnvironment ) override;
IPhysicsEnvironment *GetActiveEnvironmentByIndex( int index ) override;
IPhysicsObjectPairHash *CreateObjectPairHash() override;
void DestroyObjectPairHash( IPhysicsObjectPairHash *pHash ) override;
IPhysicsCollisionSet *FindOrCreateCollisionSet( unsigned int id, int maxElementCount ) override;
IPhysicsCollisionSet *FindCollisionSet( unsigned int id ) override;
void DestroyAllCollisionSets() override;
public:
static JoltPhysicsInterface &GetInstance() { return s_PhysicsInterface; }
JPH::TempAllocator *GetTempAllocator() { return m_pTempAllocator; }
JPH::JobSystem *GetJobSystem() { return m_pJobSystem; }
void SetDebugOverlay( IVJoltDebugOverlay *pOverlay ) { if ( m_pDebugOverlay != pOverlay ) m_pDebugOverlay = pOverlay; }
IVJoltDebugOverlay *GetDebugOverlay() { return m_pDebugOverlay; }
private:
static void OnTrace( const char *fmt, ... );
static bool OnAssert( const char *inExpression, const char *inMessage, const char *inFile, uint inLine );
std::unordered_map< unsigned int, JoltPhysicsCollisionSet > m_CollisionSets;
// We need a temp allocator for temporary allocations during the physics update. We're
// pre-allocating 10 MB to avoid having to do allocations during the physics update.
// B.t.w. 10 MB is way too much for this example but it is a typical value you can use.
// If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to
// malloc / free.
JPH::TempAllocator *m_pTempAllocator;
// We need a job system that will execute physics jobs on multiple threads. Typically
// you would implement the JobSystem interface yourself and let Jolt Physics run on top
// of your own job scheduler. JobSystemThreadPool is an example implementation.
JPH::JobSystem *m_pJobSystem;
// For debugging stuff in collide and such.
IVJoltDebugOverlay *m_pDebugOverlay = nullptr;
static JoltPhysicsInterface s_PhysicsInterface;
};

View File

@ -0,0 +1,24 @@
#pragma once
class JoltPhysicsObject;
abstract_class IJoltPhysicsController
{
public:
virtual ~IJoltPhysicsController() {}
// Called before the simulation is run
virtual void OnPreSimulate( float flDeltaTime ) {};
// Called after the simulation is run
virtual void OnPostSimulate( float flDeltaTime ) {};
};
abstract_class IJoltObjectDestroyedListener
{
public:
virtual ~IJoltObjectDestroyedListener() {}
// Called whenever a physics object is destroyed
virtual void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) = 0;
};

View File

@ -0,0 +1,238 @@
#include "cbase.h"
#include "vjolt_surfaceprops.h"
#include "vjolt_keyvalues_schema.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
const JoltKVSchemaFunc_t FillBaseProp =
{
sizeof( surfacedata_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
surfacedata_t *pSurfaceDataPtr = reinterpret_cast< surfacedata_t * >( pPtr );
int nSurfaceIndex = JoltPhysicsSurfaceProps::GetInstance().GetSurfaceIndex( pProp->GetString() );
if ( nSurfaceIndex == -1 )
{
Log_Warning( LOG_VJolt, "You must specify the base material %s before it can be used. Defaulting to 'default' as a base.\n", pProp->GetString() );
// Always the default material.
nSurfaceIndex = 0;
}
*pSurfaceDataPtr = *JoltPhysicsSurfaceProps::GetInstance().GetSurfaceData( nSurfaceIndex );
}
};
const JoltKVSchemaFunc_t FillStringProp =
{
0, // Varies.
[]( KeyValues *pProp, void *pPtr, size_t size )
{
char *pszStringPtr = reinterpret_cast< char * >( pPtr );
V_strncpy( pszStringPtr, pProp->GetString(), static_cast< strlen_t >( size ) );
}
};
const JoltKVSchemaFunc_t FillIntProp =
{
sizeof( int ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
int *pIntPtr = reinterpret_cast< int * >( pPtr );
*pIntPtr = pProp->GetInt();
}
};
const JoltKVSchemaFunc_t FillFloatProp =
{
sizeof( float ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
float *pFloatPtr = reinterpret_cast< float * >( pPtr );
*pFloatPtr = pProp->GetFloat();
}
};
const JoltKVSchemaFunc_t FillUnsignedCharProp =
{
sizeof( unsigned char ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
unsigned char *pCharPtr = reinterpret_cast< unsigned char * >( pPtr );
*pCharPtr = static_cast< unsigned char >( pProp->GetInt() );
}
};
const JoltKVSchemaFunc_t FillBoolProp =
{
sizeof( bool ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
bool *pBoolPtr = reinterpret_cast< bool * >( pPtr );
*pBoolPtr = pProp->GetBool();
}
};
const JoltKVSchemaFunc_t FillVectorProp =
{
sizeof( Vector ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
Vector *pVectorPtr = reinterpret_cast< Vector * >( pPtr );
sscanf( pProp->GetString(), "%f %f %f", &pVectorPtr->x, &pVectorPtr->y, &pVectorPtr->z );
}
};
const JoltKVSchemaFunc_t FillVector4DProp =
{
sizeof( Vector4D ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
Vector4D *pVector4DPtr = reinterpret_cast< Vector4D * >( pPtr );
sscanf( pProp->GetString(), "%f %f %f %f", &pVector4DPtr->x, &pVector4DPtr->y, &pVector4DPtr->z, &pVector4DPtr->w );
}
};
const JoltKVSchemaFunc_t FillQAngleProp =
{
sizeof( QAngle ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
QAngle *pQAnglePtr = reinterpret_cast< QAngle * >( pPtr );
sscanf( pProp->GetString(), "%f %f %f", &pQAnglePtr->x, &pQAnglePtr->y, &pQAnglePtr->z );
}
};
const JoltKVSchemaFunc_t FillIntPairProp =
{
sizeof( JoltPhysicsIntPair ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
JoltPhysicsIntPair *pIntPairPtr = reinterpret_cast< JoltPhysicsIntPair * >( pPtr );
sscanf( pProp->GetString(), "%d,%d", &pIntPairPtr->Index0, &pIntPairPtr->Index1 );
}
};
const JoltKVSchemaFunc_t FillGameMaterialProp =
{
sizeof( unsigned short ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
const char *pValue = pProp->GetString();
unsigned short *pShortPtr = reinterpret_cast< unsigned short * >( pPtr );
if ( V_strlen( pValue ) == 1 && !V_isdigit( pValue[ 0 ] ) )
*pShortPtr = FastASCIIToUpper( pValue[ 0 ] );
else
*pShortPtr = pProp->GetInt();
}
};
const JoltKVSchemaFunc_t FillSoundProp =
{
sizeof( unsigned short ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
const char *pValue = pProp->GetString();
unsigned short *pShortPtr = reinterpret_cast< unsigned short * >( pPtr );
*pShortPtr = JoltPhysicsSurfaceProps::GetInstance().RegisterSound( pValue );
}
};
const JoltKVSchemaFunc_t FillSurfaceProp =
{
sizeof( int ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
const char* pValue = pProp->GetString();
int *pIntPtr = reinterpret_cast< int * >( pPtr );
*pIntPtr = JoltPhysicsSurfaceProps::GetInstance().GetSurfaceIndex( pValue );
}
};
//-------------------------------------------------------------------------------------------------
void ParseJoltKVSchema( KeyValues *pKV, const JoltKVSchemaProp_t *pDescs, uint count, void *pObj, void *pUnknownKeyObj, IVPhysicsKeyHandler *pUnknownKeyHandler )
{
bool bHandled = false;
for ( KeyValues* pProp = pKV->GetFirstSubKey(); pProp != nullptr; pProp = pProp->GetNextKey() )
{
const char *pName = pProp->GetName();
for ( uint i = 0; i < count; i++ )
{
const JoltKVSchemaProp_t &desc = pDescs[ i ];
if ( !V_stricmp( pName, desc.pszName ) )
{
VJoltAssertMsg( desc.func.ptr_size == 0 || desc.size == desc.func.ptr_size, "Desc element size does not match function size");
int *pArraySize = nullptr;
if ( desc.arrayOffset != static_cast<size_t>( ~0llu ) )
pArraySize = reinterpret_cast< int * >( reinterpret_cast< char * >( pObj ) + desc.arrayOffset );
char* pElement = reinterpret_cast< char * >( pObj ) + desc.offset;
if ( pArraySize )
pElement += *pArraySize * desc.size;
desc.func.ReadFunc( pProp, reinterpret_cast< void * >( pElement ), desc.size);
if ( desc.fixupFunc )
desc.fixupFunc( pObj );
if ( pArraySize )
( *pArraySize )++;
bHandled = true;
}
}
if ( !bHandled && pUnknownKeyHandler )
pUnknownKeyHandler->ParseKeyValue( pUnknownKeyObj, pName, pProp->GetString() );
}
}
void ParseJoltKVCustom( KeyValues *pKV, void *pUnknownKeyObj, IVPhysicsKeyHandler* pUnknownKeyHandler )
{
// Josh:
// Parse out custom KV entries like "vehicle_sounds" etc
// out recursively.
for ( KeyValues* pProp = pKV->GetFirstSubKey(); pProp != nullptr; pProp = pProp->GetNextKey() )
{
if ( pUnknownKeyHandler )
pUnknownKeyHandler->ParseKeyValue( pUnknownKeyObj, pProp->GetName(), pProp->GetString());
ParseJoltKVCustom( pProp, pUnknownKeyObj, pUnknownKeyHandler );
}
}
//-------------------------------------------------------------------------------------------------
KeyValues *HeaderlessKVBufferToKeyValues( const char *pszBuffer, const char *pszSetName )
{
CUtlBuffer buffer;
buffer.SetBufferType( true, true );
buffer.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
buffer.PutString( "\"PhysProps\"\r\n{" );
buffer.PutString( pszBuffer );
buffer.PutString( "\r\n}" );
buffer.PutChar( '\0' );
KeyValues *pszKV = new KeyValues( pszSetName );
if ( !pszKV->LoadFromBuffer( pszSetName, buffer ) )
{
pszKV->deleteThis();
pszKV = nullptr;
}
return pszKV;
}

View File

@ -0,0 +1,56 @@
#pragma once
// This function fixes up the base object
// after loading a single KV value.
using JoltKVSchemaFixupFunc_t = void (*)( void *pBaseObject );
struct JoltKVSchemaFunc_t
{
size_t ptr_size;
void ( *ReadFunc )( KeyValues *pProp, void *pPtr, size_t size );
};
struct JoltKVSchemaProp_t
{
const char *pszName;
size_t offset;
size_t size;
size_t arrayOffset;
JoltKVSchemaFunc_t func;
JoltKVSchemaFixupFunc_t fixupFunc;
};
struct JoltPhysicsIntPair
{
int Index0;
int Index1;
};
extern const JoltKVSchemaFunc_t FillBaseProp;
extern const JoltKVSchemaFunc_t FillStringProp;
extern const JoltKVSchemaFunc_t FillIntProp;
extern const JoltKVSchemaFunc_t FillFloatProp;
extern const JoltKVSchemaFunc_t FillUnsignedCharProp;
extern const JoltKVSchemaFunc_t FillBoolProp;
extern const JoltKVSchemaFunc_t FillVectorProp;
extern const JoltKVSchemaFunc_t FillVector4DProp;
extern const JoltKVSchemaFunc_t FillQAngleProp;
extern const JoltKVSchemaFunc_t FillIntPairProp;
extern const JoltKVSchemaFunc_t FillGameMaterialProp;
extern const JoltKVSchemaFunc_t FillSurfaceProp;
extern const JoltKVSchemaFunc_t FillSoundProp;
#define KVSCHEMA_DESC_ARRAY( type, x, len ) \
offsetof( type, x ), sizeof( *type::x ), offsetof( type, len )
#define KVSCHEMA_DESC( type, x ) \
offsetof( type, x ), sizeof( type::x ), static_cast<size_t>(~0llu)
#define KVSCHEMA_DESC_NO_OFFSET( type ) \
0, sizeof( type ), static_cast<size_t>(~0llu)
void ParseJoltKVSchema( KeyValues *pKV, const JoltKVSchemaProp_t *pDescs, uint count, void *pObj, void *pUnknownKeyObj = nullptr, IVPhysicsKeyHandler* pUnknownKeyHandler = nullptr );
void ParseJoltKVCustom( KeyValues *pKV, void *pUnknownKeyObj, IVPhysicsKeyHandler* pUnknownKeyHandler );
KeyValues *HeaderlessKVBufferToKeyValues( const char *pszBuffer, const char *pszSetName );

View File

@ -0,0 +1,30 @@
#pragma once
// Layer that objects can be in, determines which other objects it can collide with
// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more
// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation
// but only if you do collision testing).
namespace Layers
{
inline constexpr uint8 NON_MOVING_WORLD = 0;
inline constexpr uint8 NON_MOVING_OBJECT = 1;
inline constexpr uint8 MOVING = 2;
inline constexpr uint8 NO_COLLIDE = 3; // Disables collisions with everything, including the world.
inline constexpr uint8 DEBRIS = 4; // Disables collisions with everything except the world
inline constexpr uint8 NUM_LAYERS = 5;
};
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame.
// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have
// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune
// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY.
namespace BroadPhaseLayers
{
inline constexpr JPH::BroadPhaseLayer NON_MOVING_WORLD( 0 );
inline constexpr JPH::BroadPhaseLayer NON_MOVING_OBJECT( 1 );
inline constexpr JPH::BroadPhaseLayer MOVING( 2 );
inline constexpr JPH::BroadPhaseLayer NO_COLLIDE( 3 );
inline constexpr JPH::BroadPhaseLayer DEBRIS( 4 );
};

View File

@ -0,0 +1,572 @@
#pragma once
#include "vjolt_controller_fluid.h"
struct JoltPhysicsContactPair
{
JoltPhysicsObject *pObject1 = nullptr;
JoltPhysicsObject *pObject2 = nullptr;
};
enum VPhysicsGameFlags : uint32
{
FVPHYSICS_DMG_SLICE = 0x0001,
FVPHYSICS_CONSTRAINT_STATIC = 0x0002,
FVPHYSICS_PLAYER_HELD = 0x0004,
FVPHYSICS_PART_OF_RAGDOLL = 0x0008,
FVPHYSICS_MULTIOBJECT_ENTITY = 0x0010,
FVPHYSICS_HEAVY_OBJECT = 0x0020,
FVPHYSICS_PENETRATING = 0x0040,
FVPHYSICS_NO_PLAYER_PICKUP = 0x0080,
FVPHYSICS_WAS_THROWN = 0x0100,
FVPHYSICS_DMG_DISSOLVE = 0x0200,
FVPHYSICS_NO_IMPACT_DMG = 0x0400,
FVPHYSICS_NO_NPC_IMPACT_DMG = 0x0800,
FVPHYSICS_PUSH_PLAYER = 0x1000,
FVPHYSICS_NO_SELF_COLLISIONS = 0x8000,
};
class JoltPhysicsContactListener final : public JPH::ContactListener
{
public:
JoltPhysicsContactListener( JPH::PhysicsSystem &physicsSystem )
: m_PhysicsSystem( physicsSystem )
{
}
JPH::ValidateResult OnContactValidate( const JPH::Body &inBody1, const JPH::Body &inBody2, const JPH::CollideShapeResult &inCollisionResult ) override
{
return JPH::ValidateResult::AcceptAllContactsForThisBodyPair;
}
void OnContactAdded( const JPH::Body &inBody1, const JPH::Body &inBody2, const JPH::ContactManifold &inManifold, JPH::ContactSettings &ioSettings ) override
{
JoltPhysicsObject* pObject1 = reinterpret_cast<JoltPhysicsObject*>( inBody1.GetUserData() );
JoltPhysicsObject* pObject2 = reinterpret_cast<JoltPhysicsObject*>( inBody2.GetUserData() );
bool bShouldCollide = ShouldCollide( pObject1, pObject2 );
// If the game says we shouldn't collide, we will treat this as a sensor
// to satisfy the StartTouch/EndTouch events.
ioSettings.mIsSensor = !bShouldCollide || ioSettings.mIsSensor;
if ( !m_pGameListener )
return;
if ( pObject1->IsFluid() || pObject2->IsFluid() )
{
const uint32 uThreadId = GetThreadId();
if ( pObject1->IsFluid() && ( pObject2->GetCallbackFlags() & CALLBACK_FLUID_TOUCH ) )
m_FluidStartTouchEvents.EmplaceBack( uThreadId, pObject1, pObject2 );
if ( pObject2->IsFluid() && ( pObject1->GetCallbackFlags() & CALLBACK_FLUID_TOUCH ) )
m_FluidStartTouchEvents.EmplaceBack( uThreadId, pObject2, pObject1 );
return;
}
if ( pObject1->IsTrigger() || pObject2->IsTrigger() )
{
const uint32 uThreadId = GetThreadId();
if ( pObject1->IsTrigger() )
m_EnterTriggerEvents.EmplaceBack( uThreadId, pObject1, pObject2 );
if ( pObject2->IsTrigger() )
m_EnterTriggerEvents.EmplaceBack( uThreadId, pObject2, pObject1 );
return;
}
const bool bIsCollision = bShouldCollide && JoltPhysicsCollisionEvent::IsCollision ( pObject1, pObject2 );
const bool bIsShadowCollision = bShouldCollide && JoltPhysicsCollisionEvent::IsShadowCollision( pObject1, pObject2 );
if ( bIsCollision || bIsShadowCollision )
{
// Josh:
// We know ahead of time what this is used for (playing sounds and such)
// and it is not easily threadable
// (unlike the StartTouch objects which we can get away as long as the objects themselves aren't concurrent it seems)
// To avoid this causing locks and therefore lagging with many objects,
// we can just know ahead of time what is going to cause a sound to play, which is
// hardcoded at speed > 70.0f and deltaTime < 0.05 (the latter of which we don't track)
// So we can just avoid sending these PreCollision in this case.
const Vector vecCollideNormal = Vector( inManifold.mWorldSpaceNormal.GetX(), inManifold.mWorldSpaceNormal.GetY(), inManifold.mWorldSpaceNormal.GetZ() );
const float flCollisionSpeed = JoltPhysicsCollisionEvent::GetCollisionSpeed( pObject1, pObject2, vecCollideNormal );
const bool bHasSound =
flCollisionSpeed >= 70.0f &&
pObject1->GetGameMaterialAllowsSounds() &&
pObject2->GetGameMaterialAllowsSounds();
const bool bSane = m_GlobalCollisionEventCount < MaxCollisionEvents;
const bool bSendCollisionCallback = ( bHasSound && bSane ) || bIsShadowCollision;
if ( bSendCollisionCallback )
{
m_CollisionEvents.EmplaceBack( GetThreadId(), JoltPhysicsCollisionInfo( pObject1, pObject2, inManifold ) );
m_GlobalCollisionEventCount++;
}
}
if ( ShouldTouchCallback( pObject1, pObject2 ) )
m_StartTouchEvents.EmplaceBack( GetThreadId(), JoltPhysicsCollisionInfo( pObject1, pObject2, inManifold ) );
}
void OnContactPersisted( const JPH::Body &inBody1, const JPH::Body &inBody2, const JPH::ContactManifold &inManifold, JPH::ContactSettings &ioSettings ) override
{
JoltPhysicsObject* pObject1 = reinterpret_cast<JoltPhysicsObject*>( inBody1.GetUserData() );
JoltPhysicsObject* pObject2 = reinterpret_cast<JoltPhysicsObject*>( inBody2.GetUserData() );
bool bShouldCollide = ShouldCollide( pObject1, pObject2 );
// If the game says we shouldn't collide, we will treat this as a sensor
// to satisfy the StartTouch/EndTouch events.
ioSettings.mIsSensor = !bShouldCollide || ioSettings.mIsSensor;
if ( !m_pGameListener )
return;
// TODO(Josh): Need a way to calculate the energy to send friction callbacks.
//
//JoltPhysicsObject *pObject1 = reinterpret_cast< JoltPhysicsObject * >( inBody1.GetUserData() );
//JoltPhysicsObject *pObject2 = reinterpret_cast< JoltPhysicsObject * >( inBody2.GetUserData() );
//
//if ( !ShouldFrictionCallback( pObject1, pObject2 ) )
// return;
//
//JoltPhysicsCollisionData data( inManifold );
//std::unique_lock lock( m_CallbackMutex );
//m_pGameListener->Friction( pObject1, 15500.0f, pObject1->GetMaterialIndex(), pObject2->GetMaterialIndex(), &data );
//m_pGameListener->Friction( pObject2, 15500.0f, pObject2->GetMaterialIndex(), pObject1->GetMaterialIndex(), &data );
}
void OnContactRemoved( const JPH::SubShapeIDPair &inSubShapePair )
{
if ( !m_pGameListener )
return;
// This is always called with all bodies locked.
const JPH::BodyLockInterfaceNoLock &bodyInterface = m_PhysicsSystem.GetBodyLockInterfaceNoLock();
JPH::Body *pBody1 = bodyInterface.TryGetBody( inSubShapePair.GetBody1ID() );
JPH::Body *pBody2 = bodyInterface.TryGetBody( inSubShapePair.GetBody2ID() );
// One of the bodies may have been deleted.
// TODO(Josh): Handle calling end touch when we delete a body.
if ( !pBody1 || !pBody2 )
return;
JoltPhysicsObject *pObject1 = reinterpret_cast< JoltPhysicsObject * >( pBody1->GetUserData() );
JoltPhysicsObject *pObject2 = reinterpret_cast< JoltPhysicsObject * >( pBody2->GetUserData() );
if ( pObject1->IsFluid() || pObject2->IsFluid() )
{
const uint32 uThreadId = GetThreadId();
if ( pObject1->IsFluid() && ( pObject2->GetCallbackFlags() & CALLBACK_FLUID_TOUCH ) )
m_FluidEndTouchEvents.EmplaceBack( uThreadId, pObject1, pObject2 );
if ( pObject2->IsFluid() && ( pObject1->GetCallbackFlags() & CALLBACK_FLUID_TOUCH ) )
m_FluidEndTouchEvents.EmplaceBack( uThreadId, pObject2, pObject1 );
return;
}
if ( pObject1->IsTrigger() || pObject2->IsTrigger() )
{
const uint32 uThreadId = GetThreadId();
if ( pObject1->IsTrigger() )
m_LeaveTriggerEvents.EmplaceBack( uThreadId, pObject1, pObject2 );
if ( pObject2->IsTrigger() )
m_LeaveTriggerEvents.EmplaceBack( uThreadId, pObject2, pObject1 );
return;
}
if ( !ShouldTouchCallback( pObject1, pObject2 ) )
return;
const uint32 uThreadId = GetThreadId();
// Josh:
// We don't have any collision data here
// and caching it would be annoying and expensive.
//
// Lucky for us though, the game simply just calls the stuff
// to retrieve the contact point and normal, then just never uses it
// so we can return anything we want and it will change *nothing*!
m_EndTouchEvents.EmplaceBack( uThreadId, JoltPhysicsCollisionInfo( pObject1, pObject2 ) );
}
bool ShouldCollide( JoltPhysicsObject *pObject0, JoltPhysicsObject *pObject1 )
{
VJoltAssert( pObject0 != pObject1 );
if ( !pObject0 || !pObject1 )
return false;
if ( ( pObject0->GetCallbackFlags() & CALLBACK_ENABLING_COLLISION ) && ( pObject1->GetCallbackFlags() & CALLBACK_MARKED_FOR_DELETE ) )
return false;
if ( ( pObject1->GetCallbackFlags() & CALLBACK_ENABLING_COLLISION ) && ( pObject0->GetCallbackFlags() & CALLBACK_MARKED_FOR_DELETE ) )
return false;
if ( !m_pGameSolver )
return true;
// Josh:
// Do some work the game does ahead of time to
// avoid needless locking.
if ( !PreEmptGameShouldCollide( pObject0, pObject1 ) )
return false;
// Actually ask the game now, locking both bodies so they cannot have
// concurrent ShouldCollide calls.
//JoltPhysicsObjectPairLock lock( pObject0->GetCollisionTestLock(), pObject1->GetCollisionTestLock() );
std::unique_lock lock( m_ShouldCollideLock );
return m_pGameSolver->ShouldCollide( pObject0, pObject1, pObject0->GetGameData(), pObject1->GetGameData() );
}
bool PreEmptGameShouldCollide( JoltPhysicsObject *pObject0, JoltPhysicsObject *pObject1 )
{
// This function pre-empts the result of the
// game's ShouldCollide implementation to avoid needless locking.
// Check if the entities are the same and self-collisions are disabled.
if ( pObject0->GetGameData() == pObject1->GetGameData() )
{
if ( ( pObject0->GetGameFlags() | pObject1->GetGameFlags() ) & FVPHYSICS_NO_SELF_COLLISIONS )
return false;
}
// If both of these are constrained to the world, they shouldn't collide.
if ( pObject0->GetGameFlags() & pObject1->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC )
return false;
// We do wheels separately, the IS_VEHICLE_WHEEL is just to have some dummy object to return to the game.
if ( ( pObject0->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL ) || ( pObject1->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL ) )
return false;
// Two shadow controlled objects should not collide.
if ( pObject0->GetShadowController() && pObject1->GetShadowController() )
return false;
return true;
}
bool ShouldFrictionCallback( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2 )
{
if ( !( pObject1->GetCallbackFlags() & CALLBACK_GLOBAL_FRICTION ) )
return false;
if ( !( pObject2->GetCallbackFlags() & CALLBACK_GLOBAL_FRICTION ) )
return false;
return true;
}
bool ShouldTouchCallback( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2 )
{
uint32 uFlags = 0;
uFlags |= pObject1->GetCallbackFlags();
uFlags |= pObject2->GetCallbackFlags();
if ( !( uFlags & CALLBACK_GLOBAL_TOUCH ) )
return false;
if ( !( uFlags & CALLBACK_GLOBAL_TOUCH_STATIC ) && ( pObject1->IsStatic() || pObject2->IsStatic() ) )
return false;
return true;
}
IPhysicsCollisionEvent *GetGameListener()
{
return m_pGameListener;
}
void SetGameListener( IPhysicsCollisionEvent *pListener )
{
m_pGameListener = pListener;
}
IPhysicsCollisionSolver *GetGameSolver()
{
return m_pGameSolver;
}
void SetGameSolver( IPhysicsCollisionSolver *pSolver )
{
m_pGameSolver = pSolver;
}
void FlushCallbacks()
{
if ( !m_pGameListener )
return;
// Send PreCollision events
//
// Don't clear the collision events the first time around as we have
// the post-collisde event to send too!
m_CollisionEvents.ForEach< false >( [ this ]( JoltPhysicsCollisionEvent& event )
{
// Fake the velocities for the objects during the PreCollision callback so
// we get a proper delta velocity between Pre/Post for damage callbacks to work.
JPH::Vec3 object1Vel = event.m_Data.GetPair().pObject1->FakeJoltLinearVelocity( event.m_Data.GetObject1PreCollisionVelocity() );
JPH::Vec3 object2Vel = event.m_Data.GetPair().pObject2->FakeJoltLinearVelocity( event.m_Data.GetObject2PreCollisionVelocity() );
m_pGameListener->PreCollision( &event.m_Event );
event.m_Data.GetPair().pObject1->RestoreJoltLinearVelocity( object1Vel );
event.m_Data.GetPair().pObject2->RestoreJoltLinearVelocity( object2Vel );
});
// Send StartTouch events
m_StartTouchEvents.ForEach< true >( [ this ]( JoltPhysicsCollisionData& event )
{
m_pGameListener->StartTouch( event.GetPair().pObject1, event.GetPair().pObject2, &event );
});
// Send EnterTrigger events
m_EnterTriggerEvents.ForEach< true >( [ this ]( JoltPhysicsContactPair& event )
{
m_pGameListener->ObjectEnterTrigger( event.pObject1, event.pObject2 );
});
// Send FluidStartTouch events
m_FluidStartTouchEvents.ForEach< true >( [ this ]( JoltPhysicsContactPair& event )
{
m_pGameListener->FluidStartTouch( event.pObject2, event.pObject1->GetFluidController() );
});
// Send PostCollision events
//
// Clear it this time as we are done with these!
m_CollisionEvents.ForEach< true >( [ this ]( JoltPhysicsCollisionEvent& event )
{
m_pGameListener->PostCollision( &event.m_Event );
});
// Send EndTouch events
m_EndTouchEvents.ForEach< true >( [ this ]( JoltPhysicsCollisionData& event )
{
m_pGameListener->EndTouch( event.GetPair().pObject1, event.GetPair().pObject2, &event );
});
// Send LeaveTrigger events
m_LeaveTriggerEvents.ForEach< true >( [ this ]( JoltPhysicsContactPair& event )
{
m_pGameListener->ObjectLeaveTrigger( event.pObject1, event.pObject2 );
});
// Send FluidEndTouch events
m_FluidEndTouchEvents.ForEach< true >( [ this ]( JoltPhysicsContactPair& event )
{
m_pGameListener->FluidEndTouch( event.pObject2, event.pObject1->GetFluidController() );
});
// Reset the collision event counter.
m_GlobalCollisionEventCount = 0u;
}
void PostSimulationFrame()
{
if ( m_pGameListener )
m_pGameListener->PostSimulationFrame();
}
private:
static uint32 GetThreadId()
{
static thread_local uint32 s_ThreadId = ~0u;
static std::atomic< uint32 > s_ThreadCtr = { 0u };
if ( s_ThreadId == ~0u )
s_ThreadId = s_ThreadCtr++;
return s_ThreadId;
}
const JPH::PhysicsSystem &m_PhysicsSystem;
IPhysicsCollisionEvent *m_pGameListener = nullptr;
IPhysicsCollisionSolver *m_pGameSolver = nullptr;
std::mutex m_ShouldCollideLock;
class JoltPhysicsCollisionInfo
{
public:
JoltPhysicsCollisionInfo( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2 )
: m_CollisionPair{ pObject1, pObject2 }
{
}
JoltPhysicsCollisionInfo( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2, const JPH::ContactManifold &inManifold )
: m_CollisionPair{ pObject1, pObject2 }
// Slart: Note this negated vector, it is important, Portal 2 bouncy paint needs it negated otherwise things fly into the surface they hit
, m_SurfaceNormal( -Vector( inManifold.mWorldSpaceNormal.GetX(), inManifold.mWorldSpaceNormal.GetY(), inManifold.mWorldSpaceNormal.GetZ() ) )
, m_ContactPoint( JoltToSource::Distance( inManifold.mWorldSpaceContactPointsOn1[0] ) )
// Unused...
, m_ContactSpeed( vec3_origin )
, m_Velocity0( pObject1->GetBody()->GetLinearVelocity() )
, m_Velocity1( pObject1->GetBody()->GetLinearVelocity() )
{
}
JoltPhysicsContactPair m_CollisionPair;
Vector m_SurfaceNormal = vec3_origin;
Vector m_ContactPoint = vec3_origin;
Vector m_ContactSpeed = vec3_origin;
JPH::Vec3 m_Velocity0 = JPH::Vec3::sZero();
JPH::Vec3 m_Velocity1 = JPH::Vec3::sZero();
};
class JoltPhysicsCollisionData final : public IPhysicsCollisionData
{
public:
JoltPhysicsCollisionData( const JoltPhysicsCollisionInfo &info )
: m_CollisionData{ info }
{
}
void GetSurfaceNormal( Vector &out ) override
{
out = m_CollisionData.m_SurfaceNormal;
}
void GetContactPoint( Vector &out ) override
{
out = m_CollisionData.m_ContactPoint;
}
void GetContactSpeed( Vector &out ) override
{
out = m_CollisionData.m_ContactSpeed;
}
JoltPhysicsContactPair GetPair() const
{
return m_CollisionData.m_CollisionPair;
}
JPH::Vec3 GetObject1PreCollisionVelocity() const
{
return m_CollisionData.m_Velocity0;
}
JPH::Vec3 GetObject2PreCollisionVelocity() const
{
return m_CollisionData.m_Velocity1;
}
private:
JoltPhysicsCollisionInfo m_CollisionData;
};
class JoltPhysicsCollisionEvent
{
public:
JoltPhysicsCollisionEvent( const JoltPhysicsCollisionInfo &info )
: m_Data{ info }
{
JoltPhysicsObject *pObject1 = m_Data.GetPair().pObject1;
JoltPhysicsObject* pObject2 = m_Data.GetPair().pObject2;
m_Event.pObjects[0] = pObject1;
m_Event.pObjects[1] = pObject2;
m_Event.surfaceProps[0] = pObject1->GetMaterialIndex();
m_Event.surfaceProps[1] = pObject2->GetMaterialIndex();
m_Event.isCollision = IsCollision( pObject1, pObject2 );
m_Event.isShadowCollision = IsShadowCollision( pObject1, pObject2 );
m_Event.deltaCollisionTime = 100.0f;
m_Event.collisionSpeed = GetCollisionSpeed( pObject1, pObject2, info.m_SurfaceNormal );
m_Event.pInternalData = &m_Data;
}
static bool IsCollision( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2 )
{
bool bIsCollision = ( pObject1->GetCallbackFlags() & pObject2->GetCallbackFlags() ) & CALLBACK_GLOBAL_COLLISION;
if ( pObject1->IsStatic() && !( pObject2->GetCallbackFlags() & CALLBACK_GLOBAL_COLLIDE_STATIC ) )
bIsCollision = false;
if ( pObject2->IsStatic() && !( pObject1->GetCallbackFlags() & CALLBACK_GLOBAL_COLLIDE_STATIC ) )
bIsCollision = false;
return bIsCollision;
}
static bool IsShadowCollision( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2 )
{
return ( pObject1->GetCallbackFlags() ^ pObject2->GetCallbackFlags() ) & CALLBACK_SHADOW_COLLISION;
}
static float GetCollisionSpeed( JoltPhysicsObject *pObject1, JoltPhysicsObject *pObject2, Vector vecNormal )
{
const Vector vecCollisionSpeed = pObject1->GetVelocity() - pObject2->GetVelocity();
return fabsf( vecCollisionSpeed.Dot( vecNormal ) );
}
vcollisionevent_t m_Event = {};
JoltPhysicsCollisionData m_Data;
};
template < typename Data >
struct JoltPhysicsEventTracker
{
public:
template < typename... T >
void EmplaceBack( uint32 uThreadId, T&&... val)
{
m_Mask |= 1ull << uThreadId;
m_Events[ uThreadId ].emplace_back( std::forward< T >( val )... );
}
template < bool bClear, typename FuncType >
void ForEach( FuncType func )
{
for ( uint32 thread = m_Mask; thread; thread &= thread - 1 )
{
const uint32 i = tzcnt( thread );
for ( auto &event : m_Events[ i ] )
func( event );
if constexpr ( bClear )
m_Events[ i ].clear();
}
if constexpr ( bClear )
m_Mask = 0ull;
}
private:
static constexpr uint32 kMaxThreads = 64;
std::atomic< uint64_t > m_Mask = { 0ull };
std::vector< Data > m_Events[ kMaxThreads ];
};
// The maximum number of sent collision events to send per-frame.
// This is used to play stuff like sounds and physics fx.
// This is quite expensive to do so, we rate-limit this quite aggressively.
static constexpr uint32_t MaxCollisionEvents = 4;
std::atomic< uint32 > m_GlobalCollisionEventCount = { 0u };
JoltPhysicsEventTracker< JoltPhysicsCollisionEvent > m_CollisionEvents;
JoltPhysicsEventTracker< JoltPhysicsCollisionData > m_StartTouchEvents;
JoltPhysicsEventTracker< JoltPhysicsCollisionData > m_EndTouchEvents;
JoltPhysicsEventTracker< JoltPhysicsContactPair > m_EnterTriggerEvents;
JoltPhysicsEventTracker< JoltPhysicsContactPair > m_LeaveTriggerEvents;
// For the fluid events:
// Object1 = the fluid
// Object2 = the object
JoltPhysicsEventTracker< JoltPhysicsContactPair > m_FluidStartTouchEvents;
JoltPhysicsEventTracker< JoltPhysicsContactPair > m_FluidEndTouchEvents;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,289 @@
//=================================================================================================
//
// A physics object
//
//=================================================================================================
#pragma once
class IPredictedPhysicsObject;
class IJoltObjectDestroyedListener;
class JoltPhysicsShadowController;
class JoltPhysicsFluidController;
class JoltPhysicsEnvironment;
class JoltPhysicsObject;
#if defined( GAME_CSGO_OR_NEWER )
using IPhysicsObjectInterface = IPredictedPhysicsObject;
#else
using IPhysicsObjectInterface = IPhysicsObject;
#endif
class JoltPhysicsObject final : public IPhysicsObjectInterface
{
public:
JoltPhysicsObject( JPH::Body *pBody, JoltPhysicsEnvironment *pEnvironment, bool bStatic, int nMaterialIndex, const objectparams_t *pParams );
JoltPhysicsObject( JPH::Body *pBody, JoltPhysicsEnvironment *pEnvironment, void *pGameData, JPH::StateRecorder &recorder );
~JoltPhysicsObject() override;
bool IsStatic() const override;
bool IsAsleep() const override;
bool IsTrigger() const override;
bool IsFluid() const override;
bool IsHinged() const override;
bool IsCollisionEnabled() const override;
bool IsGravityEnabled() const override;
bool IsDragEnabled() const override;
bool IsMotionEnabled() const override;
bool IsMoveable() const override; // legacy: IsMotionEnabled() && !IsStatic()
bool IsAttachedToConstraint( bool bExternalOnly ) const override;
void EnableCollisions( bool enable ) override;
void EnableGravity( bool enable ) override;
void EnableDrag( bool enable ) override;
void EnableMotion( bool enable ) override;
void SetGameData( void *pGameData ) override;
void * GetGameData() const override;
void SetGameFlags( unsigned short userFlags ) override;
unsigned short GetGameFlags() const override;
void SetGameIndex( unsigned short gameIndex ) override;
unsigned short GetGameIndex() const override;
void SetCallbackFlags( unsigned short callbackflags ) override;
unsigned short GetCallbackFlags() const override;
void Wake() override;
void Sleep() override;
void RecheckCollisionFilter() override;
void RecheckContactPoints( bool bSearchForNewContacts ) override_portal2;
void RecheckContactPoints() override_not_portal2 { RecheckContactPoints( false ); }
void SetMass( float mass ) override;
float GetMass() const override;
float GetInvMass() const override;
Vector GetInertia() const override;
Vector GetInvInertia() const override;
void SetInertia( const Vector &inertia ) override;
void SetDamping( const float *speed, const float *rot ) override;
void GetDamping( float *speed, float *rot ) const override;
void SetDragCoefficient( float *pDrag, float *pAngularDrag ) override;
void SetBuoyancyRatio( float ratio ) override;
int GetMaterialIndex() const override;
void SetMaterialIndex( int materialIndex ) override;
unsigned int GetContents() const override;
void SetContents( unsigned int contents ) override;
float GetSphereRadius() const override;
void SetSphereRadius( float radius ) override_asw;
float GetEnergy() const override;
Vector GetMassCenterLocalSpace() const override;
void SetPosition( const Vector &worldPosition, const QAngle &angles, bool isTeleport ) override;
void SetPositionMatrix( const matrix3x4_t &matrix, bool isTeleport ) override;
void GetPosition( Vector *worldPosition, QAngle *angles ) const override;
void GetPositionMatrix( matrix3x4_t *positionMatrix ) const override;
void SetVelocity( const Vector *velocity, const AngularImpulse *angularVelocity ) override;
void SetVelocityInstantaneous( const Vector *velocity, const AngularImpulse *angularVelocity ) override;
void GetVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const override;
void AddVelocity( const Vector *velocity, const AngularImpulse *angularVelocity ) override;
void GetVelocityAtPoint( const Vector &worldPosition, Vector *pVelocity ) const override;
void GetImplicitVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const override;
void LocalToWorld( Vector *worldPosition, const Vector &localPosition ) const override;
void WorldToLocal( Vector *localPosition, const Vector &worldPosition ) const override;
void LocalToWorldVector( Vector *worldVector, const Vector &localVector ) const override;
void WorldToLocalVector( Vector *localVector, const Vector &worldVector ) const override;
void ApplyForceCenter( const Vector &forceVector ) override;
void ApplyForceOffset( const Vector &forceVector, const Vector &worldPosition ) override;
void ApplyTorqueCenter( const AngularImpulse &torque ) override;
void CalculateForceOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerForce, AngularImpulse *centerTorque ) const override;
void CalculateVelocityOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerVelocity, AngularImpulse *centerAngularVelocity ) const override;
float CalculateLinearDrag( const Vector &unitDirection ) const override;
float CalculateAngularDrag( const Vector &objectSpaceRotationAxis ) const override;
bool GetContactPoint( Vector *contactPoint, IPhysicsObject **contactObject ) const override;
void SetShadow( float maxSpeed, float maxAngularSpeed, bool allowPhysicsMovement, bool allowPhysicsRotation ) override;
void UpdateShadow( const Vector &targetPosition, const QAngle &targetAngles, bool tempDisableGravity, float timeOffset ) override;
int GetShadowPosition( Vector *position, QAngle *angles ) const override;
IPhysicsShadowController * GetShadowController() const override;
void RemoveShadowController() override;
float ComputeShadowControl( const hlshadowcontrol_params_t &params, float secondsToArrival, float dt ) override;
const CPhysCollide * GetCollide() const override;
const char * GetName() const override;
void BecomeTrigger() override;
void RemoveTrigger() override;
void BecomeHinged( int localAxis ) override;
void RemoveHinged() override;
IPhysicsFrictionSnapshot *CreateFrictionSnapshot() override;
void DestroyFrictionSnapshot( IPhysicsFrictionSnapshot *pSnapshot ) override;
void OutputDebugInfo() const override;
#if OBJECT_WELDING
void WeldToObject( IPhysicsObject *pParent ) override;
void RemoveWeld( IPhysicsObject *pOther ) override;
void RemoveAllWelds() override;
#endif
void SetUseAlternateGravity( bool bSet ) override_asw;
void SetCollisionHints( uint32 collisionHints ) override_asw;
uint32 GetCollisionHints() const override_asw;
IPredictedPhysicsObject * GetPredictedInterface() const override_csgo;
void SyncWith( IPhysicsObject *pOther ) override_csgo;
void SetErrorDelta_Position( const Vector& vPosition ) override_csgo {}
void SetErrorDelta_Velocity( const Vector& vVelocity ) override_csgo {}
public:
JoltPhysicsEnvironment *GetEnvironment() { return m_pEnvironment; }
JPH::BodyID GetBodyID() { return m_pBody->GetID(); }
JPH::Body *GetBody() { return m_pBody; }
void UpdateEnvironment( JoltPhysicsEnvironment *pEnvironment );
void AddDestroyedListener( IJoltObjectDestroyedListener *pListener );
void RemoveDestroyedListener( IJoltObjectDestroyedListener *pListener );
// Grabs the position, adds addPos and teleports the object
void AddToPosition( JPH::Vec3Arg addPos );
// Only sets the position, and nothing else.
void SetPosition( const Vector &worldPosition );
// Adds to the velocity (Source space)
void AddVelocity( const Vector &worldPosition );
Vector GetVelocity();
void CalculateBuoyancy();
float GetMaterialDensity() const;
float GetBuoyancyRatio() const;
float GetVolume() const { return m_flVolume; }
bool IsControlledByGame() const;
void AddCallbackFlags( uint16 flags ) { m_callbackFlags |= flags; }
void RemoveCallbackFlags( uint16 flags ) { m_callbackFlags &= ~flags; }
void SaveObjectState( JPH::StateRecorder &recorder );
void RestoreObjectState( JPH::StateRecorder &recorder );
unsigned short GetGameMaterial() const
{
return m_GameMaterial;
}
bool GetGameMaterialAllowsSounds() const
{
return m_GameMaterial != 'X';
}
JoltPhysicsFluidController *GetFluidController()
{
return m_pFluidController;
}
void SetFluidController( JoltPhysicsFluidController *pFluidController )
{
m_pFluidController = pFluidController;
}
// Fakes a linear velocity so we can have correct before/after velocity
// when going between PreCollision and PostCollision callbacks.
JPH::Vec3 FakeJoltLinearVelocity( JPH::Vec3Arg fakeVelocity )
{
if ( m_pBody->IsStatic() )
return JPH::Vec3::sZero();
JPH::Vec3 oldVel = m_pBody->GetLinearVelocity();
m_pBody->SetLinearVelocity( fakeVelocity );
return oldVel;
}
void RestoreJoltLinearVelocity( JPH::Vec3Arg realVelocity )
{
if ( m_pBody->IsStatic() )
return;
m_pBody->SetLinearVelocity( realVelocity );
}
private:
void UpdateMaterialProperties();
void UpdateLayer();
// Josh:
// Always put m_pGameData first. Some games that will
// remain un-named offset by the vtable to get to this
// instead of calling GetGameData().
void *m_pGameData = nullptr;
uint16 m_gameFlags = 0;
uint16 m_gameIndex = 0;
uint16 m_callbackFlags = CALLBACK_GLOBAL_COLLISION|CALLBACK_GLOBAL_FRICTION|CALLBACK_FLUID_TOUCH|CALLBACK_GLOBAL_TOUCH|CALLBACK_GLOBAL_COLLIDE_STATIC|CALLBACK_DO_FLUID_SIMULATION;
uint32 m_collisionHints = 0;
bool m_bStatic = false;
bool m_bPinned = false;
int m_materialIndex = 0;
uint m_contents = CONTENTS_SOLID;
// Need this as Jolt gets very unhappy about reading motion
// properties of static objects.
float m_flCachedMass = 0.0f;
float m_flCachedInvMass = 0.0f;
bool m_bCachedCollisionEnabled = true;
float m_flMaterialDensity = 1.0f; // Material density in Jolt space.
float m_flBuoyancyRatio = 0.0f;
float m_flVolume = 0.0f;
unsigned short m_GameMaterial = 0;
CUtlVector< IJoltObjectDestroyedListener * > m_destroyedListeners;
// Shadow variables
JoltPhysicsShadowController *m_pShadowController = nullptr;
JoltPhysicsFluidController *m_pFluidController = nullptr;
bool m_bShadowTemporarilyDisableGravity = false;
JPH::Body *m_pBody = nullptr; // Underlying Jolt body
JoltPhysicsEnvironment *m_pEnvironment = nullptr; // Physics environment this body belongs to
JPH::PhysicsSystem *m_pPhysicsSystem = nullptr; // Physics system this body belongs to
};
// Josh: This doesn't handle mass change and is kind of a hack and sliightly wrong.
// Would be nice to just specify spring constant directly in Jolt.
inline float GetInvEffectiveMass( JoltPhysicsObject *pObject0, JoltPhysicsObject *pObject1 )
{
return ( pObject0->IsStatic() ? 0.0f : pObject0->GetInvMass() ) + ( pObject1->IsStatic() ? 0.0f : pObject1->GetInvMass() );
}
inline float GetSpringFrequency( float flConstant, JoltPhysicsObject *pObject0, JoltPhysicsObject *pObject1 )
{
return sqrt( flConstant * GetInvEffectiveMass( pObject0, pObject1 ) ) / ( 2.0f * M_PI_F );
}

View File

@ -0,0 +1,96 @@
#include "cbase.h"
#include "vjolt_objectpairhash.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
static std::pair< void *, void * > CreateSortedPair( void *pObject0, void *pObject1 )
{
return std::make_pair(
pObject0 <= pObject1 ? pObject0 : pObject1,
pObject0 <= pObject1 ? pObject1 : pObject0 );
}
//-------------------------------------------------------------------------------------------------
JoltPhysicsObjectPairHash::JoltPhysicsObjectPairHash()
{
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsObjectPairHash::AddObjectPair( void *pObject0, void *pObject1 )
{
auto pair = CreateSortedPair( pObject0, pObject1 );
if ( IsObjectPairInHash( pObject0, pObject1 ) )
return;
m_PairHashes[ GetHashArrayIndex( PointerHasher{}( pair ) ) ].emplace( pair );
m_ObjectHashes[ GetHashArrayIndex( std::hash< void* >()( pObject0 ) ) ].emplace( pair );
m_ObjectHashes[ GetHashArrayIndex( std::hash< void* >()( pObject1 ) ) ].emplace( pair );
m_Objects.emplace( pObject0 );
m_Objects.emplace( pObject1 );
}
void JoltPhysicsObjectPairHash::RemoveObjectPair( void *pObject0, void *pObject1 )
{
auto pair = CreateSortedPair( pObject0, pObject1 );
if ( !IsObjectPairInHash( pObject0, pObject1 ) )
return;
m_PairHashes[ GetHashArrayIndex( PointerHasher{}( pair ) ) ].erase( pair );
m_ObjectHashes[ GetHashArrayIndex( std::hash< void* >()( pObject0 ) ) ].erase( pair );
m_ObjectHashes[ GetHashArrayIndex( std::hash< void* >()( pObject1 ) ) ].erase( pair );
m_Objects.erase( pObject0 );
m_Objects.erase( pObject1 );
}
bool JoltPhysicsObjectPairHash::IsObjectPairInHash( void *pObject0, void *pObject1 )
{
auto pair = CreateSortedPair( pObject0, pObject1 );
return m_PairHashes[ GetHashArrayIndex( PointerHasher{}( pair ) ) ].contains( pair );
}
void JoltPhysicsObjectPairHash::RemoveAllPairsForObject( void *pObject0 )
{
auto &objectHashes = m_ObjectHashes[ GetHashArrayIndex( std::hash< void* >()( pObject0 ) ) ];
for ( auto it = objectHashes.begin(); it != objectHashes.end(); )
{
auto pair = *it++;
RemoveObjectPair( pair.first, pair.second );
}
}
bool JoltPhysicsObjectPairHash::IsObjectInHash( void *pObject0 )
{
return m_Objects.contains( pObject0 );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsObjectPairHash::GetPairCountForObject( void *pObject0 )
{
return int( m_Objects.count( pObject0 ) );
}
int JoltPhysicsObjectPairHash::GetPairListForObject( void *pObject0, int nMaxCount, void **ppObjectList )
{
auto& objectHashes = m_ObjectHashes[GetHashArrayIndex( std::hash< void* >()( pObject0 ) )];
int nCount = 0;
for ( auto it = objectHashes.begin(); it != objectHashes.end() && nCount < nMaxCount; ++it, ++nCount )
{
auto pair = *it;
ppObjectList[ nCount ] = pair.second != pObject0 ? pair.second : pair.first;
}
return nCount;
}

View File

@ -0,0 +1,49 @@
#pragma once
class JoltPhysicsObjectPairHash : public IPhysicsObjectPairHash
{
public:
JoltPhysicsObjectPairHash();
void AddObjectPair( void *pObject0, void *pObject1 ) override;
void RemoveObjectPair( void *pObject0, void *pObject1 ) override;
bool IsObjectPairInHash( void *pObject0, void *pObject1 ) override;
void RemoveAllPairsForObject( void *pObject0 ) override;
bool IsObjectInHash( void *pObject0 ) override;
int GetPairCountForObject( void *pObject0 ) override;
int GetPairListForObject( void *pObject0, int nMaxCount, void **ppObjectList ) override;
private:
struct PointerHasher
{
template < typename T >
static void HashCombine( size_t& seed, const T& v )
{
std::hash< T > hasher;
seed ^= hasher( v ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );
}
size_t operator() ( const std::pair< void*, void* >& val ) const
{
size_t hash = 0;
HashCombine( hash, val.first );
HashCombine( hash, val.second );
return hash;
}
};
static constexpr size_t HashSize = 1024;
static constexpr size_t GetHashArrayIndex( size_t hash )
{
return hash & ( HashSize - 1u );
}
using HashEntries = std::unordered_set< std::pair< void*, void* >, PointerHasher >;
std::array< HashEntries, HashSize > m_PairHashes;
std::array< HashEntries, HashSize > m_ObjectHashes;
std::unordered_multiset< void* > m_Objects;
};

View File

@ -0,0 +1,496 @@

#include "cbase.h"
#include "vjolt_keyvalues_schema.h"
#include "vjolt_surfaceprops.h"
#include "vjolt_parse.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
class JoltPhysicsParseKV final : public IVPhysicsKeyParser
{
public:
JoltPhysicsParseKV( KeyValues *pKV );
~JoltPhysicsParseKV() override;
const char* GetCurrentBlockName() override;
bool Finished() override;
void ParseSolid( solid_t *pSolid, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void ParseFluid( fluid_t *pFluid, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void ParseRagdollConstraint( constraint_ragdollparams_t *pConstraint, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void ParseSurfaceTable( int *table, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void ParseCustom( void *pCustom, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void ParseVehicle( vehicleparams_t *pVehicle, IVPhysicsKeyHandler *unknownKeyHandler ) override;
void SkipBlock() override;
void ParseCollisionRules( ragdollcollisionrules_t *pRules, IVPhysicsKeyHandler *unknownKeyHandler ) override_asw;
void ParseRagdollAnimatedFriction( ragdollanimatedfriction_t *pFriction, IVPhysicsKeyHandler *unknownKeyHandler ) override_asw;
private:
void NextBlock();
KeyValues *m_pKV;
KeyValues *m_pCurrentBlock;
};
//-------------------------------------------------------------------------------------------------
struct JoltPhysicsCollisionRulesHelper
{
ragdollcollisionrules_t Rules;
IVPhysicsKeyHandler *pUnknownKeyHandler;
JoltPhysicsIntPair CollisionPair;
};
//-------------------------------------------------------------------------------------------------
static const JoltKVSchemaProp_t kSolidDescs[] =
{
{ "Index", KVSCHEMA_DESC( solid_t, index ), FillIntProp },
#ifdef GAME_ASW_OR_NEWER
{ "Contents", KVSCHEMA_DESC( solid_t, contents ), FillIntProp },
#endif
{ "Name", KVSCHEMA_DESC( solid_t, name ), FillStringProp },
{ "Parent", KVSCHEMA_DESC( solid_t, parent ), FillStringProp },
{ "Mass", KVSCHEMA_DESC( solid_t, params.mass ), FillFloatProp },
{ "SurfaceProp", KVSCHEMA_DESC( solid_t, surfaceprop ), FillStringProp },
{ "MassCenterOverride", KVSCHEMA_DESC( solid_t, massCenterOverride ), FillVectorProp,
[]( void *pBaseObject )
{
// Josh:
// If we ended up setting this, we need to update the pointer
// of the params to point to our Vector.
solid_t *pSolid = reinterpret_cast< solid_t * >( pBaseObject );
pSolid->params.massCenterOverride = &pSolid->massCenterOverride;
}
},
{ "Damping", KVSCHEMA_DESC( solid_t, params.damping ), FillFloatProp },
{ "RotDamping", KVSCHEMA_DESC( solid_t, params.rotdamping ), FillFloatProp },
{ "Drag", KVSCHEMA_DESC( solid_t, params.dragCoefficient ), FillFloatProp },
//{ "RollingDrag", KVSCHEMA_DESC( solid_t, params.rollingDrag ), FillFloatProp },
{ "Inertia", KVSCHEMA_DESC( solid_t, params.inertia ), FillFloatProp },
{ "RotInertiaLimit", KVSCHEMA_DESC( solid_t, params.rotInertiaLimit ), FillFloatProp },
{ "Volume", KVSCHEMA_DESC( solid_t, params.volume ), FillFloatProp },
};
static const JoltKVSchemaProp_t kFluidDescs[] =
{
{ "Index", KVSCHEMA_DESC( fluid_t, index ), FillIntProp },
{ "SurfaceProp", KVSCHEMA_DESC( fluid_t, surfaceprop ), FillStringProp },
{ "Damping", KVSCHEMA_DESC( fluid_t, params.damping ), FillFloatProp },
{ "SurfacePlane", KVSCHEMA_DESC( fluid_t, params.surfacePlane ), FillVector4DProp },
{ "CurrentVelocity", KVSCHEMA_DESC( fluid_t, params.currentVelocity ), FillVectorProp },
{ "Contents", KVSCHEMA_DESC( fluid_t, params.contents ), FillIntProp },
};
static const JoltKVSchemaProp_t kRagdollDescs[] =
{
{ "Parent", KVSCHEMA_DESC( constraint_ragdollparams_t, parentIndex ), FillIntProp },
{ "Child", KVSCHEMA_DESC( constraint_ragdollparams_t, childIndex ), FillIntProp },
{ "XMin", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[0].minRotation ), FillFloatProp },
{ "YMin", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[1].minRotation ), FillFloatProp },
{ "ZMin", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[2].minRotation ), FillFloatProp },
{ "XMax", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[0].maxRotation ), FillFloatProp },
{ "YMax", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[1].maxRotation ), FillFloatProp },
{ "ZMax", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[2].maxRotation ), FillFloatProp },
{ "XFriction", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[0].torque ), FillFloatProp,
[]( void *pBaseObject )
{
constraint_ragdollparams_t *pParams = reinterpret_cast< constraint_ragdollparams_t * >( pBaseObject );
pParams->axes[0].angularVelocity = 0;
},
},
{ "YFriction", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[1].torque ), FillFloatProp,
[]( void *pBaseObject )
{
constraint_ragdollparams_t *pParams = reinterpret_cast< constraint_ragdollparams_t * >( pBaseObject );
pParams->axes[1].angularVelocity = 0;
},
},
{ "ZFriction", KVSCHEMA_DESC( constraint_ragdollparams_t, axes[2].torque ), FillFloatProp,
[]( void *pBaseObject )
{
constraint_ragdollparams_t *pParams = reinterpret_cast< constraint_ragdollparams_t * >( pBaseObject );
pParams->axes[2].angularVelocity = 0;
},
},
};
static const JoltKVSchemaProp_t kVehicleAxleWheelDescs[] =
{
{ "Radius", KVSCHEMA_DESC( vehicle_wheelparams_t, radius ), FillFloatProp },
{ "Mass", KVSCHEMA_DESC( vehicle_wheelparams_t, mass ), FillFloatProp },
{ "Intertia", KVSCHEMA_DESC( vehicle_wheelparams_t, inertia ), FillFloatProp },
{ "Damping", KVSCHEMA_DESC( vehicle_wheelparams_t, damping ), FillFloatProp },
{ "RotDamping", KVSCHEMA_DESC( vehicle_wheelparams_t, rotdamping ), FillFloatProp },
{ "FrictionScale", KVSCHEMA_DESC( vehicle_wheelparams_t, frictionScale ), FillFloatProp },
{ "Material", KVSCHEMA_DESC( vehicle_wheelparams_t, materialIndex ), FillSurfaceProp },
{ "SkidMaterial", KVSCHEMA_DESC( vehicle_wheelparams_t, skidMaterialIndex ), FillSurfaceProp },
{ "BrakeMaterial", KVSCHEMA_DESC( vehicle_wheelparams_t, brakeMaterialIndex ), FillSurfaceProp },
};
static const JoltKVSchemaProp_t kVehicleAxleSuspensionDescs[] =
{
{ "SpringConstant", KVSCHEMA_DESC( vehicle_suspensionparams_t, springConstant ), FillFloatProp },
{ "SpringDamping", KVSCHEMA_DESC( vehicle_suspensionparams_t, springDamping ), FillFloatProp },
{ "StabilizerConstant", KVSCHEMA_DESC( vehicle_suspensionparams_t, stabilizerConstant ), FillFloatProp },
{ "SpringDampingCompression", KVSCHEMA_DESC( vehicle_suspensionparams_t, springDampingCompression ), FillFloatProp },
{ "MaxBodyForce", KVSCHEMA_DESC( vehicle_suspensionparams_t, maxBodyForce ), FillFloatProp },
};
static const JoltKVSchemaProp_t kVehicleAxleDescs[] =
{
{ "Wheel", KVSCHEMA_DESC( vehicle_axleparams_t, wheels ),
{
sizeof( vehicle_wheelparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_wheelparams_t *pWheelParams = reinterpret_cast< vehicle_wheelparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleAxleWheelDescs, ARRAYSIZE( kVehicleAxleWheelDescs ), pWheelParams );
}
}
},
{ "Suspension", KVSCHEMA_DESC( vehicle_axleparams_t, suspension ),
{
sizeof( vehicle_suspensionparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_suspensionparams_t *pSuspensionParams = reinterpret_cast< vehicle_suspensionparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleAxleSuspensionDescs, ARRAYSIZE( kVehicleAxleSuspensionDescs ), pSuspensionParams );
}
}
},
{ "Offset", KVSCHEMA_DESC( vehicle_axleparams_t, offset ), FillVectorProp },
{ "WheelOffset", KVSCHEMA_DESC( vehicle_axleparams_t, wheelOffset ), FillVectorProp },
{ "TorqueFactor", KVSCHEMA_DESC( vehicle_axleparams_t, torqueFactor ), FillFloatProp },
{ "BrakeFactor", KVSCHEMA_DESC( vehicle_axleparams_t, brakeFactor ), FillFloatProp },
};
static const JoltKVSchemaProp_t kVehicleBodyDescs[] =
{
{ "MassCenterOverride", KVSCHEMA_DESC( vehicle_bodyparams_t, massCenterOverride ), FillVectorProp },
{ "AddGravity", KVSCHEMA_DESC( vehicle_bodyparams_t, addGravity ), FillFloatProp },
{ "MaxAngularVelocity", KVSCHEMA_DESC( vehicle_bodyparams_t, maxAngularVelocity ), FillFloatProp },
{ "MassOverride", KVSCHEMA_DESC( vehicle_bodyparams_t, massOverride ), FillFloatProp },
{ "TiltForce", KVSCHEMA_DESC( vehicle_bodyparams_t, tiltForce ), FillFloatProp },
{ "TiltForceHeight", KVSCHEMA_DESC( vehicle_bodyparams_t, tiltForceHeight ), FillFloatProp },
{ "CounterTorqueFactor", KVSCHEMA_DESC( vehicle_bodyparams_t, counterTorqueFactor ), FillFloatProp },
{ "KeepUprightTorque", KVSCHEMA_DESC( vehicle_bodyparams_t, keepUprightTorque ), FillFloatProp },
};
static const JoltKVSchemaProp_t kVehicleEngineBoostDescs[] =
{
{ "Force", KVSCHEMA_DESC( vehicle_engineparams_t, boostForce ), FillFloatProp },
{ "Duration", KVSCHEMA_DESC( vehicle_engineparams_t, boostDuration ), FillFloatProp },
{ "Delay", KVSCHEMA_DESC( vehicle_engineparams_t, boostDelay ), FillFloatProp },
{ "MaxSpeed", KVSCHEMA_DESC( vehicle_engineparams_t, boostMaxSpeed ), FillFloatProp },
{ "TorqueBoost", KVSCHEMA_DESC( vehicle_engineparams_t, torqueBoost ), FillBoolProp },
};
static const JoltKVSchemaProp_t kVehicleEngineDescs[] =
{
{ "Boost", KVSCHEMA_DESC_NO_OFFSET( vehicle_engineparams_t ),
{
sizeof( vehicle_engineparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_engineparams_t *pEngineParams = reinterpret_cast< vehicle_engineparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleEngineBoostDescs, ARRAYSIZE( kVehicleEngineBoostDescs ), pEngineParams );
}
}
},
{ "Gear", KVSCHEMA_DESC_ARRAY( vehicle_engineparams_t, gearRatio, gearCount ), FillFloatProp },
{ "Horsepower", KVSCHEMA_DESC( vehicle_engineparams_t, horsepower ), FillFloatProp },
{ "MaxSpeed", KVSCHEMA_DESC( vehicle_engineparams_t, maxSpeed ), FillFloatProp },
{ "MaxReverseSpeed", KVSCHEMA_DESC( vehicle_engineparams_t, maxRevSpeed ), FillFloatProp },
{ "AxleRatio", KVSCHEMA_DESC( vehicle_engineparams_t, axleRatio ), FillFloatProp },
{ "MaxRPM", KVSCHEMA_DESC( vehicle_engineparams_t, maxRPM ), FillFloatProp },
{ "ThrottleTime", KVSCHEMA_DESC( vehicle_engineparams_t, throttleTime ), FillFloatProp },
{ "AutoTransmission", KVSCHEMA_DESC( vehicle_engineparams_t, isAutoTransmission ), FillBoolProp },
{ "ShiftUpRPM", KVSCHEMA_DESC( vehicle_engineparams_t, shiftUpRPM ), FillFloatProp },
{ "ShiftDownRPM", KVSCHEMA_DESC( vehicle_engineparams_t, shiftDownRPM ), FillFloatProp },
{ "AutobrakeSpeedGain", KVSCHEMA_DESC( vehicle_engineparams_t, autobrakeSpeedGain ), FillFloatProp },
{ "AutobrakeSpeedFactor", KVSCHEMA_DESC( vehicle_engineparams_t, autobrakeSpeedFactor ), FillFloatProp },
};
static const JoltKVSchemaProp_t kVehicleSteeringDescs[] =
{
{ "DegreesSlow", KVSCHEMA_DESC( vehicle_steeringparams_t, degreesSlow ), FillFloatProp },
{ "DegreesFast", KVSCHEMA_DESC( vehicle_steeringparams_t, degreesFast ), FillFloatProp },
{ "DegreesBoost", KVSCHEMA_DESC( vehicle_steeringparams_t, degreesBoost ), FillFloatProp },
{ "SlowCarSpeed", KVSCHEMA_DESC( vehicle_steeringparams_t, speedSlow ), FillFloatProp },
{ "FastCarSpeed", KVSCHEMA_DESC( vehicle_steeringparams_t, speedFast ), FillFloatProp },
{ "SlowSteeringRate", KVSCHEMA_DESC( vehicle_steeringparams_t, steeringRateSlow ), FillFloatProp },
{ "FastSteeringRate", KVSCHEMA_DESC( vehicle_steeringparams_t, steeringRateFast ), FillFloatProp },
{ "SteeringRestRateSlow", KVSCHEMA_DESC( vehicle_steeringparams_t, steeringRestRateSlow ), FillFloatProp },
{ "SteeringRestRateFast", KVSCHEMA_DESC( vehicle_steeringparams_t, steeringRestRateFast ), FillFloatProp },
{ "ThrottleSteeringRestRateFactor", KVSCHEMA_DESC( vehicle_steeringparams_t, throttleSteeringRestRateFactor ), FillFloatProp },
{ "BoostSteeringRestRateFactor", KVSCHEMA_DESC( vehicle_steeringparams_t, boostSteeringRestRateFactor ), FillFloatProp },
{ "BoostSteeringRateFactor", KVSCHEMA_DESC( vehicle_steeringparams_t, boostSteeringRateFactor ), FillFloatProp },
{ "SteeringExponent", KVSCHEMA_DESC( vehicle_steeringparams_t, steeringExponent ), FillFloatProp },
{ "TurnThrottleReduceSlow", KVSCHEMA_DESC( vehicle_steeringparams_t, turnThrottleReduceSlow ), FillFloatProp },
{ "TurnThrottleReduceFast", KVSCHEMA_DESC( vehicle_steeringparams_t, turnThrottleReduceFast ), FillFloatProp },
{ "BrakeSteeringRateFactor", KVSCHEMA_DESC( vehicle_steeringparams_t, brakeSteeringRateFactor ), FillFloatProp },
{ "PowerSlideAccel", KVSCHEMA_DESC( vehicle_steeringparams_t, powerSlideAccel ), FillFloatProp },
{ "SkidAllowed", KVSCHEMA_DESC( vehicle_steeringparams_t, isSkidAllowed ), FillBoolProp },
{ "DustCloud", KVSCHEMA_DESC( vehicle_steeringparams_t, dustCloud ), FillBoolProp },
};
static const JoltKVSchemaProp_t kVehicleDescs[] =
{
{ "Axle", KVSCHEMA_DESC_ARRAY( vehicleparams_t, axles, axleCount ),
{
sizeof( vehicle_axleparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_axleparams_t *pAxleParams = reinterpret_cast< vehicle_axleparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleAxleDescs, ARRAYSIZE( kVehicleAxleDescs ), pAxleParams );
}
}
},
{ "Body", KVSCHEMA_DESC( vehicleparams_t, body ),
{
sizeof( vehicle_bodyparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_bodyparams_t *pBodyParams = reinterpret_cast< vehicle_bodyparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleBodyDescs, ARRAYSIZE( kVehicleBodyDescs ), pBodyParams );
}
}
},
{ "Engine", KVSCHEMA_DESC( vehicleparams_t, engine ),
{
sizeof( vehicle_engineparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_engineparams_t *pEngineParams = reinterpret_cast< vehicle_engineparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleEngineDescs, ARRAYSIZE( kVehicleEngineDescs ), pEngineParams );
}
}
},
{ "Steering", KVSCHEMA_DESC( vehicleparams_t, steering ),
{
sizeof( vehicle_steeringparams_t ),
[]( KeyValues *pProp, void *pPtr, size_t size )
{
vehicle_steeringparams_t *pSteeringParams = reinterpret_cast< vehicle_steeringparams_t * >( pPtr );
ParseJoltKVSchema( pProp, kVehicleSteeringDescs, ARRAYSIZE( kVehicleSteeringDescs ), pSteeringParams );
}
}
},
{ "WheelsPerAxle", KVSCHEMA_DESC( vehicleparams_t, wheelsPerAxle ), FillIntProp },
};
static const JoltKVSchemaProp_t kCollisionRulesDescs[] =
{
{ "SelfCollisions", KVSCHEMA_DESC( JoltPhysicsCollisionRulesHelper, Rules.bSelfCollisions ), FillIntProp },
{ "CollisionPair", KVSCHEMA_DESC( JoltPhysicsCollisionRulesHelper, CollisionPair ), FillIntPairProp,
[]( void *pBaseObject )
{
// Josh:
// Now that it's been parsed, set it on the collision set.
JoltPhysicsCollisionRulesHelper* pHelper = reinterpret_cast<JoltPhysicsCollisionRulesHelper*>( pBaseObject );
if ( pHelper->Rules.bSelfCollisions )
pHelper->Rules.pCollisionSet->EnableCollisions( pHelper->CollisionPair.Index0, pHelper->CollisionPair.Index1 );
}
},
};
static const JoltKVSchemaProp_t kRagdollAnimatedFrictionDescs[] =
{
{ "AnimFrictionMin", KVSCHEMA_DESC( ragdollanimatedfriction_t, minFriction ), FillFloatProp },
{ "AnimFrictionMax", KVSCHEMA_DESC( ragdollanimatedfriction_t, maxFriction ), FillFloatProp },
{ "AnimFrictionTimeIn", KVSCHEMA_DESC( ragdollanimatedfriction_t, timeIn ), FillFloatProp },
{ "AnimFrictionTimeOut", KVSCHEMA_DESC( ragdollanimatedfriction_t, timeOut ), FillFloatProp },
{ "AnimFrictionTimeHold", KVSCHEMA_DESC( ragdollanimatedfriction_t, timeHold ), FillFloatProp },
};
//-------------------------------------------------------------------------------------------------
JoltPhysicsParseKV::JoltPhysicsParseKV( KeyValues *pKV )
: m_pKV( pKV )
, m_pCurrentBlock( m_pKV->GetFirstSubKey() )
{
}
JoltPhysicsParseKV::~JoltPhysicsParseKV()
{
}
//-------------------------------------------------------------------------------------------------
const char* JoltPhysicsParseKV::GetCurrentBlockName()
{
if ( !m_pCurrentBlock )
return nullptr;
return m_pCurrentBlock->GetName();
}
bool JoltPhysicsParseKV::Finished()
{
return !m_pCurrentBlock;
}
void JoltPhysicsParseKV::ParseSolid( solid_t *pSolid, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pSolid );
else
V_memset( pSolid, 0, sizeof( *pSolid ) );
ParseJoltKVSchema( m_pCurrentBlock, kSolidDescs, ARRAYSIZE( kSolidDescs ), pSolid, pSolid, unknownKeyHandler );
NextBlock();
}
void JoltPhysicsParseKV::ParseFluid( fluid_t *pFluid, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pFluid );
else
{
V_memset( pFluid, 0, sizeof( *pFluid ) );
V_strncpy( pFluid->surfaceprop, "water", sizeof( pFluid->surfaceprop ) );
}
ParseJoltKVSchema( m_pCurrentBlock, kFluidDescs, ARRAYSIZE( kFluidDescs ), pFluid, pFluid, unknownKeyHandler );
NextBlock();
}
void JoltPhysicsParseKV::ParseRagdollConstraint( constraint_ragdollparams_t *pConstraint, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pConstraint );
else
{
V_memset( pConstraint, 0, sizeof( *pConstraint ) );
pConstraint->childIndex = -1;
pConstraint->parentIndex = -1;
}
// Josh: The KV specifies clockwise rotations.
pConstraint->useClockwiseRotations = true;
ParseJoltKVSchema( m_pCurrentBlock, kRagdollDescs, ARRAYSIZE( kRagdollDescs ), pConstraint, pConstraint, unknownKeyHandler );
NextBlock();
}
void JoltPhysicsParseKV::ParseSurfaceTable( int *table, IVPhysicsKeyHandler *unknownKeyHandler )
{
for ( KeyValues* pProp = m_pCurrentBlock->GetFirstSubKey(); pProp != nullptr; pProp = pProp->GetNextKey() )
{
int nPropIdx = JoltPhysicsSurfaceProps::GetInstance().GetSurfaceIndex( pProp->GetName() );
int nTableIdx = pProp->GetInt();
if ( nTableIdx < 128 )
table[nTableIdx] = nPropIdx;
}
NextBlock();
}
void JoltPhysicsParseKV::ParseCustom( void *pCustom, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pCustom );
ParseJoltKVCustom( m_pCurrentBlock, pCustom, unknownKeyHandler );
NextBlock();
}
void JoltPhysicsParseKV::ParseVehicle( vehicleparams_t *pVehicle, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pVehicle );
else
V_memset( pVehicle, 0, sizeof( *pVehicle ) );
ParseJoltKVSchema( m_pCurrentBlock, kVehicleDescs, ARRAYSIZE( kVehicleDescs ), pVehicle, pVehicle, unknownKeyHandler);
NextBlock();
}
void JoltPhysicsParseKV::SkipBlock()
{
NextBlock();
}
void JoltPhysicsParseKV::ParseCollisionRules( ragdollcollisionrules_t *pRules, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pRules );
JoltPhysicsCollisionRulesHelper helper =
{
.Rules = pRules ? *pRules : ragdollcollisionrules_t{},
.pUnknownKeyHandler = unknownKeyHandler,
};
ParseJoltKVSchema( m_pCurrentBlock, kCollisionRulesDescs, ARRAYSIZE( kCollisionRulesDescs ), &helper, pRules, unknownKeyHandler );
if ( pRules )
*pRules = helper.Rules;
NextBlock();
}
void JoltPhysicsParseKV::ParseRagdollAnimatedFriction( ragdollanimatedfriction_t *pFriction, IVPhysicsKeyHandler *unknownKeyHandler )
{
if ( unknownKeyHandler )
unknownKeyHandler->SetDefaults( pFriction );
else
V_memset( pFriction, 0, sizeof( *pFriction ) );
ParseJoltKVSchema( m_pCurrentBlock, kRagdollAnimatedFrictionDescs, ARRAYSIZE( kRagdollAnimatedFrictionDescs ), pFriction, pFriction, unknownKeyHandler );
NextBlock();
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsParseKV::NextBlock()
{
if ( m_pCurrentBlock )
m_pCurrentBlock = m_pCurrentBlock->GetNextKey();
}
//-------------------------------------------------------------------------------------------------
IVPhysicsKeyParser *CreateVPhysicsKeyParser( const char *pKeyData, bool bIsPacked )
{
VJoltAssertMsg( !bIsPacked, "Packed VPhysics KV not supported. You should not get here anyway as we do not emit it." );
if ( bIsPacked )
return nullptr;
KeyValues *pszKV = HeaderlessKVBufferToKeyValues( pKeyData, "VPhysicsKeyParse" );
if ( !pszKV )
return nullptr;
return new JoltPhysicsParseKV( pszKV );
}
void DestroyVPhysicsKeyParser( IVPhysicsKeyParser *pParser )
{
JoltPhysicsParseKV *pJoltParser = static_cast< JoltPhysicsParseKV * >( pParser );
delete pJoltParser;
}

View File

@ -0,0 +1,9 @@
#pragma once
//-------------------------------------------------------------------------------------------------
class IVPhysicsKeyParser;
IVPhysicsKeyParser* CreateVPhysicsKeyParser( const char* pKeyData, bool bIsPacked );
void DestroyVPhysicsKeyParser( IVPhysicsKeyParser* pParser );

View File

@ -0,0 +1,96 @@
#include "cbase.h"
#include "vjolt_querymodel.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
JoltCollisionQuery::JoltCollisionQuery( JPH::Shape *pShape )
: m_pShape ( pShape )
{
}
//-------------------------------------------------------------------------------------------------
int JoltCollisionQuery::ConvexCount()
{
const JPH::StaticCompoundShape *pCompoundShape = GetCompoundShape( m_pShape );
if ( pCompoundShape )
return int( pCompoundShape->GetNumSubShapes() );
// If we aren't a compound shape, then we have one.
return 1;
}
int JoltCollisionQuery::TriangleCount( int convexIndex )
{
return ActOnSubShape<int, JPH::Shape>( m_pShape, convexIndex, []( const JPH::Shape* pShape ) -> int
{
JPH::Shape::Stats stats = pShape->GetStats();
return stats.mNumTriangles;
} );
}
//-------------------------------------------------------------------------------------------------
unsigned int JoltCollisionQuery::GetGameData( int convexIndex )
{
return static_cast< unsigned int >( m_pShape->GetUserData() );
}
//-------------------------------------------------------------------------------------------------
void JoltCollisionQuery::GetTriangleVerts( int convexIndex, int triangleIndex, Vector *verts )
{
ActOnSubShape<int, JPH::Shape>( m_pShape, convexIndex, [&]( const JPH::Shape* pShape ) -> int
{
static constexpr int kRequestedTriangles = 2048;
JPH::Shape::GetTrianglesContext ctx;
pShape->GetTrianglesStart( ctx, JPH::AABox::sBiggest(), JPH::Vec3::sZero(), JPH::Quat::sIdentity(), JPH::Vec3( 1.0f, 1.0f, 1.0f ) );
int i = 0;
for ( ;; )
{
JPH::Float3 vertices[ kRequestedTriangles * 3 ];
int count = pShape->GetTrianglesNext( ctx, kRequestedTriangles, vertices, nullptr /* materials */ );
if ( count == 0 )
break;
if ( triangleIndex >= i && triangleIndex < i + count )
{
verts[ 0 ] = JoltToSource::Distance( vertices[ ( triangleIndex % kRequestedTriangles ) * 3 + 0 ] );
verts[ 1 ] = JoltToSource::Distance( vertices[ ( triangleIndex % kRequestedTriangles ) * 3 + 1 ] );
verts[ 2 ] = JoltToSource::Distance( vertices[ ( triangleIndex % kRequestedTriangles ) * 3 + 2 ] );
return 0;
}
i += kRequestedTriangles;
}
return 1;
} );
}
void JoltCollisionQuery::SetTriangleVerts( int convexIndex, int triangleIndex, const Vector *verts )
{
Log_Stub( LOG_VJolt );
}
//-------------------------------------------------------------------------------------------------
int JoltCollisionQuery::GetTriangleMaterialIndex( int convexIndex, int triangleIndex )
{
Log_Stub( LOG_VJolt );
return 0;
}
void JoltCollisionQuery::SetTriangleMaterialIndex( int convexIndex, int triangleIndex, int index7bits )
{
Log_Stub( LOG_VJolt );
}
//-------------------------------------------------------------------------------------------------

View File

@ -0,0 +1,23 @@
#pragma once
class JoltCollisionQuery final : public ICollisionQuery
{
public:
JoltCollisionQuery( JPH::Shape *pShape );
int ConvexCount() override;
int TriangleCount( int convexIndex ) override;
unsigned int GetGameData( int convexIndex ) override;
void GetTriangleVerts( int convexIndex, int triangleIndex, Vector *verts ) override;
void SetTriangleVerts( int convexIndex, int triangleIndex, const Vector *verts ) override;
int GetTriangleMaterialIndex( int convexIndex, int triangleIndex ) override;
void SetTriangleMaterialIndex( int convexIndex, int triangleIndex, int index7bits ) override;
private:
JPH::Ref<JPH::Shape> m_pShape;
};

View File

@ -0,0 +1,35 @@
#pragma once
class JoltStateRecorderFile final : public JPH::StateRecorder
{
public:
JoltStateRecorderFile( const char* pszPath, bool input )
: m_Stream( pszPath, std::ios::binary | ( input ? std::ios::in : std::ios::out ) )
{
}
JoltStateRecorderFile( JoltStateRecorderFile &&other )
: StateRecorder( other )
, m_Stream( move( other.m_Stream ) )
{
}
void WriteBytes( const void* inData, size_t inNumBytes ) override
{
m_Stream.write( reinterpret_cast< const char * >( inData ), inNumBytes );
}
void ReadBytes( void* outData, size_t inNumBytes ) override
{
m_Stream.read( reinterpret_cast< char * >( outData ), inNumBytes );
}
bool IsEOF() const override { return m_Stream.eof(); }
bool IsFailed() const override { return m_Stream.fail(); }
bool IsValid() const { return !m_Stream.bad(); }
private:
std::fstream m_Stream;
};

View File

@ -0,0 +1,272 @@
#include "cbase.h"
#include "vjolt_interface.h"
#include "vjolt_keyvalues_schema.h"
#include "vjolt_surfaceprops.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
JoltPhysicsSurfaceProps JoltPhysicsSurfaceProps::s_PhysicsSurfaceProps;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( JoltPhysicsSurfaceProps, IPhysicsSurfaceProps, VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, JoltPhysicsSurfaceProps::GetInstance() );
//-------------------------------------------------------------------------------------------------
static const JoltKVSchemaProp_t kSurfacePropDescs[] =
{
// Base Property
{ "Base", KVSCHEMA_DESC( JoltSurfaceProp, data ), FillBaseProp },
// Physics Properties
{ "Friction", KVSCHEMA_DESC( JoltSurfaceProp, data.physics.friction ), FillFloatProp },
{ "Elasticity", KVSCHEMA_DESC( JoltSurfaceProp, data.physics.elasticity ), FillFloatProp },
{ "Density", KVSCHEMA_DESC( JoltSurfaceProp, data.physics.density ), FillFloatProp },
{ "Thickness", KVSCHEMA_DESC( JoltSurfaceProp, data.physics.thickness ), FillFloatProp },
{ "Dampening", KVSCHEMA_DESC( JoltSurfaceProp, data.physics.dampening ), FillFloatProp },
// Audio Properties
{ "AudioReflectivity", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.reflectivity ), FillFloatProp },
{ "AudioHardnessFactor", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.hardnessFactor ), FillFloatProp },
{ "AudioRoughnessFactor", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.roughnessFactor ), FillFloatProp },
{ "ScrapeRoughThreshold", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.roughThreshold ), FillFloatProp },
{ "ImpactHardThreshold", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.hardThreshold ), FillFloatProp },
{ "AudioHardMinVelocity", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.hardVelocityThreshold ), FillFloatProp },
#ifdef GAME_CSGO_OR_NEWER
{ "HighPitchOcclusion", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.highPitchOcclusion ), FillFloatProp },
{ "MidPitchOcclusion", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.midPitchOcclusion ), FillFloatProp },
{ "LowPitchOcclusion", KVSCHEMA_DESC( JoltSurfaceProp, data.audio.lowPitchOcclusion ), FillFloatProp },
#endif
// Sound Properties
#ifdef GAME_ASW_OR_NEWER
{ "StepLeft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.walkStepLeft ), FillSoundProp },
{ "StepLeft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.runStepLeft ), FillSoundProp },
{ "StepRight", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.walkStepRight ), FillSoundProp },
{ "StepRight", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.runStepRight ), FillSoundProp },
{ "WalkLeft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.walkStepLeft ), FillSoundProp },
{ "WalkRight", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.walkStepRight ), FillSoundProp },
{ "RunLeft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.runStepLeft ), FillSoundProp },
{ "RunRight", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.runStepRight ), FillSoundProp },
#else
{ "StepLeft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.stepleft ), FillSoundProp },
{ "StepRight", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.stepright ), FillSoundProp },
#endif
{ "ImpactSoft", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.impactSoft ), FillSoundProp },
{ "ImpactHard", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.impactHard ), FillSoundProp },
{ "ScrapeSmooth", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.scrapeSmooth ), FillSoundProp },
{ "ScrapeRough", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.scrapeRough ), FillSoundProp },
{ "BulletImpact", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.bulletImpact ), FillSoundProp },
{ "Rolling", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.rolling ), FillSoundProp },
{ "Break", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.breakSound ), FillSoundProp },
{ "Strain", KVSCHEMA_DESC( JoltSurfaceProp, data.sounds.strainSound ), FillSoundProp },
// Game Properties
{ "MaxSpeedFactor", KVSCHEMA_DESC( JoltSurfaceProp, data.game.maxSpeedFactor ), FillFloatProp },
{ "JumpFactor", KVSCHEMA_DESC( JoltSurfaceProp, data.game.jumpFactor ), FillFloatProp },
#ifdef GAME_CSGO_OR_NEWER
{ "PenetrationModifier", KVSCHEMA_DESC( JoltSurfaceProp, data.game.penetrationModifier ), FillFloatProp },
{ "DamageModifier", KVSCHEMA_DESC( JoltSurfaceProp, data.game.damageModifier ), FillFloatProp },
#endif
{ "GameMaterial", KVSCHEMA_DESC( JoltSurfaceProp, data.game.material ), FillGameMaterialProp },
{ "Climbable", KVSCHEMA_DESC( JoltSurfaceProp, data.game.climbable ), FillUnsignedCharProp },
#ifdef GAME_CSGO_OR_NEWER
{ "HideTargetID", KVSCHEMA_DESC( JoltSurfaceProp, data.game.hidetargetid ), FillBoolProp },
{ "DamageLossPercentPerPenetration", KVSCHEMA_DESC( JoltSurfaceProp, data.game.damageLossPercentPerPenetration ), FillFloatProp },
#endif
};
//-------------------------------------------------------------------------------------------------
JoltPhysicsSurfaceProps::JoltPhysicsSurfaceProps()
{
JoltSurfaceProp prop = {};
prop.data.physics.friction = 0.8f;
prop.data.physics.elasticity = 0.25f;
prop.data.physics.density = 2000.0f;
prop.data.physics.thickness = 0.0f;
prop.data.physics.dampening = 0.0f;
m_SurfaceProps[ "default" ] = prop;
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsSurfaceProps::ParseSurfaceData( const char *pFilename, const char *pTextfile )
{
KeyValues::AutoDelete kv( SurfacePropsToKeyValues( pTextfile ) );
for ( KeyValues* pSurface = kv->GetFirstSubKey(); pSurface != nullptr; pSurface = pSurface->GetNextKey() )
{
const char *pSurfaceName = pSurface->GetName();
JoltSurfaceProp values = {};
// Try to find it if we already have a material with this name,
// so we can update the values on it, otherwise take the base material.
UtlSymId_t id = m_SurfaceProps.Find( pSurfaceName );
if ( id != m_SurfaceProps.InvalidIndex() )
values = m_SurfaceProps[ id ];
else
values = m_SurfaceProps[ BaseMaterialIdx ];
ParseJoltKVSchema( pSurface, kSurfacePropDescs, uint( std::size( kSurfacePropDescs ) ), &values );
// If we don't have this already, add it,
// otherwise update the values.
if ( id == m_SurfaceProps.InvalidIndex() )
m_SurfaceProps[ pSurfaceName ] = values;
else
m_SurfaceProps[ id ] = values;
}
return m_SurfaceProps.GetNumStrings();
}
int JoltPhysicsSurfaceProps::SurfacePropCount( void ) const
{
return int ( m_SurfaceProps.GetNumStrings() );
}
//-------------------------------------------------------------------------------------------------
int JoltPhysicsSurfaceProps::GetSurfaceIndex( const char *pSurfacePropName ) const
{
// TODO(Josh): Something about reserved props for $MATERIAL_INDEX_SHADOW
UtlSymId_t nIndex = m_SurfaceProps.Find( pSurfacePropName );
if ( nIndex != m_SurfaceProps.InvalidIndex() )
return int( nIndex );
return -1;
}
void JoltPhysicsSurfaceProps::GetPhysicsProperties( int surfaceDataIndex, float *density, float *thickness, float *friction, float *elasticity ) const
{
const UtlSymId_t id = surfaceDataIndex >= 0 && surfaceDataIndex < int( m_SurfaceProps.GetNumStrings() )
? UtlSymId_t( surfaceDataIndex )
: BaseMaterialIdx;
const JoltSurfaceProp& prop = m_SurfaceProps[ id ];
if ( density ) *density = prop.data.physics.density;
if ( thickness ) *thickness = prop.data.physics.thickness;
if ( friction ) *friction = prop.data.physics.friction;
if ( elasticity ) *elasticity = prop.data.physics.elasticity;
}
//-------------------------------------------------------------------------------------------------
surfacedata_t *JoltPhysicsSurfaceProps::GetSurfaceData( int surfaceDataIndex )
{
const UtlSymId_t id = surfaceDataIndex >= 0 && surfaceDataIndex < int( m_SurfaceProps.GetNumStrings() )
? UtlSymId_t( surfaceDataIndex )
: BaseMaterialIdx;
JoltSurfaceProp& prop = m_SurfaceProps[ id ];
return &prop.data;
}
const char *JoltPhysicsSurfaceProps::GetString( unsigned short stringTableIndex ) const
{
return m_SoundStrings.String( stringTableIndex );
}
//-------------------------------------------------------------------------------------------------
const char *JoltPhysicsSurfaceProps::GetPropName( int surfaceDataIndex ) const
{
if ( surfaceDataIndex < 0 || surfaceDataIndex >= int ( m_SurfaceProps.GetNumStrings() ) )
return nullptr;
return m_SurfaceProps.String( surfaceDataIndex );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsSurfaceProps::SetWorldMaterialIndexTable( int *pMapArray, int mapSize )
{
Log_Stub( LOG_VJolt );
}
//-------------------------------------------------------------------------------------------------
void JoltPhysicsSurfaceProps::GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const
{
if ( !pParamsOut )
return;
const UtlSymId_t id = surfaceDataIndex >= 0 && surfaceDataIndex < int( m_SurfaceProps.GetNumStrings() )
? UtlSymId_t( surfaceDataIndex )
: BaseMaterialIdx;
const JoltSurfaceProp& prop = m_SurfaceProps[ id ];
*pParamsOut = prop.data.physics;
}
//-------------------------------------------------------------------------------------------------
ISaveRestoreOps *JoltPhysicsSurfaceProps::GetMaterialIndexDataOps() const
{
return &JoltPhysicsMaterialIndexSaveOps::GetInstance();
}
//-------------------------------------------------------------------------------------------------
unsigned short JoltPhysicsSurfaceProps::RegisterSound( const char *pName )
{
return m_SoundStrings.AddString( pName );
}
//-------------------------------------------------------------------------------------------------
KeyValues *JoltPhysicsSurfaceProps::SurfacePropsToKeyValues( const char *pszBuffer )
{
return HeaderlessKVBufferToKeyValues( pszBuffer, "PhysProps" );
}
//-------------------------------------------------------------------------------------------------
JoltPhysicsMaterialIndexSaveOps JoltPhysicsMaterialIndexSaveOps::s_Instance;
void JoltPhysicsMaterialIndexSaveOps::Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
{
int *pMaterialIdx = reinterpret_cast<int*>( fieldInfo.pField );
const char *pMaterialName = JoltPhysicsSurfaceProps::GetInstance().GetPropName( *pMaterialIdx );
if ( !pMaterialName )
pMaterialName = JoltPhysicsSurfaceProps::GetInstance().GetPropName( 0 );
int nMaterialNameLength = V_strlen( pMaterialName ) + 1;
pSave->WriteInt( &nMaterialNameLength );
pSave->WriteString( pMaterialName );
}
void JoltPhysicsMaterialIndexSaveOps::Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
{
int nMaterialNameLength = pRestore->ReadInt();
char szMaterialName[ 2048 ];
pRestore->ReadString( szMaterialName, sizeof( szMaterialName ), nMaterialNameLength );
int *pMaterialIdx = reinterpret_cast<int*>( fieldInfo.pField );
*pMaterialIdx = Max( JoltPhysicsSurfaceProps::GetInstance().GetSurfaceIndex( szMaterialName ), 0 );
}
bool JoltPhysicsMaterialIndexSaveOps::IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
{
int *pMaterialIdx = reinterpret_cast<int*>( fieldInfo.pField );
return !*pMaterialIdx;
}
void JoltPhysicsMaterialIndexSaveOps::MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
{
int* pMaterialIdx = reinterpret_cast<int*>( fieldInfo.pField );
*pMaterialIdx = 0;
}

View File

@ -0,0 +1,60 @@
#pragma once
struct JoltSurfaceProp
{
surfacedata_t data;
};
class JoltPhysicsMaterialIndexSaveOps : public CDefSaveRestoreOps
{
public:
void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) override;
void Restore( const SaveRestoreFieldInfo_t& fieldInfo, IRestore* pRestore ) override;
bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) override;
void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) override;
static JoltPhysicsMaterialIndexSaveOps& GetInstance() { return s_Instance; }
private:
static JoltPhysicsMaterialIndexSaveOps s_Instance;
};
class JoltPhysicsSurfaceProps final : public IPhysicsSurfaceProps
{
public:
JoltPhysicsSurfaceProps();
int ParseSurfaceData( const char *pFilename, const char *pTextfile ) override;
int SurfacePropCount( void ) const override;
int GetSurfaceIndex( const char *pSurfacePropName ) const override;
void GetPhysicsProperties( int surfaceDataIndex, float *density, float *thickness, float *friction, float *elasticity ) const override;
surfacedata_t *GetSurfaceData( int surfaceDataIndex ) override;
const char *GetString( unsigned short stringTableIndex ) const override;
const char *GetPropName( int surfaceDataIndex ) const override;
void SetWorldMaterialIndexTable( int *pMapArray, int mapSize ) override;
void GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const override;
ISaveRestoreOps *GetMaterialIndexDataOps() const override_portal2;
public:
static JoltPhysicsSurfaceProps& GetInstance() { return s_PhysicsSurfaceProps; }
unsigned short RegisterSound( const char *pName );
private:
static JoltPhysicsSurfaceProps s_PhysicsSurfaceProps;
CUtlStringMap< JoltSurfaceProp > m_SurfaceProps;
CUtlSymbolTable m_SoundStrings;
static constexpr UtlSymId_t BaseMaterialIdx = UtlSymId_t( 0 );
KeyValues *SurfacePropsToKeyValues( const char *pszBuffer );
};

364
vphysics_jolt/vjolt_util.h Normal file
View File

@ -0,0 +1,364 @@
//=================================================================================================
//
// Source / Jolt utilities
//
//=================================================================================================
#pragma once
inline constexpr float InchesToMetres = 0.0254f;
inline constexpr float MetresToInches = 1.0f / 0.0254f;
// TODO! Remove loam_expr -> constexpr when mathlib stuff is sorted.
#define loam_expr inline
loam_expr Vector VectorHalfExtent( Vector mins, Vector maxs )
{
return 0.5f * ( maxs - mins );
}
loam_expr Quaternion ToQuaternion( const QAngle& angles )
{
Quaternion result;
AngleQuaternion( angles, result );
return result;
}
loam_expr QAngle ToQAngle( const Quaternion &q )
{
QAngle result;
QuaternionAngles( q, result );
return result;
}
loam_expr QAngle ToQAngle( const matrix3x4_t& m )
{
QAngle result;
MatrixAngles( m, result );
return result;
}
loam_expr Vector Abs( const Vector &v )
{
Vector result;
VectorAbs( v, result );
return result;
}
loam_expr Vector Rotate( const Vector &vector, const Quaternion &angle )
{
Vector out;
VectorRotate( vector, angle, out );
return out;
}
loam_expr Vector Rotate( const Vector& vector, const matrix3x4_t &matrix )
{
Vector out;
VectorRotate( vector, matrix, out );
return out;
}
template < typename T >
constexpr T Cube( T x )
{
return x * x * x;
}
namespace MatrixAxis
{
enum JoltMatrixAxes
{
Forward = 0,
Left = 1,
Up = 2,
X = 0,
Y = 1,
Z = 2,
Origin = 3,
Projective = 3,
};
}
using JoltMatrixAxes = MatrixAxis::JoltMatrixAxes;
loam_expr Vector GetColumn( const matrix3x4_t& m, JoltMatrixAxes axis )
{
Vector value;
MatrixGetColumn( m, (int)axis, value );
return value;
}
//
// SourceToJolt:
//
// Type conversions from:
// - JPH::Vec3 -> Vector,
// - JPH::Float3 -> Vector,
// - JPH::Quat-> Quaternion,
// - JPH::Quat -> QAngle,
// Unit conversions from:
// - metres -> inches (distance, area, volume, energy)
// - radians -> degrees
namespace JoltToSource
{
inline constexpr float Factor = MetresToInches;
inline constexpr float InvFactor = InchesToMetres;
// This is for doing direct type conversions
// ie. normals, directions, scale factors, coefficients and
// certain dimensionless quantities.
loam_expr float Unitless( float value ) { return value; }
loam_expr Vector Unitless( JPH::Vec3Arg value ) { return Vector( value[0], value[1], value[2] ); }
loam_expr Vector Unitless( JPH::Float3 value ) { return Vector( value[0], value[1], value[2] ); }
// This is used for any unit that has a singular metre factor
// ie. distance (m), velocity (m/s), acceleration (m/s^2), force (kg m/s^2).
loam_expr float Distance( float value ) { return value * Factor; }
loam_expr Vector Distance( JPH::Vec3Arg value ) { return Vector( Distance( value[0] ), Distance( value[1] ), Distance( value[2] ) ); }
loam_expr Vector Distance( JPH::Float3 value ) { return Vector( Distance( value[0] ), Distance( value[1] ), Distance( value[2] ) ); }
// m^2 -> in^2
loam_expr float Area( float value ) { return value * Factor * Factor; }
// m^3 -> in^3
loam_expr float Volume( float value ) { return value * Factor * Factor * Factor; }
// These handle converting Quaternions/QAngles -> JPH::Quat,
// which is a direct type passthrough.
// They also handles converting scalar angle units, which is rad -> deg.
loam_expr Quaternion Quat( JPH::QuatArg value ) { return Quaternion( value.GetX(), value.GetY(), value.GetZ(), value.GetW() ); }
loam_expr float Angle( float value ) { return RAD2DEG( value ); }
loam_expr QAngle Angle( JPH::QuatArg value ) { return ToQAngle( Quat( value ) ); }
loam_expr float Energy( float value ) { return value / ( InvFactor * InvFactor ); }
// Converts types and handles the angle (rad -> deg) unit conversion.
loam_expr float AngularImpulse( float value ) { return Angle( value ); }
loam_expr Vector AngularImpulse( JPH::Vec3Arg value ) { return Vector( AngularImpulse( value[0] ), AngularImpulse( value[1] ), AngularImpulse( value[2] ) ); }
// Misc. AABB helpers.
loam_expr Vector AABBCenter( const JPH::AABox &aabox ) { return Distance( aabox.mMin + aabox.GetExtent() ); }
loam_expr Vector AABBHalfExtent( const JPH::AABox &aabox ) { return Distance( aabox.GetExtent() ); }
loam_expr void AABBBounds( const JPH::AABox &aabox, Vector &outMins, Vector &outMaxs )
{
outMins = AABBCenter( aabox ) - AABBHalfExtent( aabox );
outMaxs = AABBCenter( aabox ) + AABBHalfExtent( aabox );
}
loam_expr matrix3x4_t Matrix( JPH::Mat44Arg matrix )
{
return matrix3x4_t
{
Vector( matrix.GetAxisX().GetX(), matrix.GetAxisX().GetY(), matrix.GetAxisX().GetZ() ),
Vector( matrix.GetAxisY().GetX(), matrix.GetAxisY().GetY(), matrix.GetAxisY().GetZ() ),
Vector( matrix.GetAxisZ().GetX(), matrix.GetAxisZ().GetY(), matrix.GetAxisZ().GetZ() ),
Distance( matrix.GetTranslation() ),
};
}
loam_expr ::Color Color( JPH::ColorArg color )
{
return ::Color( color.r, color.g, color.b, color.a );
}
}
//
// SourceToJolt:
//
// Type conversions from:
// - Vector -> JPH::Vec3,
// - Vector -> JPH::Float3,
// - Quaternion -> JPH::Quat,
// - QAngle -> JPH::Quat,
// Unit conversions from:
// - inches -> metres (distance, area, volume, energy)
// - degrees -> radians
namespace SourceToJolt
{
inline constexpr float Factor = InchesToMetres;
inline constexpr float InvFactor = MetresToInches;
// This is for doing direct type conversions
// ie. normals, directions, scale factors, coefficients and
// certain dimensionless quantities.
loam_expr float Unitless( float value ) { return value; }
loam_expr JPH::Vec3 Unitless( Vector value ) { return JPH::Vec3( value[0], value[1], value[2] ); }
loam_expr JPH::Float3 UnitlessFloat3( Vector value ) { return JPH::Float3( value[0], value[1], value[2] ); }
// This is used for any unit that has a singular Source Unit(tm) "inch" factor
// ie. distance (in), velocity (in/s), acceleration (in/s^2), force (kg in/s^2).
loam_expr float Distance( float value ) { return value * Factor; }
loam_expr JPH::Vec3 Distance( Vector value ) { return JPH::Vec3( Distance( value[0] ), Distance( value[1] ), Distance( value[2] ) ); }
loam_expr JPH::Float3 DistanceFloat3( Vector value ) { return JPH::Float3( Distance( value[0] ), Distance( value[1] ), Distance( value[2] ) ); }
// in^2 -> m^2
loam_expr float Area( float value ) { return value * Factor * Factor; }
// in^3 -> m^3
loam_expr float Volume( float value ) { return value * Factor * Factor * Factor; }
// These handle converting JPH::Quat -> Quaternions/QAngles,
// which is a direct type passthrough.
// They also handles converting scalar angle units, which is deg -> rad.
loam_expr JPH::Quat Quat( Quaternion value ) { return JPH::Quat( value.x, value.y, value.z, value.w ); }
loam_expr float Angle( float value ) { return DEG2RAD( value ); }
loam_expr JPH::Quat Angle( QAngle value ) { return Quat( ToQuaternion( value ) ); }
loam_expr float Energy( float value ) { return value / ( InvFactor * InvFactor ); }
// Converts types and handles the angle (deg -> rad) unit conversion.
loam_expr float AngularImpulse( float value ) { return Angle( value ); }
loam_expr JPH::Vec3 AngularImpulse( Vector value ) { return JPH::Vec3( AngularImpulse( value[0] ), AngularImpulse( value[1] ), AngularImpulse( value[2] ) ); }
// Misc. AABB helpers.
loam_expr JPH::Vec3 AABBCenter( Vector mins, Vector maxs ) { return Distance( mins + VectorHalfExtent( mins, maxs ) ); }
loam_expr JPH::Vec3 AABBHalfExtent( Vector mins, Vector maxs ) { return Distance( VectorHalfExtent( mins, maxs ) ); }
loam_expr JPH::AABox AABBBounds( Vector mins, Vector maxs )
{
return JPH::AABox
{
AABBCenter( mins, maxs ) - AABBHalfExtent( mins, maxs ),
AABBCenter( mins, maxs ) + AABBHalfExtent( mins, maxs ),
};
}
loam_expr JPH::Mat44 Matrix( const matrix3x4_t &m )
{
return JPH::Mat44
{
JPH::Vec4( GetColumn( m, MatrixAxis::X ).x, GetColumn( m, MatrixAxis::X ).y, GetColumn( m, MatrixAxis::X ).z, 0.0f ),
JPH::Vec4( GetColumn( m, MatrixAxis::Y ).x, GetColumn( m, MatrixAxis::Y ).y, GetColumn( m, MatrixAxis::Y ).z, 0.0f ),
JPH::Vec4( GetColumn( m, MatrixAxis::Z ).x, GetColumn( m, MatrixAxis::Z ).y, GetColumn( m, MatrixAxis::Z ).z, 0.0f ),
JPH::Vec4( Distance( GetColumn( m, MatrixAxis::Origin ) ), 1.0f ),
};
}
}
// Traces
// Same as CM_ClearTrace
inline void ClearTrace( trace_t *trace )
{
memset( trace, 0, sizeof( *trace ) );
trace->fraction = 1.0f;
trace->fractionleftsolid = 0.0f;
trace->surface.name = "**empty**";
}
// Converts a JoltPhysics smart ref-counted pointer to a raw pointer with a dangling
// reference that we can clean up explicitly explcitly later in eg. ConvexFree.
//
// The main reason is to avoids trailing class pointers that just have a single instance
// of this class that then get deleted, when we can just pass the raw pointer around ourselves
// and explicitly dereference on ourside when the game calls delete.
template < typename T >
T *ToDanglingRef( const JPH::Ref< T >& ref )
{
T *pPtr = ref.GetPtr();
pPtr->AddRef();
return pPtr;
}
template < typename T >
bool VectorContains( const std::vector< T >& vector, const T &object )
{
return std::find(vector.begin(), vector.end(), object) != vector.end();
}
inline const JPH::Shape* UndecorateShape( const JPH::Shape *pShape )
{
if ( pShape->GetType() == JPH::EShapeType::Decorated )
pShape = static_cast< const JPH::DecoratedShape * >( pShape )->GetInnerShape();
return pShape;
}
inline const JPH::StaticCompoundShape *GetCompoundShape( const JPH::Shape *pShape )
{
pShape = UndecorateShape( pShape );
return pShape->GetType() == JPH::EShapeType::Compound
? static_cast< const JPH::StaticCompoundShape * >( pShape )
: nullptr;
}
template < typename T, typename ShapeType, typename Func >
T ActOnSubShape( const JPH::Shape *pShape, int nIndex, Func ShapeFunc )
{
const JPH::StaticCompoundShape *pCompoundShape = GetCompoundShape( pShape );
if ( pCompoundShape )
{
const JPH::CompoundShape::SubShape& subShape = pCompoundShape->GetSubShape( nIndex );
return ShapeFunc( static_cast< const ShapeType * >( UndecorateShape( subShape.mShape.GetPtr() ) ) );
}
return ShapeFunc( static_cast< const ShapeType * >( UndecorateShape( pShape ) ) );
}
template < typename ShapeType, typename Func >
void ActOnSubShapes( const JPH::Shape *pShape, Func ShapeFunc )
{
const JPH::StaticCompoundShape *pCompoundShape = GetCompoundShape( pShape );
if ( pCompoundShape )
{
for ( const JPH::CompoundShape::SubShape& subShape : pCompoundShape->GetSubShapes() )
{
JPH::Mat44 matLocalTranslation = JPH::Mat44::sRotationTranslation( subShape.GetRotation(), subShape.GetPositionCOM() );
ShapeFunc( static_cast< const ShapeType * >( UndecorateShape( subShape.mShape.GetPtr() ) ), matLocalTranslation );
}
return;
}
ShapeFunc( static_cast< const ShapeType * >( UndecorateShape( pShape ) ), JPH::Mat44::sIdentity() );
}
inline uint32 popcntStep( uint32 n, uint32 mask, uint32 shift )
{
return ( n & mask ) + ( ( n & ~mask ) >> shift );
}
inline uint32 popcnt( uint32 n )
{
n = popcntStep(n, 0x55555555, 1);
n = popcntStep(n, 0x33333333, 2);
n = popcntStep(n, 0x0F0F0F0F, 4);
n = popcntStep(n, 0x00FF00FF, 8);
n = popcntStep(n, 0x0000FFFF, 16);
return n;
}
inline uint32 tzcnt( uint32 n )
{
#if defined(_MSC_VER) && !defined(__clang__)
return _tzcnt_u32( n );
#elif defined(__BMI__)
return __tzcnt_u32( n );
#elif defined(__GNUC__) || defined(__clang__)
// tzcnt is encoded as rep bsf, so we can use it on all
// processors, but the behaviour of zero inputs differs:
// - bsf: zf = 1, cf = ?, result = ?
// - tzcnt: zf = 0, cf = 1, result = 32
// We'll have to handle this case manually.
uint32 res;
uint32 tmp;
asm (
"tzcnt %2, %0;"
"mov $32, %1;"
"test %2, %2;"
"cmovz %1, %0;"
: "=&r" (res), "=&r" (tmp)
: "r" (n)
: "cc");
return res;
#else
uint32 r = 31;
n &= -n;
r -= ( n & 0x0000FFFF ) ? 16 : 0;
r -= ( n & 0x00FF00FF ) ? 8 : 0;
r -= ( n & 0x0F0F0F0F ) ? 4 : 0;
r -= ( n & 0x33333333 ) ? 2 : 0;
r -= ( n & 0x55555555 ) ? 1 : 0;
return n != 0 ? r : 32;
#endif
}

View File

@ -0,0 +1,9 @@
//-----------------------------------------------------------------------------
// VPHYSICS_JOLT.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro PROJNAME "vphysics_jolt"
$Include "vphysics_jolt_inc.vpc"

View File

@ -0,0 +1,141 @@
//-----------------------------------------------------------------------------
// VPHYSICS_JOLT_INC.INC
//
// Project Script
//-----------------------------------------------------------------------------
$MacroRequired "PROJNAME"
$Macro SRCDIR "..\.."
$Macro OUTBINDIR "$SRCDIR\..\$GAMEDIR\bin"
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Include "..\joltphysics\joltphysics_settings.vpc"
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Use Precompiled Header (/Yu)"
$Create/UsePCHThroughFile "cbase.h"
}
}
$Project "$PROJNAME"
{
$Folder "Precompiled Header"
{
$File "cbase.h"
$File "cbase.cpp"
{
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Create Precompiled Header (/Yc)"
}
}
}
}
$Folder "Source Files"
{
$Folder "Public Files"
{
$File "$SRCDIR\public\collisionutils.cpp" \
"$SRCDIR\public\filesystem_helpers.cpp"
{
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Not Using Precompiled Headers"
}
}
}
}
$File "vjolt_collide.cpp"
$File "vjolt_collide_trace.cpp"
$File "vjolt_constraints.cpp"
$File "vjolt_controller_fluid.cpp"
$File "vjolt_controller_motion.cpp"
$File "vjolt_controller_player.cpp"
$File "vjolt_controller_vehicle.cpp"
$File "vjolt_controller_shadow.cpp"
$File "vjolt_debugrender.cpp"
$File "vjolt_environment.cpp"
$File "vjolt_friction.cpp"
$File "vjolt_interface.cpp"
$File "vjolt_keyvalues_schema.cpp"
$File "vjolt_object.cpp"
$File "vjolt_objectpairhash.cpp"
$File "vjolt_parse.cpp"
$File "vjolt_querymodel.cpp"
$File "vjolt_surfaceprops.cpp"
}
$Folder "Header Files"
{
$Folder "Public Files"
{
$File "$SRCDIR\public\collisionutils.h"
$File "$SRCDIR\public\datamap.h"
$File "$SRCDIR\public\filesystem_helpers.h"
$File "$SRCDIR\public\vcollide_parse.h"
$File "$SRCDIR\public\vcollide.h"
}
$Folder "Compatibility"
{
$File "compat\better_winlite.h"
$File "compat\branch_overrides.h"
$File "compat\compat_sdk2013.h"
}
$File "vjolt_callstack.h"
$File "vjolt_collide.h"
$File "vjolt_constraints.h"
$File "vjolt_controller_fluid.h"
$File "vjolt_controller_motion.h"
$File "vjolt_controller_player.h"
$File "vjolt_controller_vehicle.h"
$File "vjolt_controller_shadow.h"
$File "vjolt_debugrender.h"
$File "vjolt_environment.h"
$File "vjolt_friction.h"
$File "vjolt_interface.h"
$File "vjolt_internal_listeners.h"
$File "vjolt_keyvalues_schema.h"
$File "vjolt_layers.h"
$File "vjolt_listener_contact.h"
$File "vjolt_object.h"
$File "vjolt_objectpairhash.h"
$File "vjolt_parse.h"
$File "vjolt_querymodel.h"
$File "vjolt_state_recorder_file.h"
$File "vjolt_surfaceprops.h"
$File "vjolt_util.h"
}
$Folder "Interface"
{
$File "$SRCDIR\public\vphysics_interface.h"
$File "$SRCDIR\public\vphysics\collision_set.h"
$File "$SRCDIR\public\vphysics\constraints.h"
$File "$SRCDIR\public\vphysics\friction.h"
$File "$SRCDIR\public\vphysics\object_hash.h"
$File "$SRCDIR\public\vphysics\performance.h"
$File "$SRCDIR\public\vphysics\player_controller.h"
$File "$SRCDIR\public\vphysics\stats.h"
$File "$SRCDIR\public\vphysics\vehicles.h"
}
$Folder "Link Libraries"
{
$Lib "joltphysics"
$Lib "mathlib"
$Lib "tier2"
}
}

View File

@ -0,0 +1,9 @@
//-----------------------------------------------------------------------------
// VPHYSICS_JOLT_STATIC.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro PROJNAME "vphysics_jolt_static"
$Include "vphysics_jolt_inc.vpc"