Vous êtes sur la page 1sur 656

PhysX SDK Training Programs Overview

Introduction
The SDK Training Programs are divided into lessons on rigid bodies, joints, user reports,
large-scale physics effects, tools, force fields, vehicles, integration, fluids, cloth, soft
bodies and other. They are designed to teach everything about the SDK in a highly
structured environment. Each lesson includes buildable source code and a document
explaining the source in full. The rigid bodies, joints, and user reports lessons teach
the fundamentals of the SDK. Each of these lessons introduces a new feature of the
SDK until all the features have been exhausted. The rest of the lessons go on to instruct
the user on implementation of the fundamentals for use in games. They demonstrate
how PSCL scripts from Rocket can be easily translated into C++ code and vice-versa.
They explain how to use Max and Maya to create physics assets, save them away, and
load them into an application. They teach developers how to implement character
controllers and vehicles using PhysX. They teach how to use the hardware scene
manager and implement special hardware features such as fluids, soft bodies and cloth.

1 Rigid Body Lessons


A PhysX rigid body is called an actor. Actors are constructed from collision shapes.
There are six types of collision shapes you can use to build an actor:

1. Plane
2. Box
3. Sphere
4. Capsule
5. Convex Mesh
6. Triangle Mesh

Normally, you will push your shapes onto the actor, assign the actor a density. Its mass
and inertia tensor will be computed when it is created. Actors can be made of a single
shape or multiple shapes. Each shape has a local position and orientation within the
actor with respect to the actor’s global position and orientation. The actor has a center
of mass and inertia tensor as well.

You can assign a material to an actor which describes what the interior and surface of the
actor are made of. You can specify actors as dynamic, static, or kinematic. Dynamic
actors are normal rigid bodies that move under physical laws and constraints.
Kinematic actors are moved by the user. Static actors are frozen in the scene. You
can selectively freeze the translational or rotational motion of an actor, you can disable
gravity on the actor, and you can damp the actor’s linear and angular velocities. You
can turn off collision between different groups of actors. Shapes can be assigned a
collision group mask as well as a collision group. The collision group mask allows for
implementing sophisticated collision filtering.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
Actors are moved by applying forces or torques to them. Forces or torques are normally
applied at the center of mass of the actor, but can be applied anywhere on the actor and
defined globally or locally with respect to the actor.

Next, the rigid body lessons discuss triangle meshes and how you can use them to create
convex and concave shapes for your actors. Materials can be applied on a per-shape
basis and they can also be applied on a per-triangle basis if you build your object from an
indexed triangle mesh. You can manually detect for shapes and triangle geometry
intersecting a particular region using the SDK's intersection tests. You can get contact
information between actors from the user contact report.

2 Joints Lessons
The joints lessons cover everything about the connections between rigid bodies: joints.
Joints connect two actors together at a point. The simplest joint is the fixed joint which
fuses two actors together at a point so they translate and rotate together as if they were
two pieces of a single rigid body. Other joints open up further degrees of freedom of
movement between the actors, both rotational and translational. The simplest rotational
joint is the revolute or “hinge” joint which allows its actors to rotate with respect to each
other around a point about a single axis. The simplest translational joint is the prismatic
or “shock absorber” joint which allows its actors to translate with respect to each other
through a point along a single axis. There are several types of joints used by the SDK.
You will learn how to use 7 of them. By each are their associated degrees of freedom.

1. Fixed Joint (0 rotational, 0 translational)


2. Revolute Joint (1 rotational, 0 translational)
3. Spherical Joint (3 rotational, 0 translational)
4. Prismatic Joint (0 rotational, 1 translational)
5. Cylindrical Joint (1 rotational, 1 translational)
6. Point-on-Line Joint (3 rotational, 1 translational)
7. Point-in-Plane Joint (3 rotational, 2 translational)
8. 6 DOF Joint (3 rotational, 3 translational)

Joints can have limits, springs, and motors attached to them. They can be made to break
under certain forces or torques as well. You will learn about an additional Pulley Joint,
which simulates the action of a tackle of pulley gears connecting two objects. Finally,
there is a Distance Joint which keeps objects at a distance with an optional springiness.

3 User Reports Lessons


The user reports lessons educates the developer on the various user report objects used by
the SDK. The actor pair filtering lesson show the function of how to filter the collision
pair. The memory allocator is a user-specified implementation for allocating and
deallocating memory. The contact report collects contact signals from the scene. Shapes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
within an actor can be designated as trigger areas which have no collision on them and
detect when other shapes enter or leave. You can shoot a ray through a scene comprised
of actors to determine which actors the ray hits and where the ray hits them. The entity
report collects information on the rigid body objects located within a specified geometric
region. The notify lesson allows you receive the notification when the joint breaking. The
output stream lessons teach the developer how to define a custom error handle
mechanism.

4 Physics Effects Lessons


The rigid body, joints, and user reports lessons teach all the fundamentals of the PhysX
SDK. The physics effects lessons cover the implementation of complicated systems of
connected rigid bodies using these fundamentals. These systems create many examples
of effects physics. We create examples of rope, jointed rigid body cloth, ragdolls, gears,
a tread made out of gears and a belt, a tank, and rigid body grass.

5 Tools Lessons
The tools lessons teach the developer how to build physics assets with 3DS Max and
Maya, save them away, and load them into an application.

6 Forcefield Lessons
The Forcefield lessons teach the developer how to build Forcefields, Forcefields are SDK
objects akin to actors, which affect cloth, soft bodies, fluid and rigid bodies that enter
their area of influence. Force fields allow you to implement for example gusts of wind,
dust devils, vacuum cleaners or anti-gravity zones.
In lesson601, we show you how to create a simple Forcefield. The Forcefield shapes,
which determine the volume of space in which the force field is active, is introduced.
Another concept is the Forcefield shapes, which is simply a collection of force field
shapes.
In lesson602, the Forcefield kernels is introduced, which define how to computes a force
and torque on a particle (or volume element or rigid body) with a particular position and
linear velocity.
In lesson603, the Force field Coordinate System is introduced. We can use CARTESIAN,
SPHERICAL, CYLINDRICAL, or TOROIDAL Coordinate Systems in Forcefields.
Lesson604 is about the Forcefield scaling. The force values obtained from the kernel are
then scaled. Each kind of object (fluid, cloth, soft body, rigid body) has its own scaling
properties.

7 Vehicles Lessons
The vehicles lessons teach the developer how to create a wheel shape. It teaches how to
assign a suspension and torque to the wheel shape as well as brake and steer the wheel

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
shape. The final lesson builds a four-wheeled user-controlled car with a rigid body and
four wheel shapes.

8 Integration Lessons
The integration lessons teach the developer advanced use of the hardware. It teaches how
to use the hardware scene manager, how to build an application with multiple scenes,
how to create a gameplay software scene that interacts with an “effects” hardware scene
and how to take control of PhysX thread allocation.

9 Fluids Lessons
The fluids lessons teach the developer how to create fluid particles, emitter that spawn
fluid particles, assign meshes for the fluid to collide against, how to receive fluid events,
and how to use the fluid UserData.

10 Cloth Lessons
The cloth lessons teach the developer how to create cloth, how to apply forces to points
on the cloth, how to damp the cloth, how to attach the cloth to rigid bodies, how to make
the cloth tearable, how to assign a pressure value within a fully enclosed piece of cloth,
how to apply self collision to the cloth, how to make the cloth interact with fluids and
how to create a cloth metal.

11 Soft Body Lessons


The soft body lessons teach the developer how to create general soft body, attached soft
body, self-collision soft body, tearable soft body. And we also can learn how to apply
external acceleration to the soft body.

12 Other Lessons
The character controller lesson teaches the developer how to build character controllers.
This lesson has character controllers moving over the triangle meshes and through the
game level with various dynamic objects in the level.

13 The Time Step


Throughout the lessons, I use the QueryPerformanceFrequency() and
QueryPerformanceCounter() functions in UpdateTime.h to get the time-step. These are
very precise and robust.

unsigned long timeGetTime()


{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
unsigned long long ticksPerMillisecond = freq.QuadPart/1000;

LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (unsigned long)(counter.QuadPart/ticksPerMillisecond);
}

QueryPerformanceFrequency() and QueryPerformanceCounter() don’t work on all


machines. They may not work well on laptops where the processor frequency is
contantly being adjusted. If your simulations freeze at startup, this may be the problem
and you may want to switch to timeGetTime(). Your update time function would then
look like this:

float UpdateTime()
{
static unsigned int previousTime = timeGetTime();
unsigned int currentTime = timeGetTime();
unsigned int elapsedTime = currentTime - previousTime;
previousTime = currentTime;
return (float)(elapsedTime)*0.001f;
}

You can also use a fixed time step. Change your physics loop like this to run physics
for 1/60th of a second every frame.

NxReal UpdateTime()
{
return 1.0f/60.0f;
}

14 IsValid()
When trying out the lessons, you may want to strike out on your own to alter them. A
check that was left out for the sake of user readability is the “isValid()” call. isValid()
can be called on any object descriptor before you pass it in to create an object. Take a
look at the CreateBox() function in Lesson 101:

NxActor* CreateBox()
{
// Set the box starting height to 3.5m so box starts off falling onto the ground
NxReal boxStartHeight = 3.5;

// Add a single-shape actor to the scene


NxActorDesc actorDesc;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
NxBodyDesc bodyDesc;

// The actor has one shape, a box, 1m on a side


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(0.5,0.5,0.5);
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = 10;
actorDesc.globalPose.t = NxVec3(0,boxStartHeight,0);
return gScene->createActor(actorDesc);
}

Try running the demo after commenting out the line that sets the actor density:

// actorDesc.density = 10;

The actor descriptor you passed in has neither density nor mass, so the box is not created
and createActor returns NULL (so box is NULL). The box is not there and if you try to
apply a force to the box, you will get an unhandled exception as you try to access a
function from the NULL actor.

Use NxActorDesc::isValid() to avoid the confusion.

actorDesc.body = &bodyDesc;
// actorDesc.density = 10;
actorDesc.globalPose.t = NxVec3(0,boxStartHeight,0);
NX_ASSERT(actorDesc.isValid());
return gScene->createActor(actorDesc);

The program will now halt immediately as the assertion is made and noted in the debug
log. isValid() is a must for triangle mesh descriptors and triangle mesh shape
descriptors as there are several tests they need to pass before they can be used to create
meshes and mesh shapes.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
Nvidia PhysX SDK Training Programs Manifest
Welcome to the Nvidia PhysX SDK Training Programs! The lessons are divided into twelve
chapters:

Chapter 1 – Rigid Bodies


Chapter 2 – Joints
Chapter 3 – User Reports
Chapter 4 – Physics Effects
Chapter 5 – Tools
Chapter 6 – Force Fields
Chapter 7 – Vehicles
Chapter 8 – Integration
Chapter 9 – Fluids
Chapter 10 – Cloth
Chapter 11 – Soft Body
Chapter 12 – Other

The following is a manifest of the lessons.

Chapter 1 – Rigid Bodies


Lesson 101 – Primary Shape
Lesson 102 – Mesh Shapes
Lesson 103 – Compound
Lesson 104 – Actors
Lesson 105 – Materials
Lesson 106 – Putting Actors to Sleep and Skin Width
Lesson 107 – Rigidbody Properties
Lesson 108 – Freeze Flags and Damping
Lesson 109 – Collision Groups
Lesson 110 – Collision Filtering
Lesson 111 – Dynamic CCD
Lesson 112 – Overlap
Lesson 113 – Rigidbody Cloth
Lesson 114 – Rigidbody Fluid
Lesson 115 – Rigidbody Softbody
Lesson 116 – Rigidbody ForceField

Chapter 2 – Joints
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
Lesson 201 – Revolute Joints
Lesson 202 – Joint Springs and Motors
Lesson 203 – Spherical Joints
Lesson 204 – Prismatic Joints
Lesson 205 – Cylindrical and Fixed Joints
Lesson 206 – Point_on_Line Joints
Lesson 207 – Point_in_Plane Joints
Lesson 208 – Pulley Joints
Lesson 209 – Distance Joints
Lesson210 - 6DOF_Joints
Lesson211 - 6DOF_Joint_Limits_Springs
Lesson212 - Joint_Limit
Lesson213 - 6DOF_Motion_Controller
Lesson214 - Breakable_Joints
Lesson215 - 6DOF_Linear_Driver
Lesson216 - 6DOF_Angular_Driver

Chapter 3 – User Reports


Lesson 301 – Actor Pair Filtering
Lesson 302 – Memory Allocator
Lesson 303 – Contact Report
Lesson 304 – Trigger Report
Lesson 305 – Raycast Report
Lesson 306 – Entity Report
Lesson 307 – Notify
Lesson 308 – Output Stream
Lesson 309 – Scheduler

Chapter 4 –Physics Effects


Lesson 401 – Rope
Lesson 402 – Jointed_Rigid_Body_Cloth
Lesson 403 – Ragdolls
Lesson 404 – Tank
Lesson 405 – Grass
Lesson 406 – Articulated_Truck
Lesson 407 – Particle_Systems
Lesson 408 – Fragmenting_Objects

Chapter 5 – Tools
Lesson 501 – Debug Renderer

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
Lesson 502 – Visual Remote Debugger
Lesson 503 – Core Dump
Lesson 504 – Scene Export
Lesson 505 – Asset Export

Chapter 6 – Force Fields


Lesson 601 – Forcefield Creation
Lesson 602 – Forcefield Coordinate
Lesson 603 – Forcefield Kernels
Lesson 604 – Forcefield Scaling

Chapter 7 – Vehicles
Lesson 701 – Wheel Shapes
Lesson 702 – Suspension and Torque
Lesson 703 – Brakes and Steering
Lesson 704 – All Wheel Drive

Chapter 8 – Integration
Lesson 801 – Hardware_Scene_Manager
Lesson 802 – Multiple Scenes
Lesson 803 – PhysX FX Integration
Lesson 804 – User Controlled Threads

Chapter 9 – Fluids
Lesson 901 – Fluid Creation
Lesson 902 – Fluid Emitters
Lesson 903 – Fluids and Rigid Bodies
Lesson 904 – Fluids and Drain
Lesson 905 – Fluids and Events
Lesson 906 – Fluids UserData

Chapter 10 – Cloth
Lesson 1001 – Cloth
Lesson 1002 – External Forces
Lesson 1003 – Damping
Lesson 1004 – Pipe
Lesson 1005 – Attachment
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
Lesson 1006 – Tearing
Lesson 1007 – Pressure
Lesson 1008 – Self Collision
Lesson 1009 – Interaction with Fluid
Lesson 1010 – Metal

Chapter 11 – Soft Body


Lesson1101 – SoftBodyCube
Lesson1102 – PalmTree
Lesson1103 – BunnyScene
Lesson1104 – SelfCollision
Lesson1105 – SoftWheelCar
Lesson1106 – Tearing

Chapter 12 – Other
Lesson 1201 – Character Controller

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
NVIDIA PhysX Math Primer

1. How do I take a position in world space and transform it into the local space of an
actor?

Basic implementation:

NxActor* actor;

NxVec3 localPos, worldPos;
NxMat34 mat, invMat;

worldPos = NxVec3(0,0,1);
mat = actor->getGlobalPose();
mat.getInverse(invMat);
localPos = invMat * worldPos;

Optimized implementation:

NxVec3 localPos, worldPos;


worldPos = NxVec3(0,0,1);
localPos = actor->getGlobalPose() % worldPos;

(From NxMat34.h:

NX_INLINE NxVec3 operator% (const NxVec3 & src) const { NxVec3 dest;
multiplyByInverseRT(src, dest); return dest; } )

2. How do I take a position in the local space of an actor and transform it into world
space?

NxActor* actor;

NxVec3 localPos, worldPos;
NxMat34 mat;

localPos = NxVec3(0,0,1);
mat = actor->getGlobalPose();
worldPos = mat * localPos;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
3. How do I take a vector in world space and transform it into the local space of an
actor?

Basic implementation:

NxVec3 localVec, worldVec;


NxMat33 orient, invOrient;

worldVec = NxVec3(0,0,1);
orient = actor->getGlobalOrientation();
orient.getInverse(invOrient);
localVec = invOrient * worldVec;

Optimized implementation:

NxVec3 localVec, worldVec;


worldVec = NxVec3(0,0,1);
localVec = actor->getGlobalOrientation() % worldVec;

4. How do I take a vector in the local space of an actor and transform it into world
space?

Basic implementation:

NxVec3 localVec, worldVec;


NxMat33 orient, invOrient;

localVec = NxVec3(0,0,1);
orient = actor->getGlobalOrientation();
orient.getInverse(invOrient);
invOrient.setTransposed();
worldVec = invOrient * localVec;

Optimized implementation:

NxVec3 localVec, worldVec;


localVec = NxVec3(0,0,1);
worldVec = actor->getGlobalOrientation() * localVec;

5. How do I create a quaternion from an angle and an axis?

NxVec3 v(0,1,0);
NxReal ang = 90;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
NxQuat q;
q.fromAngleAxis(ang, v);

This quaternion is a rotation of the identity transformation about the vector, v, through
the angle, ang.

6. How do I get an orientation matrix from a quaternion?

NxQuat q;

NxMat33 orient;
orient.fromQuat(q);

7. How do I extract the 3 local axes of an actor?

NxMat33 orient;
NxVec3 xaxis, yaxis, zaxis;

orient = actor->getGlobalOrientation();
orient.getRow(0, xaxis);
orient.getRow(1, yaxis);
orient.getRow(2, zaxis);

8. How do I build an orientation matrix from a vector?

NxVec3 axis, binormal, normal;


...
axis.normalize();
NxNormalToTangents(axis, binormal, normal);
NxMat33 orient(normal, axis, binormal);

9. How do I use Euler Angles to set the orientation of an actor?

NxActor* actor;
...
// Euler angle
NxVec3 eulerAngle = NxVec3(90,90,30);

// Quaternion
NxQuat q1, q2, q3;
q1.fromAngleAxis(eulerAngle.x, NxVec3(1,0,0));
q2.fromAngleAxis(eulerAngle.y, NxVec3(0,1,0));
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
q3.fromAngleAxis(eulerAngle.z, NxVec3(0,0,1));

// NxQuat q;
// q = q3*q2*q1; // Use global axes
// q = q1*q2*q3; // Use local axes

// Orientation matrix
NxMat33 orient1, orient2, orient3;
orient1.fromQuat(q1);
orient2.fromQuat(q2);
orient3.fromQuat(q3);

NxMat33 orient;
orient = orient3*orient2*orient1; // Use global axes
// orient = orient1*orient2*orient3; // Use local axes

// 1. Set actor orientation from NxMat33


actor->setGlobalOrientation(orient);

// 2. Get NxMat33 from NxQuat


// orient.fromQuat(q);
// actor->setGlobalOrientation(orient);

// 3. Set actor orientation directly from NxQuat


// actor->setGlobalOrientationQuat(q);

PhysX does its rotations in a right-handed manner. In the uncommented code, I assume
that you're rotating your object about the global axes (meaning you need to multiply the
transformations M3*M2*M1). If you want to rotate your object about its local axes, you
need to uncomment the local axes code (M1*M2*M3).

We use NxQuat::fromAngleAxis() to get the 3 quaternions and then


NxMat33::fromQuat() to convert the quaternions into 3 3x3 matrices which we multiply
together to get our final orientation matrix (1). You can also multiply your quaternions
together, then convert the final quaternion into your final orientation matrix (2). Or, you
can bypass matrices altogether and set the orientation of the actor using
NxActor::setGlobalOrientationQuat() (3).

10. How do I rotate a vector about another vector?

You can rotate a vector, v, around another vector, w, like this:

NxVec3 w(0,0,1)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
NxQuat q;
NxReal ang = 30;
q.fromAngleAxis(ang, w);

NxVec3 v(1,0,0);
q.rotate(v);

We basically build a quaternion, q, that is a rotation of the identity orientation matrix


about the vector, w, through the angle, ang. (see #5 above)

In this case, w = (0,0,1) and ang = 30. The vector we rotate is v = (1,0,0).

We then use NxQuat::rotate() to rotate the vector, v, about w through ang.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
GPU PhysX Guide
Introduction
The PhysX on GeForce uses NVIDIA CUDA technology, and requires the installation of
compatible graphics drivers with CUDA 2.0 support. We exploit the computing ability of GPU
to accelerate the PhysX.

If multiple CUDA 2.0 enabled GPUs of different models are present, it is possible to select a
particular GPU for PhysX.

GPU PhysX drivers


First you should install the compatible GPU drivers with CUDA 2.0 support, then installing the
compatible GPU PhysX drivers, such as PhysX_x.x.x_SystemSoftware_xxx.exe and
PhysX_xxx _SDK_Core.msi.

Latest installer packages including graphics driver and PhysX driver are available at NVIDIA
website.

The PhysX control panel which is from PhysX system software installer allows a target device
(AGEIA PhysX PPU or a supported GeForce GPU) to be selected for PhysX hardware-enabled
content. By default, any compatible GPU is selected. Please note when using Microsoft
Windows VISTA control panel user settings will only be persistent when the control panel is run
in administer mode.

Create GPU PhysX content


Now you can create your PhysX application. Being same with creating PPU PhysX content, we
can create GPU PhysX by directly applying their Hardware flag, such as applying
NX_SBF_HARDWARE flag to soft body descriptor.

NxSoftBodyDesc softBodyDesc;

……

softBodyDesc.flags |= NX_SBF_HARDWARE; // apply hardware flag

…..

gScene-> createSoftBody(softBodyDesc);

And we can create fluid, cloth and softbody in hardware compartment.

NxSoftBodyDesc softBodyDesc;
Copyright (c) 2002 - 2008, NVIDIA Technologies, Inc., USA. All rights reserved
http://www.nvidia.com
……

softBodyDesc.compartment = HWcompartment ; // see PhysX SDK doc for HW compartment

softBodyDesc.flags |= NX_SBF_HARDWARE; // apply hardware flag

…..

gScene-> createSoftBody(softBodyDesc);

With above simple code, the hardware soft body is created.

Run PhysX content on GPU


When your application with HW content is ready, you can select GPU to run PhysX. See
documentation named NVIDIA_PhysX_Help.mht for detailed information, which is
under C:\Program Files\AGEIA Technologies\NVIDIA_PhysX_Help.mht.
Or you launch the file by PhysX control panel -> Help -> Documentation.

Now, you can enjoy the GPU PhysX!

Copyright (c) 2002 - 2008, NVIDIA Technologies, Inc., USA. All rights reserved
http://www.nvidia.com
Application Startup in Visual Studio
File->New Project->Lesson101

1. Add files to “..\Lesson101\source” directory

Lesson101.cpp
DrawShapes.cpp

Lesson101.h
DrawShapes.h
UpdateTime.h
DebugRenderer.h
ExitCallback.h

2. Add libraries

novodex/Graphics/libs/win32/release/glut32.lib
novodex/SDKs/Foundation/libs/win32/release/NxFoundation.lib
novodex/SDKs/Physics/libs/win32/release/NxPhysics.lib

3. Set directory to output files

Project->Lesson101 Properties->General->Output Directory

.\../../../../../../bin/win32

4. Set additional include directories

Project-> Lesson101 Properties->C++->General->Additional Include


Directories

../../../../Shared Source
../../../../../../Graphics/include/win32

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
../../../../../../SDKs/Foundation/include
../../../../../../SDKs/Physics/include

5. Make sure output file is correct name

Project-> Lesson101 Properties->Linker->General

.\../../../../../../bin/win32/Lesson101DEBUG.exe

Project-> Lesson101 Properties->Linker->Debugging

.\../../../../../../bin/win32/Lesson101.pdb

6. Don’t use precompiled headers

Project-> Lesson101 Properties->C/C++->Precompiled Headers

Not Using Precompiled Headers

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
Asynchronous API
1 Introduction
Next-generation platforms are moving towards multi-processor architectures to provide
more computing power for rendering, animation, AI, physics, sound and other game
elements. This is the basic motivation underlying the NVIDIA PhysX SDK’s transition
to multithreaded physics simulation. In a multiprocessor environment, the thread running
the simulate(), flushStream(), fetchResults() function block can be offloaded from the
main processor, and in a properly designed game engine, this approach can expand the
scale and quality of physics content while providing better and more consistent frame
rate.

2 Multithreading
Version 2.2 of the PhysX SDK runs in a multithreaded mode only. The basic
mechanism is as follows: the major SDK objects run in the game application thread, and
a separate thread is spawned for performing the simulation calculations. Publicly visible
state and object data are double buffered, and a write-locking mechanism is employed to
prevent the application from altering this data while simulation is in progress. It is
important to note that this multithreading approach does not support re-entrancy; that is,
it is not thread safe. This means that you should not make calls to the DLL from more
than one thread at any time.

3 Advancing the Simulation State


The main feature of a scene is its capability to actually perform the physics simulation.
This results in various object properties being evolved over time, including for example
bodies’ positions and velocities.

Simulation is done one time step at a time, typically using a fixed stepsize ranging
between 1/100th of a second and 1/50th of a second. For a real-time simulation the
application must perform several of these time steps to synchronize the physics behavior
with the rendered framerate. You can control this time subdivision with the

NxScene::setTiming();

method. You can alse set timing parameters in the scene descriptor prior to scene
construction. The default settings are appropriate for most applications.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
4 State Buffering
With multithreading, the physics simulation calculations run in their own thread, separate
from the application thread. The state of the simulation is updated by calling a sequence
of functions that

5 Start the simulation.


6 Ensure that all necessary data have been sent to the simulation thread.
7 Check to see whether the simulation is finished, and if so, update the state data in
the buffer.
8 Swap the state data buffers so that the next simulation step will be performed on
the alternate buffer, leaving the current results accessible to the application.

This physics state buffering system works much the same way as graphics buffering
system, where the next scene is drawn to a back buffer while the current scene, the screen
buffer, is being displayed. When drawing to the back buffer is finished, the back buffer
and screen buffers are swapped. With the state buffering system, we have access to the
results of the current state of the physics simulation, which we use for rendering,
observing positions and velocities of objects, etc. The next state of the physics
simulation is being computed while we process the data from the current simulation state.
When we are done processing the current state data, we fetch the results for the new state
and the new state and current state are swapped.

9 The New Physics Loop


Throughout the previous lessons, our main physics loop, RunPhysics() in the
RenderCallback() function has looked like this:

void RenderCallback()
{

if (gScene && !bPause) RunPhysics();

}

void RunPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();

// Run collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
gScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
2
This is horribly unoptimal using multithreading. We start the physics simulation in a
separate thread with the simulate() and flushStream() functions. Then we call
fetchResults() which waits for the simulation to finish before continuing. In short,
fetchResults() stalls the program for as long as the simulation takes to run its course.

We fix up the physics loop in this lesson so the main game loop thread and the physics
thread run concurrently instead of one after the other. (i.e., run in parallel instead of back-
to-back). Look at the new RenderCallback():

void RenderCallback()
{

if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

}

Every frame when we get to RenderCallback(), we call GetPhysicsResults(), which


contains gScene->fetchResults(), first:

void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}

This gets the results from the simulation started on the previous call to RenderCallback()
with StartPhysics(). Note that the fetchResults() we call here is non-blocking with
“false” as the second parameter. If we have gotten here and the simulation has not
finished, we can continue to do more useful work on the previous frame’s state data, or
work unrelated to the physics data, while the simulation thread is processing the
calculations. Also, the parameter NX_RIGID_BODY_FINISHED is a flag of type
NxSimulationStatus indicating the rigid body part of the simulation has been completed.
Future versions of the SDK will provide simulation capabilities beyond rigid body
dynamics, but as of version 2.2, NX_RIGID_BODY_FINSIHED is the only flag
available.

Immediately after we get the new physics state from GetPhysicsResults(), we execute
processing code from the last state and setup code for the next state with ProcessPhysics()
(explained in detail below) and then call StartPhysics(), which contains gScene-
>simulate() and gScene->flushStream(), to begin the next simulation.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
void StartPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();

// Start collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
}

For this order-reversal to work, we make an initial call to StartPhysics() in InitNx() to get
the ball rolling.

void InitNx()
{
...
// Initialize the physics loop
UpdateTime();
if (gScene && !bPause) StartPhysics();
}

With the exception of the remaining code in ProcessPhysics(), the above code is all you
need to get your physics simulation to run in parallel with the rest of your game code.

10 ProcessInputs()
In ProcessInputs(), we call all the code to place actors, move actors, and add forces to
actors in preparation for the next scene. We also set up user-written code to get called
called within the main physics loop (i.e., the user report calls). This includes raycasts,
setting triggers, setting contact pairs, and initializing objects for debug rendering.

Take a look at ProcessInputs():

void ProcessInputs()
{
ProcessKeys();

// Show debug wireframes


if (bDebugWireframeMode)
{
glDisable(GL_LIGHTING);
gScene->visualize();
gPhysicsSDK->visualize(gDebugRenderer);
glEnable(GL_LIGHTING);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
}
}

In our demo, ProcessKeys() contains all the code that moves actors or applies forces to
actors, so we put that here.

NxVec3 ApplyForceToActor(NxActor* actor, const NxVec3& forceDir, const NxReal


forceStrength)
{
NxVec3 forceVec = forceStrength*forceDir;
actor->addForce(forceVec);
return forceVec;
}

void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{
...
// Force controls
case 'i': {gForceVec =
ApplyForceToActor(box,NxVec3(0,0,1),gForceStrength); break; }
case 'k': {gForceVec = ApplyForceToActor(box,NxVec3(0,0,-
1),gForceStrength); break; }
case 'j': {gForceVec =
ApplyForceToActor(box,NxVec3(+1,0,0),gForceStrength); break; }
case 'l': {gForceVec = ApplyForceToActor(box,NxVec3(-
1,0,0),gForceStrength); break; }
case 'u': {gForceVec =
ApplyForceToActor(box,NxVec3(0,+1,0),gForceStrength); break; }
case 'm': {gForceVec = ApplyForceToActor(box,NxVec3(0,-
1,0),gForceStrength); break; }

// Return box to (0,5,0)


case 't': { box->setGlobalPosition(NxVec3(0,5,0)); break; }
}
}
}

We also put in the calls to draw the debug wireframes if debug rendering has been turned
on.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
As far as raycasts, contact pairs, and triggers are concerned, we set these up here as well.
We will have the results from the raycast report, contact report, and trigger report once
GetPhysicsResults() is called. For this reason, it is recommended to batch returned
information from these reports in global data as it will not be available from an
immediate call to these functions.

11 Conclusion
Multithreading is a powerful way to speed up your game as it allows you to execute your
physics code in parallel with the rest of your game code.

12 Related Classes, Functions, and Parameters


NxScene
setTiming()
simulate()
flushStream()
fetchResults()

NxSimulationStatus
NX_RIGID_BODY_FINISHED

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
NVIDIA PhysX User Defined Classes

1 Introduction
There are seven user defined classes that call user-written code within the main physics
loop.

1. NxUserAllocator - Memory Management


2. NxUserDebugRenderer - Debug visualization
3. NxUserOutputStream - Error reporting
4. NxUserContactReport - Contact information
5. NxUserNotify - Misc. events, including joint breakage
6. NxUserRaycastReport - Ray casting information
7. NxUserTriggerReport - Trigger information
8. NxUserEntityReport – Intersection information

2 The User Allocator (NxUserAllocator)


The User Allocator is a very simple memory allocator class which just uses the standard
C memory allocation routines. The real purpose of a custom allocator is for you to use
some custom memory allocator scheme.

class Allocator : public NxUserAllocator


{
public:
void * malloc(NxU32 size)
{
return ::malloc(size);
}

void * mallocDEBUG(NxU32 size, const char *fileName, int line)


{
return ::_malloc_dbg(size, _NORMAL_BLOCK, fileName, line);
}

void * realloc(void * memory, NxU32 size)


{
return ::realloc(memory,size);
}

void free(void * memory)


{
::free(memory);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
}
} gAllocator;

gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
&gAllocator, NULL);

The User Allocator is not particularly useful for PC development, but was meant to be
used on systems with special memory management requirements. However, it lets the
user monitor the memory usage of the SDK, which may be useful in many cases. Note
that currently parts of the mesh collision detection subsystem bypass this mechanism and
allocates from the OS directly.

To have the SDK use your own memory allocation methods instead of the standard C
malloc/realloc/free, you need to define a class derived from NxUserAllocator, and define
all of its pure virtual methods. These methods are basically the C runtime
malloc/realloc/free, so on your first try you could just call these methods. The above
class is an example implementation. Of course the point is for you to call your custom
made memory allocator instead. The SDK does not guarantee any particular usage
patterns that would let you make a really specialized allocator. In the many cases where
this is possible, the SDK will allocate a chunk of memory with your allocator at startup,
and then internally manage this block for its internal allocation needs.

The allocator that you define should be passed as the optional second argument to the
NxCreatePhysicsSDK() method on startup. After you create the SDK, there is no way to
change or remove the allocator.

3 The User Debug Renderer (NxUserDebugRenderer)


The User Debug Renderer is an object used throughout the lessons to display debug
visualization data as specified by the user. It is created in DebugRenderer.h with the
manner the user wants to display the debug points, lines, and triangles. Types of debug
data the user wants to display are set in InitNx() using NxPhysicsSDK::setParameter().
The Debug Renderer gets passed into NxPhysicsSDK::visualize() in RenderCallback() to
render the debug data. It first shows up in “Lesson 101: Box on a Plane”.

1. The User Debug Renderer is created in DebugRenderer.h.

class DebugRenderer: public NxUserDebugRenderer


{
public:
NX_INLINE void DebugRenderer::setupColor(NxU32 color) const
{
// Code to set up debug colors
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
void DebugRenderer::renderData(const NxDebugRenderable& data) const
{
// Code to set up rendering methods
}
} gDebugRenderer;

2. The User Debug Renderer’s parameters are set by


NxPhysicsSDK::setParameter() in InitNx().

void InitNx()
{

// Set the debug visualization parameters
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 10);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

}

3. The User Debug Renderer is passed into NxPhysicsSDK::visualize() in the


RenderCallback() routine.

void RenderCallback()
{

// Show debug wireframes
if (bDebugWireframeMode)
{
glDisable(GL_LIGHTING);
gScene->visualize();
gPhysicsSDK->visualize(gDebugRenderer);
glEnable(GL_LIGHTING);
}
}

4 The User Output Stream (NxUserOutputStream)


The User Output Stream The Physics SDK provides the possibility to supply a user
defined stream class for error logging. Basically, the user needs to subclass the abstract
base class NxUserOutputStream:

class OutputStream : public NxUserOutputStream


{
void reportError (NxErrorCode code, const char *message, const char *file,
int line)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
{
// This should be routed to the application specific error handling. If this
gets
// hit then you are in most cases using the SDK wrong and you need to
debug
// your code! However, code may just be a warning or information.
if (code < NXE_DB_INFO)
MessageBox(“SDK Error”, message, MB_OK);
exit(1);
}

NxAssertResponse reportAssertViolation (const char *message, const char


*file, int line)
{
// This should not get hit by a properly debugged SDK!
assert(0);
return NXAR_CONTINUE;
}

void print (const char *message)


{
printf(“SDK says: %s\n”, message);
}

} gOutputStream;

This object is then passed to either NxCreatePhysicsSDK():

gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, 0,
&gOutputStream);

At the moment messages are only logged when an error occurs, with standard syntax.

For example:

C:\novodex\SDKs\Physics\src\PhysicsSDK.cpp(123) : error 3: invalid parameter

If a stream object has been supplied, such an error message will be generated. In
addition, the reportError() member of the error stream will be called with a numeric error
code. Then, if the DLL has been compiled with exception handling enabled (this is by
default not the case though!) an exception (type NxException) will be thrown. This
exception contains the same information as the error message.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
5 The User Contact Report (NxUserContactReport)
The User Contact Report is used to collect contact information on actors marked as
contact pairs. It first appears in “Lesson 105: The User Contact Report and the Contact
Stream Iterator”.

6 The User Contact Report is created in ContactReport.h.

class ContactReport : public NxUserContactReport


{
public:
virtual void onContactNotify(NxContactPair& pair, NxU32 events)
{
// Code to process contacts on a contact pair that has collided
}
} gContactReport;

7 The User Contact Report is attached to the SDK in InitNx().

void InitNx()
{

// Create the scene
NxSceneDesc sceneDesc;

sceneDesc.userContactReport = &gContactReport;
gScene = gPhysicsSDK->createScene(sceneDesc);

}

8 The contact pairs monitored by the User Contact Report are set by
NxScene::setActorPairFlags() in InitNx().

void InitNx()
{
box1 = CreateBox(NxVec3(-3,5,0), NxVec3(0.75,0.75,0.75), 5);
box2 = CreateBox(NxVec3(3,0,0), NxVec3(1,1,1), 5);

gScene->setActorPairFlags(*box1, *box2,
NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_TOUCH |
NX_NOTIFY_ON_END_TOUCH);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
9 The User Notify Report (NxUserNotify)
Joints can set to be breakable. This means that if the joint is tugged on with a large
enough force, it will snap apart. Setting up a joint to be breakable is very simple: Just
set the maxForce and maxTorque members of NxJointDesc to the desired values – the
smaller the value, the more easily the joint will break. The maxForce member is the
resistance to linear pulling forces, while the maxTorque resists twisting. The exact
behavior depends on the type of joint. It is also possible to change these parameters for
an existing joint using NxJoint::setBreakable().

The User Notify Report calls onJointBreak() when a joint gets broken. When a joint
breaks, the onJointBreak() method of the NxUserNotify class is called, assuming the user
has supplied the SDK with an instance of such an object using the
NxScene::setUserNotify method. Further, the joint is put into a ‘broken’ state, which
basically deactivates it. At this point the user may ask for it to be deleted by returning
true in onJointBreak(). The user may not delete the joint simply by calling releaseJoint()
from onJointBreak() because the SDK forbids such reentrant calls that change the
simulation state. No other action is permitted on broken joints.

Note that even non-breakable joints are put in a broken state when the user releases one
of the two bodies that the joint was referencing. However, in this case no onJointBreak()
event is issued.

The User Notify Report is currently only rigged to call onJointBreak(), but will be
fleshed out in the future to notify the user of other physics events as the SDK is
expanded.

10 The User Raycast Report (NxUserRaycastReport)


The User Raycast Report determines what objects have been hit by rays shot through the
scene. It first appears in “Lesson 114: Raycasting”.

1. The User Raycast Report is created in RaycastReport.h.

class RaycastReport : public NxUserRaycastReport


{
virtual bool onHit(const NxRaycastHit& hit)
{
// Code to process a hit by a cast ray
}
} gRaycastReport;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
2. The rays for the User Raycast Report are fired by
NxScene::raycastClosestShapes() and NxScene::raycastAllShapes() called
from RaycastClosestShapeFromActor() and RaycastAllShapesFromActor()
in RunPhysics().

void RaycastClosestShapeFromActor(NxActor* actor, NxU32 groupFlag)


{

// Get the closest shape
NxShape* closestShape = gScene->raycastClosestShape(ray,
NX_ALL_SHAPES, hit, groupFlag);
….
}

void RaycastAllShapesFromActor(NxActor* actor, NxU32 groupFlag)


{

// Get all shapes
NxU32 nbShapes = gScene->raycastAllShapes(ray, gRaycastReport,
NX_ALL_SHAPES, groupFlag);
}

void RunPhysics()
{
// Cast a ray out of the emitter along its negative x-axis
if (bRaycastClosestShape)
RaycastClosestShapeFromActor(emitter, groupFlagA | groupFlagB);
else
RaycastAllShapesFromActor(emitter, groupFlagA | groupFlagB);
}

11 The User Trigger Report (NxUserTriggerReport)


The User Trigger Report is used to mark shapes as triggers. Trigger shapes are used to
determine when objects enter and leave the shape area. The Trigger Report first appears
in “Lesson 110: Triggers”.

1. The User Trigger Report is created in TriggerReport.h.

class TriggerReport : public NxUserTriggerReport


{
public:
virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape,
NxTriggerFlag status)
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
// Code to process intersection of another shape with the trigger
shape
}
} gTriggerReport;

2. The User Trigger Report is attached to the SDK in InitNx().

void InitNx()
{

// Create the scene
NxSceneDesc sceneDesc;

sceneDesc.userTriggerReport = &gTriggerReport;
gScene = gPhysicsSDK->createScene(sceneDesc);

}

3. A shape is flagged as a trigger for the User Trigger Report in by setting


NX_TRIGGER_ENABLE on its flags in CreateTriggerBox().

// Create a static trigger


NxActor* CreateTriggerBox(const NxVec3& pos, const NxVec3& boxDim)
{
NxActorDesc actorDesc;

NxBoxShapeDesc boxDesc;
boxDesc.dimensions = boxDim;
boxDesc.flags |= NX_TRIGGER_ENABLE;

actorDesc.shapes.pushBack(&boxDesc);
actorDesc.globalPose.t = pos + NxVec3(0, boxDim.y, 0);

return gScene->createActor(actorDesc);
}

12 The User Entity Report (NxUserEntityReport)


The User Entity Report is used to collect information about shapes or triangles that
intersect a particular region of the scene. The Entity Report appears in “Lesson 117:
Intersection Tests”.

1. The User Entity Report is created in EntityReport.h.

class ShapeReport : public NxUserEntityReport<NxShape*>

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
{
public:
virtual bool onEvent(NxU32 nbShapes, NxShape** shapes)
{
while (nbShapes--)
{
// Code to process shapes in the region of the intersection call
}

return true;
}

} gShapeReport;

2. The User Entity Report is passed into its intersection function. The shape
report will return the intersected shapes on the next NxScene::simulate() call.

void RenderIntersectedEntities()
{
switch (gIntersectionTestType)
{
case SPHERE_SHAPE_TEST:
{
...
gScene->overlapSphereShapes(worldSphere, shapeType,
nbShapes, shapes, &gShapeReport, activeGroups, groupsMask);
...
}
break;
...
}

void RenderCallback()
{
...
RenderIntersectedEntities();
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
Lesson 101 – Primary Shape

Introduction
This is the first lesson in the Rigid Body chapter of the PhysX SDK. In this lesson, you
will create an instance of the SDK, create a scene, create four actors (a ground plane, a
capsule, a sphere and a box), simulate the other three actors on the plane actor, and apply
global forces to the box by keyboard action during the simulation.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
1 Initializing the SDK and Creating a Scene
In each of the lessons, you need to create an instance of the PhysX SDK to build your
simulation. This is done in the function InitNx(). The following block of code is all
you need to initialize the PhysX SDK and create a scene.

#include "Lesson101.h"

// Physics SDK globals


NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxVec3 gDefaultGravity(0,-9.8,0);

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;

gScene = gPhysicsSDK->createScene(sceneDesc);
}

void main(int argc, char** argv)


{
InitNx();
}

From main(), we call InitNx(). InitNx() makes an instance of the physics SDK,
gPhysicsSDK, and creates a scene, gScene. This is probably the simplest PhysX
application possible. This will compile and run.

2 Setting Parameters for the SDK


Now we add parameters to the SDK to flesh out our simulation.

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

The first parameter is a global physics parameter used by the SDK called skin width. It
is an important parameter, basically a “grace-depth” the SDK allows objects to penetrate

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
each other by. For now, we are setting it manually to 0.01 (an absolute depth of 0.01m
or 1cm). If we don’t add this line, it will default to 0.025 (0.025m or 2.5cm).

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

These are debug visualization parameters we are setting for the simulation. We are
saying when we switch to Debug Wireframe Mode, we want debug vectors to be 1 meter
in length and we want to see all the collision shapes in the scene and the global poses of
all the actors.

3 Creating the Scene


We have adjusted all the parameters we want to the SDK and added a default material for
objects to be constructed from. Now we create the scene.

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_SW;

gScene = gPhysicsSDK->createScene(sceneDesc);

We start out with a scene descriptor which we feed to the SDK to create our scene.
Descriptors are widely used throughout the SDK. They are structures that contain all the
information you want your object to be created with. You can adjust the descriptor to
taste or leave it alone and the object will be created with the descriptor’s default settings.

In the above case, we want to create a software scene with the gravity vector provided.
To create a software scene, set the simulation type to NX_SIMULATION_SW .

4 Adding Materials to the Scene


You want to give the scene a default physical material that everything will be made of.
A material defines the collision and surface properties of the object the material is
assigned to, that is, how the object will bounce, slide, and roll off other objects.

// Create the default material


NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
This says all objects in the scene are made of a substance with average bounciness
(restitution of 0.5) and average resting and sliding friction (static friction of 0.5 and
dynamic friction of 0.5).

5 Creating the Actors in the Scene


At the end of InitNx(), we add our actors, the box and the plane, to the scene.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();
gSelectedActor = CreateBox();
CreateSphere();
CreateCapsule();

}

6 Creating the Ground Plane


We will be using a ground plane in most of our simulations and we add it here.

void InitNx()
{
...
groundPlane = CreateGroundPlane();
...
}

Look at the function CreateGroundPlane().

NxActor* CreateGroundPlane()
{
// Create a plane with default descriptor
NxPlaneShapeDesc planeDesc;
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&planeDesc);
return gScene->createActor(actorDesc);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
This is probably the simplest actor creation function. The ground plane actor is
initialized with an actor descriptor. The actor descriptor is initialized with the default
plane descriptor, so the ground plane passes through the origin (0,0,0) with normal along
the positive y-axis (0,1,0). The plane has no rigid body assigned to it, so it is a static
actor: it will not move and will push dynamic objects away from it as if it had infinite
mass.

7 Creating the Box


We now create our box. It is a single actor consisting of a single shape.

void InitNx()
{
...
box = CreateBox();
...
}

The CreateBox() function is as follows.

NxActor* CreateBox()
{
// Set the box starting height to 3.5m so box starts off falling onto the ground
NxReal boxStartHeight = 3.5;

// Add a single-shape actor to the scene


NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// The actor has one shape, a box, 1m on a side


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(0.5,0.5,0.5);
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = 10.0f;
actorDesc.globalPose.t = NxVec3(0,boxStartHeight,0);
assert(actorDesc.isValid());
NxActor *pActor = gScene->createActor(actorDesc);
assert(pActor);

// //create actor with no shapes


//NxShape* const *shape = pActor->getShapes();
//NxBoxShape *boxShape = shape[0]->isBox();
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
//assert(boxShape);
//pActor->releaseShape(*boxShape);

return pActor;
}

We are creating an actor in the scene called “box”. To create the actor we need to pass
an actor descriptor defining the actor to NxScene::createActor(). We give actorDesc a
single shape descriptor, boxDesc. boxDesc describes a box that is one meter on a side.
We set its dimensions using half the length, width, and height of the box with
boxDesc.dimensions.set(0.5, 0.5, 0.5). Then we attach a body to the actor, bodyDesc,
give the actor a density of 10, and set its global position to (0,boxStartHeight,0), meaning
the center of the box will be at boxStartHeight = 3.5 meters above the ground at the
beginning of the simulation.

Our actor consists of a single shape, a box 1m on a side, centered at (0,


boxStartHeight,0), and having a density 10. When we call NxScene::createActor(), the
rigid body we attached to the actor, bodyDesc, will be computed given the box shape and
density we have constructed the actor with.

8 Initializing the HUD


We create a heads-up-display object that displays whether or not the scene is running in
hardware, whether or not the scene is paused, as well as additional information as needed
by the tutorial.

// HUD globals
HUD hud;

// Simulation globals

bool bHardwareScene = false;
bool bPause = false;

void InitNx()
{

// Initialize HUD
bHardwareScene = (gScene->getSimType() == NX_SIMULATION_HW);
hud.Update(bHardwareScene, bPause, "");

}

The HUD class and its member variables and functions are defined in HUD.cpp and
HUD.h.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

6
9 Updating the Time
Next we call UpdateTime().

UpdateTime.h

NxReal UpdateTime()
{
NxReal deltaTime;
#ifndef LINUX
static __int64 gTime,gLastTime;
__int64 freq;
QueryPerformanceCounter((LARGE_INTEGER *)&gTime); // Get current count
QueryPerformanceFrequency((LARGE_INTEGER *)&freq); // Get processor freq
deltaTime = (double)(gTime - gLastTime)/(double)freq;
gLastTime = gTime;
#else
struct timeval tv;
static struct timeval lasttv = { 0 , 0 };
if (lasttv.tv_usec == 0 && lasttv.tv_sec == 0)
gettimeofday(&lasttv, NULL);
gettimeofday(&tv, NULL);
deltaTime = (tv.tv_usec - lasttv.tv_usec)/1000000.f
+ (tv.tv_sec - lasttv.tv_sec);
lasttv = tv;
#endif
return deltaTime;
}

Lesson101.h

void InitNx()
{
...
// Get the current time
UpdateTime();
...
}

This sets gTime to the current time. Every frame we call UpdateTime() to get
deltaTime, the amount of time that has passed since we last rendered the scene and
therefore the amount of time we need to advance the simulation to render the current
scene.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
10 Starting the First Frame of the Simulation
Finally, we call StartPhysics() to start the first frame of the simulation.

void InitNx()
{
...
// Start the first frame of the simulation
if (gScene) StartPhysics();
}

void StartPhysics()
{
// Update the time step
gDeltaTime = UpdateTime();

// Start collision and dynamics for delta time since the last frame
gScene->simulate(gDeltaTime);
gScene->flushStream();
}

11 Getting the Results of the Simulation


We have initialized the SDK and created our scene with InitNx(). Now we call
glutMainLoop() from main().

int main(int argc, char** argv)


{
PrintControls();
InitGlut(argc, argv);
InitNx();
glutMainLoop();
ReleaseNx();
return 0;
}

The program will stay in glutMainLoop() until the user ends the simulation. After a
scene is rendered, the RenderCallback() callback function gets called where we set up the
next scene. At the beginning of RenderCallback(), we call NxScene::fetchResults() from
GetPhysicsResults() to get the results of our physics simulation since our last call to
NxScene::simulate() from StartPhysics().

void RenderCallback()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

8
{
...
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

}

void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}

We call NxScene::fetchResults(NX_RIGID_BODY_FINISHED, false) to close out the


simulation started on the last call to NxScene::simulate(). The
“NX_RIGID_BODY_FINISHED” parameter specifies that we want to close out the
simulation of rigid bodies for this time step. The “false” parameter specifies that the call
is non-blocking. We will not block waiting for the rigid body simulation to finish. If
we put “true” here, instead:

void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
gScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
}

...the NxScene::fetchResults() call will not return at all until the simulation is finished.
With our call, we continually query to see if the simulation is finished and don't break out
of the loop until the funciton returns true. We could use this space to execute additional
code in case the physics for the frame is not yet finished.

void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false))
{
// Execute additional code here for rendering, AI, etc.,
// while waiting for the rigid body simulation to finish
...
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

9
12 Processing Inputs to the Simulation
We then call ProcessInputs() to collect all physics inputs to the new scene and set up any
callbacks for the new scene. Here we apply forces to the box, set the position of the box,
and set up callbacks, namely the user debug renderer, for the next simulation step.

void RenderCallback()
{
...
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

}
...
void ProcessInputs()
{
ProcessForceKeys();

// Show debug wireframes


if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

1. Selecting Actors
Press ‘r’ to call SelectNextActor to select other actor. The selected actor is rendered in
wireframe mode. The actor with NX_TRIGGER_ENABLE flag and static actor can’t be
selected.

void SelectNextActor()
{
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
for(NxU32 i = 0; i < nbActors; i++)
{
if (actors[i] == gSelectedActor)
{
NxU32 j = 1;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

10
gSelectedActor = actors[(i+j)%nbActors];
while (!IsSelectable(gSelectedActor))
{
j++;
gSelectedActor = actors[(i+j)%nbActors];
}
break;
}
}
}

2. Applying Forces to the Actors


Our first call is to ProcessForceKeys(), where we call ApplyForceToActor() on our actor.

// Force globals
NxVec3 gForceVec(0,0,0);
NxReal gForceStrength = 20000;
bool bForceMode = true;

// Keyboard globals
#define MAX_KEYS 256
bool gKeys[MAX_KEYS];

void ProcessForceKeys()
{
// Process force keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{

// Force controls

case 'j': {gForceVec = ApplyForceToActor(gSelectedActor, NxVec3(1,0,0),
gForceStrength); break; }

}
}
}
...
void ProcessInputs()
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

11
ProcessForceKeys();
...
}

ApplyForceToActor() calls NxActor::addForce() to add forces to the box through


keyboard input.

NxVec3 ApplyForceToActor(NxActor* actor, const NxVec3& forceDir, const NxReal


forceStrength)
{
NxVec3 forceVec = forceStrength*forceDir*gDeltaTime;
actor->addForce(forceVec);
return forceVec;
}

Every time we hit “j”, we call box->addForce(NxVec3(gForceStrength,0,0)) and a force


of gForceStrength = 20000 is applied continuously to the box at its center along the
positive x-axis each frame. Note, when running the simulation, you start out facing
down the positive z-axis of the scene marked in blue. The positive y-axis is in green
going up and the positive x-axis is in red going to the left. All axes in the SDK are set
up and rendered in this manner. “j” now imparts a force that pushes the box to the left.
The force is shown as a yellow arrow. Note using the “umijkl” buttons, the force is
constant, applied at the center of the box, and always faces along the global x, y, or z-
axis.

3. Resetting the Position of the Actors


Next we call NxActor::setGlobalPosition() to set the position of the box in
ProcessKeys().

// Keyboard globals
#define MAX_KEYS 256
bool gKeys[MAX_KEYS];

void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{

// Return box to (0,5,0)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

12
case 't': { gSelectedActor->setGlobalPosition(NxVec3(0,5,0)); break; }

}
}
}

Every time we hit “t”, we call NxActor::setGlobalPosition(NxVec3(0,5,0)) on our box


actor. This places the actor at a position 5 units above the origin.

As with adding forces, we need to do this in between the NxScene::fetchResults() call in


GetPhysicsResults() and the NxScene::simulate() call in StartPhysics()). It is an input to
the physics scene which we need to perform while the scene is not being calculated by
the hardware.

4. Debugging the Objects


After processing possible calls to add forces to and set the position of the box, we call the
following code.

void ProcessInputs()
{

// Show debug wireframes
if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

Hitting “b” in the demo toggles these wireframes on and off. The call
gDebugRenderer.renderData() renders wireframes in the scene that we set up to visualize
in InitNx() in order to see the collision shapes and actor axes in the scene. You can see
the DebugRenderer::renderData() function in DebugRenderer.cpp.

void DebugRenderer::renderData(const NxDebugRenderable& data) const


{
glLineWidth(1.0f);

glPushMatrix();
glDisable(GL_LIGHTING);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Render points
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

13
NxU32 NbPoints = data.getNbPoints();
if(NbPoints)
{
...
}

// Render lines
NxU32 NbLines = data.getNbLines();
if(NbLines)
{
...
}

// Render triangles
NxU32 NbTris = data.getNbTriangles();
if(NbTris)
{
...
}

// Reset the color


glColor3f(1,1,1);

glEnable(GL_LIGHTING);
glPopMatrix();
}

Note that drawing these debug wireframes is for debug mode only. The drawing is not
optimal because it is not done in parallel with the physics simulation, i.e., the physics
simulation is idle when you do the debug rendering.

For more information on the Debug Renderer, see “Lesson 301: Debug Renderer”.

13 Starting the Simulation


Finally, we call StartPhysics() to start the next frame of the simulation.

void RenderCallback()
{
...
if (gScene && !bPause)
{
...
StartPhysics();
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

14

}
...
void StartPhysics()
{
// Update the time step
gDeltaTime = UpdateTime();

// Start collision and dynamics for delta time since the last frame
gScene->simulate(gDeltaTime);
gScene->flushStream();
}

StartPhysics() calls UpdateTime(), which gets deltaTime, the time step since the last
scene. We call NxScene::simulate(deltaTime) and NxScene::flushStream() to run the
simulation for the time step.

NxScene::simulate() is point of entry to the PhysX physics solver and the heart of the
simulation. The SDK simulates the scene we have constructed for deltaTime. All
computation for the simulation takes place on the hardware and gets retrieved by the next
call to NxScene::fetchResults() which we call from GetPhysicsResults().

14 Drawing the Objects


After we call GetPhysicsResults(), ProcessInputs(), and StartPhysics()in
RenderCallback(), we draw the objects in the scene from gCameraPos (0,5,-15) facing
gCameraForward (0,0,1), so 5 meters above and 15 meters behind the origin along the z-
axis, and facing the origin along positive z. We draw the actors and their shadows on the
ground plane with the following code.

void RenderActors(bool shadows)


{
// Render all the actors in the scene
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;
DrawActor(actor);

// Handle shadows
if (shadows)
{
DrawActorShadow(actor);
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

15
}
}

void RenderCallback()
{

RenderActors(bShadows);

}

DrawActor() and DrawActorShadow() loop through all the shapes in the actor, drawing
each shape and its shadow. The code for drawing the different types of shapes is in
DrawShapes.cpp. You can look at it, but it’s not important for the lesson right now, just
know this is where the actors get drawn.

15 Drawing the Forces


The force that gets applied to the box is returned to gForceVec. In RenderCallback(), we
call DrawForce() to draw an arrow representing the force applied to the box.

void ProcessForceKeys ()
{
// Process force keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{

// Force controls
case 'i': {gForceVec = ApplyForceToActor(gSelectedActor, NxVec3(0,0,1),
gForceStrength); break; }
case 'k': {gForceVec = ApplyForceToActor(gSelectedActor, NxVec3(0,0,-1),
gForceStrength); break; }
case 'j': {gForceVec = ApplyForceToActor(gSelectedActor, NxVec3(1,0,0),
gForceStrength); break; }
case 'l': {gForceVec = ApplyForceToActor(gSelectedActor, NxVec3(-1,0,0),
gForceStrength); break; }
case 'u': {gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(0,1,0),
gForceStrength); break; }
case 'm': {gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(0,-1,0),
gForceStrength); break; }
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

16
}
...
void ProcessInputs()
{
ProcessForceKeys();

}
...
void DrawForce(NxActor* actor, NxVec3& forceVec, const NxVec3& color)
{
// Draw only if the force is large enough
NxReal force = forceVec.magnitude();
if (force < 0.1f) return;

forceVec = 3*forceVec/force;

NxVec3 pos = actor->getCMassGlobalPosition();


DrawArrow(pos, pos + forceVec, color);
}
...
void RenderCallback()
{
...
if (gScene && !bPause)
{
...
ProcessInputs();
...
}
...
DrawForce(box, gForceVec, NxVec3(1,1,0));
gForceVec = NxVec3(0,0,0);

}

This code draws a yellow arrow originating from the center of mass of the box and
extending 3 meters in the direction of the axis-aligned force.

16 Resetting the Scene and Shutting Down


Run the simulation for a while and hit F10. You will notice the box appears back in its
starting position, falling to the ground as before. Hitting F10 resets the scene, calling
ResetNx() which calls ReleaseNx() to shut down the SDK and then InitNx() to start it up
again.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

17
// Physics SDK globals
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;

void SpecialCallback(int key, int x, int y)
{
switch (key)
{
// Reset PhysX
case GLUT_KEY_F10: ResetNx(); return;
}
}

void ReleaseNx()
{
if (gScene)
{
GetPhysicsResults(); // Make sure to fetchResults() before shutting down
gPhysicsSDK->releaseScene(*gScene);
}
if (gPhysicsSDK) gPhysicsSDK->release();
}

void ResetNx()
{
ReleaseNx();
InitNx();
}

ReleaseNx() calls GetPhysicsResults() which calls NxScene::fetchResults(), which waits


for the card to finish processing the scene. It then calls NxPhysicsSDK::releaseScene()
which deletes all the objects in the scene and then deletes the scene itself. It then calls
NxPhysicsSDK::release() which shuts down the SDK.

ReleaseNx() is also called after glutMainLoop() to shut down the SDK before exiting.

17 Major Application Functions


The major functions of interest in the application are:

1. RenderCallback()

Calls GetPhysicsResults(), ProcessInputs(), and StartPhysics() each frame to simulate


the scene. Places the camera with SetupCamera(). Draws the actors in the scene

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

18
with RenderActors(). Draws the user supplied force to objects in the scene with
DrawForce().

2. GetPhysicsResults()

Calls NxScene::fetchResults() to retrieve the results of the last frame of simulation.

3. ProcessInputs()

Gets keyboard input from ProcessForceKeys(). Calls


NxDebugRenderer::renderData() to draw debug wireframes on the actors.

4. StartPhysics()

Calls UpdateTime() to get gDeltaTime, the time since the last frame of the scene was
rendered. Calls NxScene::simulate(gDeltaTime) and NxScene::flushStream() to run
the simulation for gDeltaTime.

5. InitNx()

Initializes the PhysX SDK with NxCreatePhysicsSDK(). Adds simulation-wide


parameters with NxPhysicsSDK::setParameter(). Adds new materials to the SDK by
calling NxScene::getMaterialFromIndex() and adjusting the material properties using
NxMaterial::setRestitution(), ::setStaticFriction(), and ::setDynamicFriction().
Creates the scene with NxPhysicsSDK::createScene(). Constructs the actors in the
scene with CreateGroundPlane() and CreateBox(), both of which call
NxScene::createActor(). Sets the current time with UpdateTime(). Begins the
simulation with StartPhysics().

6. gDebugRenderer.renderData()

Renders wireframes to visualize debug wireframes on the actors such as collision


shapes and actor axes.

7. ReleaseNx()

Calls GetPhysicsResults() to close out the simulation. Calls


NxPhysicsSDK::releaseScene() to shut down the scene and NxPhysicsSDK::release()
to shut down the SDK.

18 Conclusion and Playing Around


You have completed a basic application using the PhysX SDK. Congratulations! You
have taken your first step into a larger world. Experiment as much as you can with your

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

19
creation. Use the “umijkl” keys to push the box around the plane and use the “qzwasd”
keys to follow it around with the camera.

There is a lot of information in this lesson and it is fairly comprehensive as it covers an


entire working application with everything from initializing the SDK, initializing the
scene, creating an actor, applying forces to the actor through keyboard inputs, drawing
the actor, running the simulation, drawing forces applied to the actor, and drawing debug
wireframes for the objects. Don’t panic if there are some things you don’t understand
right now. Everything will become more apparent with practice as you continue to use
the SDK.

In future lessons, we will use the same framework as this lesson and introduce one or two
new features per lesson until we have covered all the features in the SDK.

19 Related Classes, Functions, and Parameters


NxCreatePhysicsSDK()

NxPhysicsSDK
createScene()
setParameter()
visualize()
releaseScene()
release()

NxMaterial
setRestitution()
setStaticFriction()
setDynamicFriction()

NxSceneDesc
gravity
simType

NxScene
getMaterialFromIndex()
createActor()
simulate()
flushStream()
fetchResults()

NxPlaneShapeDesc

NxBoxShapeDesc
Dimensions
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

20
NxSphereShapeDesc
radius

NxCapsuleShapeDesc
radius
height

NxBodyDesc

NxActorDesc
shapes
body
density
globalPose

NxActor
setGlobalPosition()
getCMassGlobalPosition()
addForce()

NxDebugRenderable
getNbPoints()
getPoints()
getNbLines()
getLines()
getNbTriangles()
getTriangles()

NxDebugPoint
color
p

NxDebugLine
color
p0
p1

NxDebugTriangle
color
p0
p1
p2

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

21
NxParameter
NX_SKIN_WIDTH
NX_VISUALIZE_COLLISION_SHAPES
NX_VISUALIZE_ACTOR_AXES

NxSimulationStatus
NX_RIGID_BODY_FINISHED

21 Relationship between localPose and globalPose


In CreateBox, CreateSphere and CreateCapsule functions, there are two variables:
localPose and globalPose need to be noted. See the following snippet:

NxCapsuleShapeDesc capsuleDesc;
capsuleDesc.radius = 0.55f;
capsuleDesc.height = 0.75f;
capsuleDesc.localPose.t = NxVec3(0, 0, 0);

//Rotate capsule shape


NxQuat quat(45, NxVec3(0, 0, 1));
NxMat33 m33(quat);
capsuleDesc.localPose.M = m33;

actorDesc.shapes.pushBack(&capsuleDesc);
actorDesc.body = &bodyDesc;
actorDesc.density = 10.0f;
actorDesc.globalPose.t =NxVec3(6.0f,capsuleStartHeight,0);

Here, globalPose is the coordinate in world coordinate System; localPose is the


coordinate based on globalPose. See the below illustration:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

22
Y Y’
Z

Z’

localPose

X’
globalPose

X
World Coordinate System

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

23
Lesson 102 – Mesh Shapes

1 Introduction
In this lesson, we learn how to create convex mesh and triangle mesh shapes. Generally,
Convex mesh is used to create dynamic and static actors; triangle mesh is used to create
static actor. The initialization process is the same as Lesson 101. Here, we mainly
illustrate the two functions: CreateConvexMesh() and CreateTriangleMesh()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Creating Convex Mesh
Convex mesh is meant to satisfy the need of using shapes that are more complex than
primary shape, yet simpler and more efficient than the general mesh-mesh collision
models. The maximum number of polygons for a convex mesh is limited to 256.

NxActor* CreateConvexMesh()
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

NxVec3 boxDim(1.5,0.75,1.5);
// Pyramid
NxVec3 verts[8] =
{ NxVec3(boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, -boxDim.y, boxDim.z),
NxVec3(boxDim.x, -boxDim.y, boxDim.z),
NxVec3(boxDim.x*0.5, boxDim.y, -boxDim.z*0.5),
NxVec3(-boxDim.x*0.5, boxDim.y, -boxDim.z*0.5),
NxVec3(-boxDim.x*0.5, boxDim.y, boxDim.z*0.5),
NxVec3(boxDim.x*0.5, boxDim.y, boxDim.z*0.5)
};

// Create descriptor for convex mesh


if (!convexDesc)
{
convexDesc = new NxConvexMeshDesc();
assert(convexDesc);
}
convexDesc->numVertices = 8;
convexDesc->pointStrideBytes = sizeof(NxVec3);
convexDesc->points = verts;
convexDesc->flags = NX_CF_COMPUTE_CONVEX;

NxConvexShapeDesc convexShapeDesc;
convexShapeDesc.localPose.t = NxVec3(0,boxDim.y,0);
convexShapeDesc.userData = convexDesc;

// Two ways on cooking mesh: 1. Saved in memory, 2. Saved in file


NxInitCooking();
// Cooking from memory
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
MemoryWriteBuffer buf;
bool status = NxCookConvexMesh(*convexDesc, buf);
//
// Please note about the created Convex Mesh, user needs to release it when no
//one uses it to save memory. It can be detected
// by API "meshData->getReferenceCount() == 0". And, the release API is
//"gPhysicsSDK->releaseConvexMesh(*convexShapeDesc.meshData);"

NxConvexMesh *pMesh = gPhysicsSDK-


>createConvexMesh(MemoryReadBuffer(buf.data));
assert(pMesh);
convexShapeDesc.meshData = pMesh;
NxCloseCooking();

if (pMesh)
{
// Save mesh in userData for drawing.
pMesh->saveToDesc(*convexDesc);
//
NxActorDesc actorDesc;
assert(convexShapeDesc.isValid());
actorDesc.shapes.pushBack(&convexShapeDesc);
actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;

actorDesc.globalPose.t = NxVec3(0.0f, 0.0f, 0.0f);


assert(actorDesc.isValid());
NxActor* actor = gScene->createActor(actorDesc);
assert(actor);
return actor;
}

return NULL;
}

3 Creating Triangle Mesh


A triangle mesh is helpful in creating more complex shapes such as terrain etc. When
creating triangle mesh, there are 2 notes to be paid more attention. One is that triangles’
normal is not passed explicitly and is assumed to be (v1-v0) x (v2-v0). We make sure that
we define face normal in the direction we expect Collision detection will only work
correctly. The other is that we mustn’t duplicate identical vertices. If we create two
copies of the vertex, the collision detection code won’t know that it is actually the same
vertex which leads to decrease performance and unreliable results.

NxActor* CreateTriangleMesh()
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
{
NxVec3 boxDim(1.0f, 1.0f, 1.0f);
// Supply hull

NxVec3 verts[8] =
{ NxVec3(-boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, boxDim.y, -boxDim.z),
NxVec3(boxDim.x, boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, -boxDim.y, boxDim.z),
NxVec3(boxDim.x, -boxDim.y, boxDim.z),
NxVec3(-boxDim.x, boxDim.y, boxDim.z),
NxVec3(boxDim.x, boxDim.y, boxDim.z),
};

// Triangles is 12*3
NxU32 indices[12*3] =
{ 1,2,3,
0,2,1,
5,4,1,
1,4,0,
1,3,5,
3,7,5,
3,2,7,
2,6,7,
2,0,6,
4,6,0,
7,4,5,
7,6,4
};

NxU16 indices16[12*3] =
{ 1,2,3,
0,2,1,
5,4,1,
1,4,0,
1,3,5,
3,7,5,
3,2,7,
2,6,7,
2,0,6,
4,6,0,
7,4,5,
7,6,4
};
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
// Create descriptor for triangle mesh
if (!triangleMeshDesc)
{
triangleMeshDesc = new NxTriangleMeshDesc();
assert(triangleMeshDesc);
}
triangleMeshDesc->numVertices = 8;
triangleMeshDesc->pointStrideBytes = sizeof(NxVec3);
triangleMeshDesc->points = verts;
triangleMeshDesc->numTriangles = 12;
triangleMeshDesc->flags = 0;
triangleMeshDesc->triangles = indices;
triangleMeshDesc->triangleStrideBytes = 3 * sizeof(NxU32);

//Alternative: see NxMeshFlags


//triangleMeshDesc->flags = NX_MF_16_BIT_INDICES
//triangleMeshDesc->triangles = indices16;
//triangleMeshDesc->triangleStrideBytes = 3 * sizeof(NxU16);

// The actor has one shape, a triangle mesh


NxInitCooking();
MemoryWriteBuffer buf;

bool status = NxCookTriangleMesh(*triangleMeshDesc, buf);


NxTriangleMesh* pMesh;
if (status)
{
pMesh = gPhysicsSDK-
>createTriangleMesh(MemoryReadBuffer(buf.data));
}
else
{
assert(false);
pMesh = NULL;
}
NxCloseCooking();
// Create TriangleMesh above code segment.

NxTriangleMeshShapeDesc tmsd;
tmsd.meshData = pMesh;
tmsd.userData = triangleMeshDesc;
tmsd.localPose.t = NxVec3(0, boxDim.y, 0);
tmsd.meshPagingMode = NX_MESH_PAGING_AUTO;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

assert(tmsd.isValid());
actorDesc.shapes.pushBack(&tmsd);
//Dynamic triangle mesh don't be supported anymore. So body = NULL
actorDesc.body = NULL;
actorDesc.globalPose.t = NxVec3(3.0f, 0.0f, 0.0f);

if (pMesh)
{
// Save mesh in userData for drawing
pMesh->saveToDesc(*triangleMeshDesc);
//
assert(actorDesc.isValid());
NxActor *actor = gScene->createActor(actorDesc);
assert(actor);

return actor;
}

return NULL;
}

4 Conclusion
We should now know how to create Convex mesh and Triangle mesh.

5 Related Classes, Functions, and Parameters


NxTriangleMeshDesc
NxConvexMeshDesc
NxTriangleMeshShapeDesc
NxConvexShapeDesc
NxInitCooking
NxCookConvexMesh
NxCookTriangleMesh
createConvexMesh
createTriangleMesh
NxCloseCooking

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Lesson 103 – Compound

1 Introduction
The SDK is initialized with the same parameters as the previous two lessons. The scene
is initialized with the same default material and ground plane. InitNx() now creates two
actors which are composed of multi shapes. Refer to two main functions:
CreatePrimaryMultiShape() and CreateMeshMultiShape().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 CreatePrimaryMultiShape() & CreateMeshMultiShape()
Actually, compound is such an actor that multi different shapes or same shapes are
assembled together and each shape still remains its own characteristic.

NxActor* CreatePrimaryMultiShape()
{
// Create an actor which is composed of Box, Capsule and Sphere.
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

//Box Shape
NxBoxShapeDesc boxShape;
boxShape.dimensions = NxVec3(1.0f, 1.0f, 1.0f);
boxShape.localPose.t = NxVec3(1.5f, 0.0f, 0.0f);
assert(boxShape.isValid());
actorDesc.shapes.pushBack(&boxShape);

//Capsule Shape
NxCapsuleShapeDesc capsuleDesc;
capsuleDesc.radius = 0.8f;
capsuleDesc.height = 1.0f;
capsuleDesc.localPose.t = NxVec3(0.0f, 0.0f, 0.0f);
//Rotate capsule 90 degree around z axis
NxQuat quat(90.0f, NxVec3(0, 0, 1));
NxMat33 m;
m.id();
m.fromQuat(quat);
capsuleDesc.localPose.M = m;
assert(capsuleDesc.isValid());
actorDesc.shapes.pushBack(&capsuleDesc);

//Sphere Shape
NxSphereShapeDesc sphereDesc;
sphereDesc.radius = 1.0f;
sphereDesc.localPose.t = NxVec3(-1.5f, 0.0f, 0.0f);
assert(sphereDesc.isValid());
actorDesc.shapes.pushBack(&sphereDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;
actorDesc.globalPose.t = NxVec3(5.0f, 0.0f, 0.0f);
assert(actorDesc.isValid());
NxActor *pActor = gScene->createActor(actorDesc);
assert(pActor);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
return pActor;
}

NxActor* CreateMeshMultiShape()
{
// Create an actor which is composed of Convex and Sphere.

NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

//Convex Shape
NxVec3 boxDim(2.0f, 2.0f, 2.0f);

NxVec3 verts[8] =
{ NxVec3(boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, -boxDim.y, -boxDim.z),
NxVec3(-boxDim.x, -boxDim.y, boxDim.z),
NxVec3(boxDim.x, -boxDim.y, boxDim.z),
NxVec3(boxDim.x*0.5, boxDim.y, -boxDim.z*0.5),
NxVec3(-boxDim.x*0.5, boxDim.y, -boxDim.z*0.5),
NxVec3(-boxDim.x*0.5, boxDim.y, boxDim.z*0.5),
NxVec3(boxDim.x*0.5, boxDim.y, boxDim.z*0.5)
};

// Create descriptor for convex mesh


if (!convexDesc)
convexDesc = new NxConvexMeshDesc();
assert(convexDesc);
convexDesc->numVertices = 8;
convexDesc->pointStrideBytes = sizeof(NxVec3);
convexDesc->points = verts;
convexDesc->flags = NX_CF_COMPUTE_CONVEX;

NxConvexShapeDesc convexShapeDesc;
convexShapeDesc.localPose.t = NxVec3(0,boxDim.y,0);
convexShapeDesc.userData = convexDesc;

NxInitCooking();
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookConvexMesh(*convexDesc, buf);
//

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
// Please note about the created Convex Mesh, user needs to release it when no
//one uses it to save memory. It can be detected
// by API "meshData->getReferenceCount() == 0". And, the release API is
//"gPhysicsSDK->releaseConvexMesh(*convexShapeDesc.meshData);"
//
NxConvexMesh *pMesh = gPhysicsSDK-
>createConvexMesh(MemoryReadBuffer(buf.data));
assert(pMesh);
convexShapeDesc.meshData = pMesh;
NxCloseCooking();

if (pMesh)
{
// Save mesh in userData for drawing.
pMesh->saveToDesc(*convexDesc);
//
assert(convexShapeDesc.isValid());
actorDesc.shapes.pushBack(&convexShapeDesc);
}

//Sphere Shape
NxSphereShapeDesc sphereDesc;
sphereDesc.radius = 1.8f;
sphereDesc.localPose.t = NxVec3(0.0f, 5.4f, 0.0f);
assert(sphereDesc.isValid());
actorDesc.shapes.pushBack(&sphereDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;
actorDesc.globalPose.t = NxVec3(-5.0f, 0.0f, 0.0f);
assert(actorDesc.isValid());
NxActor *pActor = gScene->createActor(actorDesc);
assert(pActor);

return pActor;
}

3 Conclusion
We should now know how to create compound.

4 Related Classes, Functions, and Parameters


NxCapsuleShapeDesc
height
radius

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
localPose

NxBoxShapeDesc
dimensions
localPose

NxSphereShapeDesc
radius
localPose

NxConvexMeshDesc

NxConvexShapeDesc

NxActor
globalPose

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 104 – Actors

1 Introduction
In this lesson, we learn how to create three types of actors: dynamic, kinematic and static.

2 Dynamic, Kinematic & Static


Dynamic and kinematic actors both belong to dynamic actors. Kinematic actor can only
be transferred from one position to another position and can’t be moved by applying
force to it.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
Create a static actor by setting the body member to NULL in the actor descriptor. Once
static actor is created, do not do anything with it. i.e. changing position, adding more
shapes and deleting etc.

NxActor* CreateDynamicActor()
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

//
NxBoxShapeDesc boxDesc;
boxDesc.dimensions = NxVec3(1.5f, 1.5f, 1.5f);
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.density = 1.0f;
actorDesc.body = &bodyDesc;
actorDesc.globalPose.t = NxVec3(3.0f, 0.0f, 0.0f);

NxActor *pActor = gScene->createActor(actorDesc);


assert(pActor);

return pActor;
}

NxActor* CreateKinematicActor()
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;
bodyDesc.flags |= NX_BF_KINEMATIC;
//
NxCapsuleShapeDesc capsuleDesc;
capsuleDesc.radius = 1.0f;
capsuleDesc.height = 1.5f;
actorDesc.shapes.pushBack(&capsuleDesc);

actorDesc.density = 1.0f;
actorDesc.body = &bodyDesc;
actorDesc.globalPose.t = NxVec3(0.0f, 2.0f, 0.0f);

NxActor *pActor = gScene->createActor(actorDesc);


assert(pActor);
//pActor->raiseBodyFlag(NX_BF_KINEMATIC);

return pActor;
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
NxActor* CreateStaticActor()
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

//
NxSphereShapeDesc sphereDesc;
sphereDesc.radius = 1.5f;
actorDesc.shapes.pushBack(&sphereDesc);

actorDesc.body = NULL;
actorDesc.globalPose.t = NxVec3(-3.0f, 3.0f, 0.0f);

NxActor *pActor = gScene->createActor(actorDesc);


assert(pActor);

return pActor;
}

3 Conclusion
We should now know how to create different actors.

4 Related Classes, Functions, and Parameters


NxBodyFlag
NX_BF_KINEMATIC

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 105 – Materials

1 Introduction
In this lesson we learn about object materials and their properties of restitution and
friction.

2 Materials
Materials are the physical substance objects are made of. They define the interior
properties of the object, restitution, as well as the surface properties of the object, friction.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
Both material components are linked to the physical concept of friction. The lower the
restitution is, the more energy will be lost to heat and the less it will rebound from an
impact. The higher the restitution, the less energy it will lose, and the more it will reflect
from an impact. How much energy is lost when the object slides along another surface?
The higher the surface friction of an object, the more energy will be lost when sliding, the
more the object will brake. An object with a low surface friction will lose less energy
sliding and tend to glide across a surface unrestricted.

3 Restitution
If you look at InitNx(), you will see that we create a compound which consists of three
different shapes: Box, Sphere and Capsule. Each shape is attached a different material.
Run the lesson. Hit “p” to un-pause the simulation and drop the compound from its
starting height. Now look at the code in that creates the default material.

void InitNx()
{

// Create the default material
NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);

}

Change the restitution of the default material to 0.25.

defaultMaterial->setRestitution(0.25);

Now drop the compound. Notice that the compound rebounds much less than before,
not even reaching the height of the stack.
Now change it to 0.75.

defaultMaterial->setRestitution(0.75);

Run the lesson and drop the compound.

4 Static Friction
Set the default material’s restitution back to 0.5 and change the default material’s static
friction to 0.95.

defaultMaterial->setStaticFriction(0.95);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Drop the compound and wait for it to stop bouncing. Try to move it with “umijkl”.
Notice how difficult it is to start the object moving from a stop with the high static
friction, but once you get it moving, it slides fairly easily. This is static friction. It is
the stopping force against the object when it’s resting against a surface, basically how
well the object sticks to the ground or other surface when it’s not moving. Notice that
0.95 is sticky enough that box occasionally prefers to roll on its edge rather than slide.

5 Dynamic Friction
Set the default material’s static friction back to 0.5 and change its dynamic friction to 0.1.

defaultMaterial->setDynamicFriction(0.1);

The compound glides very easily over the plane with the low dynamic friction.
Dynamic friction is a stopping force against the object while it’s moving along the
ground or other surface.

6 Material Combine Modes


All objects in our scene are made of the same material. When objects made of two
different materials collide, their restitutions and frictions are averaged by default. You
can change this by changing the combine mode on the material to get it to react to other
objects' materials in different ways.

There is a Friction Combine Mode field and a Restitution Combine Mode field on each
material. The two fields can each be set to one of the following:

NX_CM_AVERAGE - average the values (default)


NX_CM_MIN – take the minimum of the values
NX_CM_MULTIPLY - multiply the values
NX_CM_MAX – take the maximum of the values

Only one combine mode from one material will be used in a collision. If the combine
modes on the objects differ, the higher priority combine mode is used. The order of
priority of combine modes from lowest to highest is:

NX_CM_AVERAGE
NX_CM_MIN
NX_CM_MULTIPLY
NX_CM_MAX

So, for example, the lowest priority combine mode is the default combine mode,
NX_CM_AVERAGE, so any other combine mode set on another object will override it.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
To set a friction combine mode on a material, use
NxMaterial::setFrictionCombineMode(). To set a restitution combine mode on a
material, use NxMaterial::setRestitutionCombineMode().

7 Friction Scaling
You can scale the magnitude of static and dynamic friction of all your materials at once
using the NX_DYN_FRICT_SCALING and NX_STA_FRICT_SCALING parameters.
To make use of them, you need to add them to InitNx().

void InitNx()
{

gPhysicsSDK->setParameter(NX_DYN_FRICT_SCALING, 0.5);
gPhysicsSDK->setParameter(NX_STA_FRICT_SCALING, 0.5);

}

Setting these two parameters this way will halve the static and dynamic friction values on
all materials in the simulation (the default for both parameters is 1). An example use for
this scaling might be, in a racing game, gradually make all materials slippery if
conditions become wet or icy, then gradually return them to their default values once
conditions return to normal.

8 Bounce Threshold
Last in this lesson is the bounce threshold parameter. Set this in InitNx().

gPhysicsSDK->setParameter(NX_BOUNCE_THRESHOLD, -100);

Drop the compound. It doesn’t bounce. All objects with a relative velocity below the
bounce threshold will not bounce (it is negative because the velocity is a relative velocity,
basically a closing velocity between the object and what it’s colliding with.)

This parameter is useful for stopping objects prone to bouncing repeatedly until there is
little visible or physical result from their bouncing, like a basketball vibrating on the
ground after it has been dropped. These are usually high-restitution objects.

9 Conclusion
You should now know how to set up a material in the SDK, how to use the material
parameters of restitution and friction, how to use the restitution and friction combine
modes of materials, how to scale the friction on materials globally, and how to set a lower
limit on the “bounce velocity” of your objects.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
10 Related Classes, Functions, and Parameters
NxMaterial
setRestitution()
setStaticFriction()
setDynamicFriction()
setFrictionCombineMode()
setRestitutionCombineMode()

NxCombineMode
NX_CM_AVERAGE
NX_CM_MIN
NX_CM_MULTIPLY
NX_CM_MAX

NxParameter
NX_DYN_FRICT_SCALING
NX_STA_FRICT_SCALING
NX_BOUNCE_THRESHOLD

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 106 – Putting Actors to Sleep

1 Introduction
Putting actors to sleep properly is crucial in a physics simulation of any complexity. If
at all possible, we want only an isolated window of the simulation running where the
action is happening and the actors are awake. All other actors are asleep. There are two
important parameters that directly affect when objects fall asleep: the default sleep linear
velocity and default sleep angular velocity. There is also a parameter, skin width, which
indirectly affects the time it takes for objects to fall asleep. Skin width is used primarily
to determine how far objects can interpenetrate each other. Object penetration depth

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
directly affects how “reactive” objects are to collision and hence how easily they are
awakened.

2 Stack of Boxes
Look at the new InitNx() and the CreateStack() function it calls.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* sphere = NULL;
NxActor** boxes = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

void RenderCallback()
{
...
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}
...
}

NxActor** CreateStack(const NxVec3& pos, const NxVec3& stackDim, const
NxVec3& boxDim, NxReal density)
{
NxActor** stack = new NxActor*[8*stackDim.x*stackDim.y*stackDim.z];

NxVec3 offset = NxVec3(boxDim.x,0,boxDim.z) + pos;

for (int i = -stackDim.x; i < stackDim.x; i++)


{
for (int j = 0; j < stackDim.y; j++)
{
for (int k = -stackDim.z; k < stackDim.z; k++)
{
NxVec3 boxPos =
NxVec3(i*boxDim.x*2,j*boxDim.y*2,k*boxDim.z*2);
*stack++ = CreateBox(boxPos + offset, boxDim, density);
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
}

return stack;
}

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

sphere = CreateSphere(NxVec3(0,6,0), 1, 50);


sphere->setSleepLinearVelocity(0.15*0.15);
sphere->setSleepAngularVelocity(0.14*0.14);
boxes = CreateStack(NxVec3(0,0,0), NxVec3(6,3,6), NxVec3(0.25,0.25,0.25), 10);

gSelectedActor = sphere;

}

Note the variable bPause is set to true so that the physics loop isn’t called until you un-
pause the simulation hitting “p”. We have a large wrecking ball suspended above a
stack of boxes, so when you un-pause the simulation with “p”, the ball will fall and crash
into the boxes.

CreateStack() makes a rectangular stack of boxes, each 0.25x0.25x0.25, 12 across, 12


deep, and 3 high. Note you can change these parameters to make different size stacks
with different size boxes.

3 Sleeping Boxes
Take a look at how the actors in the scene are now drawn in RenderActors().

void DrawActorBlueSleep(NxActor *actor)


{
NxShape** shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();

if (actor->isSleeping())
{
glDisable(GL_LIGHTING);
glColor3f(0,0,1);
}
nShapes = actor->getNbShapes();
while (nShapes--)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
{
DrawShape(shapes[nShapes]);
}
if (actor->isSleeping())
{
glEnable(GL_LIGHTING);
}
}

void RenderActors(bool shadows)
{
// Render all the actors in the scene
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;
DrawActorBlueSleep(actor);

}
}

If an actor is sleeping, it will be drawn in blue, otherwise it will be drawn with regular
lighting. Hit “p” to start the simulation. Note that the boxes push each other aside a bit
initially, then settle. Once they have settled and the ball is falling toward them, they will
start turning blue from the bottom of the stack upward. The actors are falling asleep.
The simulation has determined they are not moving fast enough to warrant colliding them
against each other, so it stops simulating them and turns them off.

Once the ball hits the stack, the actors immediately awaken as the stack is smashed by the
ball. After the stack is smashed and the boxes settle once again, they will start to turn
blue until they are all asleep. Move the ball through the pile of boxes with “umijkl”.
The only boxes awake are the ones being pushed or immediately in the wake of the ball,
the rest remain asleep. This is the small “window of action” that continues to be
simulated while the rest of the scene is asleep. Note that lots of actors together in the
same place make it easy for one actor being pushed to start a chain reaction of actors
colliding against each other, and hence can cause many actors in the scene to wake up.

4 Sleep Linear Velocity and Sleep Angular Velocity


We are now going to start adjusting the conditions under which actors fall asleep.

void InitNx()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
sphere->raiseBodyFlag(NX_BF_ENERGY_SLEEP_TEST);


}

Enable energy-based sleeping method:


sphere-> raiseBodyFlag(NX_BF_ENERGY_SLEEP_TEST);

Run the demo and hit “p” to drop the ball. The ball freezes in mid-air and turns blue. It
has fallen asleep! Even though the ball was in motion, it never got going faster than the
sleep linear velocity we set for it. The physics solver considered it “non-moving” for a
certain number of frames. Because it was going slower than its sleep linear velocity, it's
linear and angular motion was halted and it was put to sleep. It will wake up if we
manually apply a sudden force to it, if we move it instantly somewhere with the
setGlobalPosition() or moveGlobalPosition() function, or if another actor collides with it.

Drop the ball and wait for it to fall asleep. It will not fall asleep until its linear velocity
falls under the default, that is, in order for an object to fall asleep it must have a linear
velocity lower than its sleep linear velocity and an angular velocity lower than its sleep
angular velocity. If it is sitting in same position, but spinning around fast enough, it will
not fall asleep. Now hit “f” and apply torque to the sphere along the y-axis with “u”.
The sphere will wake up because we apply the torque suddenly, but if we don’t apply
enough torque to get it above its sleep angular velocity, it will fall asleep as it is not
moving linearly and considered “non-rotating” by the SDK since we have set the sleep
angular velocity so high.

You can set the sleep linear velocity and sleep angular velocity globally for all objects as
well as on a per-actor basis. Take a look at the new parameters in InitNx().

void InitNx()
{

gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.14*0.14);
gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_ANG_VEL_SQUARED,
0.15*0.15);

}

We currently have them set to their defaults. Set them differently.

gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED, 2*2);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_ANG_VEL_SQUARED,
2*2);

This will cause all objects in the scene to fall asleep when moving under these velocities
for a certain number of frames. Note how fast all the objects fall asleep with the new
global values.

5 Skin Width
Take a look at the first parameter, “skin width”, set in InitNx(). We have been setting
this parameter in all the lessons (it defaults to 0.05). Now you’ll get to see what it does.

void InitNx()
{

// Set the physics parameters
gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

}

The parameter NX_SKIN_WIDTH is a sort of “grace depth” the actors are allowed to
penetrate each other by. Hence the “settling” of the stack you see when you first
unpause the simulation.

Change the line to this:

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.1);

Note how the boxes shift deeper into one another. You have changed the distance
between the shapes required for collision so the boxes will interpenetrate each other to a
depth of 0.1 meters, or 10 cm, rather than 0.01 meters, or 1 cm. Note that the boxes are
50 cm on a side, so a 1 cm interpenetration is minor, which 10 cm is a good chunk of the
box. Try the simulation out with skin width at 0.1.

The collisions between the boxes is fairly soft, like they’re cardboard boxes, as the boxes
are allowed a bit of give before they start pushing the other boxes.

Change the skin width parameter to 0.0001 or 0.1mm.

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.0001);

The boxes are now much more responsive to the initial impact of the wrecking ball, as if
they were made out of metal with sharp contacts.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
The skin width has a couple of effects on actor sleeping. Individual actors are awakened
more easily if they have small skin widths, in that a collision will likely. These actors
will tend to separate, spread out faster, however, due to their reactiveness. A much
larger skin width means the actors will be more sluggish and never push away very far
from each other. Hence, a cluster of actors with a high skin width will tend to stay
together and not separate. The tight group of actors, like the stack in the simulation, will
wake up and turn on in large numbers as the boxes chain collide against each other. A
roomful of objects all awake at once will bring your simulation to a crawl.

You can set the skin width on a per-shape basis as well.

NxActor* sphere = NULL;


...
void InitNx()
{
...
sphere = CreateSphere(NxVec3(0,6,0), 1, 50);
NxShape*const* shapes = sphere->getShapes();
shapes[0]->setSkinWidth(0.25);
...
}

With a skin width of 0.25m, you'll notice the sphere visibly “sinks” into the ground plane.

Skin width is a very powerful parameter since it’s important to simulation performance
and must be tuned properly for each simulation, depending on how reactive you want
your object contacts to be.

6 Conclusion
When your objects' sleep velocity is high relative to your object size, your objects will
fall asleep even when they are perceptibly moving. When their sleep velocity is low,
they will rarely fall asleep.

Other things you should have learned this lesson: what actor sleeping means, why actor
sleeping is necessary for complex simulations, what the skin width parameter is and how
to use it, and what implications the skin width has for simulation performance.

7 Related Classes, Functions, and Parameters


NxActor
isSleeping()
raiseBodyFlag()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

7
NxParameter
NX_BF_ENERGY_SLEEP_TEST
NX_SKIN_WIDTH

NxShape
setSkinWidth()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

8
Lesson 107 – Rigidbody Properties

1 Introduction
Dynamic actors have a set of special properties that are used for rigid body simulation.
In this lesson, we learn how to apply these properties to rigid body.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Rigidbody Properties
All rigid body properties have a linear and angular version. The linear version is used in
computations involving rectilinear motion along a path, and the angular version is used
when the body is rotating. Most movement is a combination of both.

Initial settings for these properties may be assigned to NxBodyDesc *


NxActorDesc::body, as well as queried and changed using appropriate methods of
NxActor.

Linear Quantity Angular Quantity


mass (scalar) inertia (3-vector) - The mass distribution
of the body along its three dimensions.
position (3-vector) - The position of the orientation (quaternion or 3x3-matrix) -
body's center of mass relative to the The orientation of the principal moments
actor's frame. relative to the actor's frame.
velocity (3-vector) - Linear velocity. angular velocity (3-vector) - Represented
as an axis of rotation with a length equal to
the magnitude of the angular velocity.
force (3-vector) - The amount of force torque (3-vector) - The amount of angular
that is being exerted on this body. force that is being exerted on this body.
Represented as an axis of rotation with a
length equal to the magnitude of the
angular velocity.

void KeyboardCallback(unsigned char key, int x, int y)


{
gKeys[key] = true;

switch (key)
{
case 'r':
{
SelectNextActor();
break;
}
case 'e':
{
if (gSelectedActor)
{
gSelectedActor->setGlobalPosition(NxVec3(3.0f, 5.0f, 3.0f));
}
break;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
case 'c':
{
if (gSelectedActor)
{
gSelectedActor->setLinearVelocity(NxVec3(10.0f, 0, 100));
}
break;
}
case 'v':
{
if (gSelectedActor)
{
gSelectedActor->setAngularVelocity(NxVec3(0, 1000.0f, 0));
}
break;
}
default: { break; }
}
}

void ProcessForceKeys()
{
if (!gSelectedActor) return;

// Process force keys


for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{
// Force controls
case 'i': { gForceVec =
ApplyForceToActor(gSelectedActor,NxVec3(0,0,1),gForceStrength,bForceMode);
break; }
case 'k': { gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(0,0,-
1),gForceStrength,bForceMode); break; }
case 'j': { gForceVec =
ApplyForceToActor(gSelectedActor,NxVec3(1,0,0),gForceStrength,bForceMode);
break; }

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
case 'l': { gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(-
1,0,0),gForceStrength,bForceMode); break; }
case 'u': { gForceVec =
ApplyForceToActor(gSelectedActor,NxVec3(0,1,0),gForceStrength,bForceMode);
break; }
case 'm': { gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(0,-
1,0),gForceStrength,bForceMode); break; }

// Return focus actor to (0,5,0)


case 't':
{
gSelectedActor->setGlobalPosition(NxVec3(0,5,0));
gScene->flushCaches();
break;
}
}
}
}

3 Conclusion
New things you should have learned: how to build a static actor, how to turn a dynamic
actor into a kinematic one and back to a dynamic actor again, how to use
moveGlobalPosition(), and how to use setGlobalPosition().

4 Related Classes, Functions, and Parameters


NxActor
setGlobalPosition()
moveGlobalPosition()
readBodyFlag()
raiseBodyFlag()
clearBodyFlag()
setLinearVelocity()
setAngularVelocity()
addForce()
addTorque()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 108 – Freeze Flags and Damping

1 Introduction
In this lesson you will learn about freeze flags and linear and angular damping. Freeze
flags are rigid body flags meant to constrain components of an object’s position and
rotation, like making sure the object stays at a particular height or is unable to rotate
about a particular axis. Disabling gravity is another rigid body flag we will see that
makes the actor unaffected by gravity.

Linear and angular damping help stop moving objects that, in the absence of friction,
constraints, or collisions, would otherwise continue to travel indefinitely. In a realistic
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
simulation, there is usually a certain measure of air resistance. Doing without linear and
angular damping can lead to results that play unrealistically, with objects travelling
perpetually, moving without resistance farther than they would normally in an everyday
environment.

2 Freeze Flags
We have created five boxes and a pyramid. The pyramid is not set with the anisotropic
friction material, just the default material, and is unconstraint, so you can move it around
normally.

// Actor globals
...
NxActor* box1 = NULL;
NxActor* box2 = NULL;
NxActor* box3 = NULL;
NxActor* box4 = NULL;
NxActor* box5 = NULL;
extern NxActor* pyramid;

extern NxActor* gSelectedActor;

...
void InitNx ()
{

box1 = CreateBox(NxVec3(10,0.5,0), NxVec3(1,1,1), 10);
box2 = CreateBox(NxVec3(10,0.5,10), NxVec3(1,1,1), 10);
box3 = CreateBox(NxVec3(0,0.5,10), NxVec3(1,1,1), 10);
box4 = CreateBox(NxVec3(-10,0.5,10), NxVec3(1,1,1), 10);
box5 = CreateBox(NxVec3(-10,0.5,0), NxVec3(1,1,1), 10);

pyramid = CreatePyramid(NxVec3(0,0,0), NxVec3(1.5,0.75,1.5), 25);


pyramid->setLinearDamping(0.5);

box1->raiseBodyFlag(NX_BF_FROZEN_POS);
box1->setLinearDamping(0.5);

box2->raiseBodyFlag(NX_BF_FROZEN_POS_X);
box2->raiseBodyFlag(NX_BF_FROZEN_POS_Z);
box2->setLinearDamping(0.5);

box3->raiseBodyFlag(NX_BF_FROZEN_POS_Y);
box3->raiseBodyFlag(NX_BF_FROZEN_POS_Z);
box3->setLinearDamping(0.5);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
box4->raiseBodyFlag(NX_BF_FROZEN_POS_X);
box4->raiseBodyFlag(NX_BF_FROZEN_POS_Z);
box4->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
box4->setLinearDamping(0.5);

box5->raiseBodyFlag(NX_BF_FROZEN_POS_X);
box5->raiseBodyFlag(NX_BF_FROZEN_POS_Y);
box5->setLinearDamping(0.5);

AddUserDataToActors(gScene);

gSelectedActor = pyramid;

}

Box1 is frozen in x, y, and z. It cannot move linearly, it can only rotate. Box2 is frozen
in x and z, it can only move along the y-axis. Box3 is frozen in y and z, it is constrained
to move along the x-axis between boxes 2 and 4. Box4 is frozen in x and z, thus
constrained along y, but we have disabled gravity, so when we hit it with the slightest
upward force it floats upward effortlessly. The same with box5 if you hit it along z.
Box5 is frozen in x and y.

Actors can be frozen in rotation about particular axes as well.

3 Damping
Linear and angular damping are stopping forces on actors, which, suspended in air with
no friction and/or gravity, might continue to tumble to infinity if we didn’t add this. Try
taking the linear damping away from box4, in the upper right and hit it with the pyramid.
Note that it tumbles upward and keeps going. Linear damping slows it down eventually
so this does not happen.

box1->setLinearDamping(0.5);

Instead of stopping an object by contact with another object (either direct collision or
friction), think of linear damping as stopping an object with air resistance. Basically, it’s
the resistance of the medium you define your objects to reside in.

There is angular damping as well which keeps actors from perpetually spinning. You
can think of this as either air resistance or loss of motion due to rotational friction in the
interior of the object.

Notice the pyramid has linear damping on it as well. It stops by itself more quickly
which makes it easier to control.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
4 Conclusion
From this lesson, you should know everything you need to know about freeze flags and
linear and angular damping.

5 Related Classes, Functions, and Parameters


NxActor
setLinearDamping()
setAngularDamping()

NxBodyFlag
NX_BF_FROZEN_POS_X
NX_BF_FROZEN_POS_Y
NX_BF_FROZEN_POS_Z
NX_BF_FROZEN_ROT_X
NX_BF_FROZEN_ROT_Y
NX_BF_FROZEN_ROT_Z
NX_BF_FROZEN_POS
NX_BF_FROZEN_ROT
NX_BF_FROZEN
NX_BF_DISABLE_GRAVITY

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 109 – Collision Groups

1 Introduction
This training program teaches you about collision groups. A collision group is used to
mark collections of shapes in the scene as collidable with none, some, or all other
collision groups. All shapes start out in collision group 0 by default.

Collision groups are useful for turning collision off for groups of shapes you don’t want
colliding with other groups of shapes. You can also use collision groups to turn off
certain groups of shapes you want to ignore when casting a ray through the scene.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Collision Groups
We create 3 sets of objects (3 boxes, 3 capsules, and 3 spheres) and place them on the
ground.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box1 = NULL;
NxActor* box2 = NULL;
NxActor* box3 = NULL;
NxActor* sphere1 = NULL;
NxActor* sphere2 = NULL;
NxActor* sphere3 = NULL;
NxActor* capsule1 = NULL;
NxActor* capsule2 = NULL;
NxActor* capsule3 = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

box1 = CreateBox(NxVec3(6,0,1), NxVec3(0.5,1,0.5), 20);


box2 = CreateBox(NxVec3(5,0,0.25), NxVec3(0.5,1,0.5), 20);
box3 = CreateBox(NxVec3(4,0,1), NxVec3(0.5,1,0.5), 20);

capsule1 = CreateCapsule(NxVec3(1,0,6), 1.5, 0.5, 20);


capsule2 = CreateCapsule(NxVec3(0,0,5), 1.5, 0.5, 20);
capsule3 = CreateCapsule(NxVec3(-1,0,6), 1.5, 0.5, 20);

sphere1 = CreateSphere(NxVec3(-6,0,1), 0.8, 20);


sphere2 = CreateSphere(NxVec3(-5,0,-0.25), 0.8, 20);
sphere3 = CreateSphere(NxVec3(-4,0,1), 0.8, 20);

InitActorCollisionGroups();
InitContactReportsPerPair();
...
gSelectedActor = box3;

}

We then call InitActorCollisionGroups().

void SetActorCollisionGroup(NxActor *actor, NxCollisionGroup group)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
{
NxU32 nbShapes = actor->getNbShapes();
NxShape*const* shapes = actor->getShapes();

while (nbShapes--)
{
shapes[nbShapes]->setGroup(group);
}
}

void InitActorCollisionGroups()
{
SetActorCollisionGroup(box1, 1);
SetActorCollisionGroup(box2, 1);
SetActorCollisionGroup(box3, 1);

SetActorCollisionGroup(capsule1, 2);
SetActorCollisionGroup(capsule2, 2);
SetActorCollisionGroup(capsule3, 2);

SetActorCollisionGroup(sphere1, 3);
SetActorCollisionGroup(sphere2, 3);
SetActorCollisionGroup(sphere3, 3);

gScene->setGroupCollisionFlag(1,2,true);
gScene->setGroupCollisionFlag(2,3,true);
gScene->setGroupCollisionFlag(1,3,false);
}

Collision groups are assigned to shapes, so we must get the shapes from each actor and
set each of them separately.

Notice the boxes are assigned to collision group 1, the capsules are assigned to collision
group 2, and the spheres are assigned to collision group 3. Note also, the ground plane
shape is assigned to collision group 0, by default.

Setting up groups like this does nothing without setting the collision group flags, which
get set in the last lines of the function.

gScene->setGroupCollisionFlag(1,2,true);
gScene->setGroupCollisionFlag(2,3,true);
gScene->setGroupCollisionFlag(1,3,false);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
So group 1 (the boxes) is set to collide with group 2 (the capsules). Group 2 (the
capsules) is set to collide with group 3 (the spheres). Group 1 (the boxes) is then set not
to collide with group 3 (the spheres).

Try playing the demo. Notice everything collides as usual, except the boxes do not
collide with the spheres. Try setting the capsules not to collide with the ground plane
(collision group 0).

gPhysicsSDK->setGroupCollisionFlag(0,2,false);

The capsules immediately fall through the floor once the simulation begins. They are
still affected by gravity and have nothing to hold them up once they are set not to collide
with the floor. If you want to keep them above ground, you need to disable gravity.

3 Contact Pairs and Collision Groups


We have attached a contact report to the SDK (c.f. “Lesson 302: Contacts”) and set up
each box and each sphere as a contact pair. Set a breakpoint on the first “y = 0” in
CheckContacts(), run the demo, and push a box into a sphere.

void CheckContacts()
{
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;

// Get our object back


GameObject* Object = (GameObject*)actor->userData;
if(!Object) continue;

if (Object->events != 0)
{
NxU32 y;
y = 0;
}

}

}

The contact between the box and the sphere does not get reported. If two objects are set
not to collide, even if they are set up as contact pairs, their collision will not be reported
as in actuality no contact between them has occurred.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
This is the distinction between collision groups and contact pairs. Collision groups are
collections of shapes marked to collide or not to collide with other collections of shapes.
Use them to enable or disable collision between groups of shapes. Contact pairs are
pairs of actors designated by the user to send back collision and contact information when
they are in contact with one another. Use them to determine the points of contact in a
collision between two actors.

There is a lone actor flag called NX_AF_DISABLE_COLLISION. It is independent of


collision groups and can be set to disable collision between an actor and the rest of the
world.

NxActor* box = NULL;



box->raiseActorFlag(NX_AF_DISABLE_COLLISION);

The disables collision between the world and all collision shapes within the actor.

4 Uses for Collision Groups


Collision groups are used when the user wants one group of objects to be unaffected by
another through collision for whatever reason. Often groups are marked as uncollidable
with other groups for performance. The two groups of shapes are never checked against
each other. Computation to determine collision between the two groups of shapes is
never performed.

Sometimes groups are marked as uncollidable for a particular effect, like a character
wading through a morass of goo or debris. It might look strange for certain debris to
bounce off the character like a rigid body does. It may be possible to tune the behaviour
of the debris, but this tuning can be time consuming. In effect, you are saying the
character has negligible effect on these objects when walking through them, they mostly
are crushed underfoot and close up behind him as if he were walking through water or
over a thick carpet. Just turning collisions off between the character and the debris with
collision groups will achieve what you want.

Collision groups can be used to keep dynamics from interfering with gameplay.
Sometimes you want to turn dynamics off if the results have a negligible or even negative
impact on gameplay. If you’re having trouble with actors sticking or colliding in an
unseemly fashion in certain special cases, you may want to mark them as uncollidable
and let them pass through each other if there is little visible result or if the visible result
of a non-collision is desired.

There are certain gameplay functions like pathfinding or raycasting, where collisions are
unwanted. If an AI is trying to avoid obstacles using collision detection in a cluttered
scene, you may want to mark some objects as obstacles and ignore the rest. The AI
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
might be able to ignore most objects, it will walk right over rocks and light debris, but
will want to avoid other objects, like a boulder or table.

For line-of-sight raycasts, you may want to put all transparent or semi-transparent objects
into a single collision group: they will collide with each other and other non-transparent
objects, but will not obstruct a line-of-sight raycast.

5 Conclusion
New things you should have learned: what collision groups are, how to assign different
shapes to different collision groups, how to enable and disable collisions between
collision groups, how to disable collision between an actor and the world, and uses for
collision groups.

6 Related Classes, Functions, and Parameters


NxCollisionGroup

NxPhysicsSDK
setCollisonGroupFlag()

NxShape
setGroup()

NxActorFlag
NX_AF_DISABLE_COLLISION

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Lesson 110 – Collision Filtering

1 Introduction
We have seen how to use collision groups to partition our actors' shapes into groups that
we can selectively make collidable or non-collidable with each other. This involved
using NxShape::setGroup() to put the shape into a collision group and then

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
NxPhysicsSDK::setCollisionGroupFlag() to flag two shape groups as collidable or non-
collidable.

We now introduce the concept of collision filtering. We assign each shape a 128-bit
bitmask using NxShape::setGroupsMask(). When determining whether two shapes will
collide with each other, we put the two shape bitmasks through a logical operation, the
results of which tell us whether the objects will collide with each other or not.

2 Collision Filtering
Collision filtering is a more powerful method to define which groups of shapes will
collide with which. You assign a “groupsMask” field to each shape, a 128-bit integer
which defines the collision group it belongs to. You then set up three filter operations
(op0, op1, and op2), two constant filter masks (F0 and F1), and a filtering boolean (b).

Whether or not collision is enabled between two shapes is determined by whether the
following equation holds true.

(G0 op0 K0) op2 (G1 op1 K1) == b

… where G0 and G1 are the groups masks for shape S0 and shape S1, respectively.

Look at InitNx():

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Set up the filtering equation
gScene->setFilterOps(NX_FILTEROP_OR, NX_FILTEROP_OR,
NX_FILTEROP_AND);
gScene->setFilterBool(true);

NxGroupsMask zeroMask;
zeroMask.bits0 = zeroMask.bits1 = zeroMask.bits2 = zeroMask.bits3 = 0;
gScene->setFilterConstant0(zeroMask);
gScene->setFilterConstant1(zeroMask);

}

op0, op1, and op2 are OR, OR, and AND.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
gPhysicsSDK->setFilterOps(NX_FILTEROP_OR, NX_FILTEROP_OR,
NX_FILTEROP_AND);

K0 and K1 are both the zero mask.

NxGroupsMask zeroMask;
zeroMask.bits0 = zeroMask.bits1 = zeroMask.bits2 = zeroMask.bits3 = 0;
gScene ->setFilterConstant0(zeroMask);
gScene ->setFilterConstant1(zeroMask);

b is true.

gScene ->setFilterBool(true);

So our filtering equation looks like this:

(G0 OR 0) AND (G1 OR 0) == TRUE

…which is the same as:

G0 AND G1 = TRUE

…or:

G0 = G1

Therefore, collision between two objects will be enabled if the the filter masks for S0 and
S1 are the same. So our equation basically makes things operate like the 0..32 collision
group system we have been using up to now, only now we have 0..2^128 different
collision groups.

Now look at the object creation code in InitNx():

// Actor globals
NxActor* groundPlane = NULL;
NxActor** boxes = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

boxes = CreateStack(NxVec3(0,0,0), NxVec3(4,3,4), NxVec3(0.5,0.5,0.5), 10);



Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
}

We create the ground plane and stuff its groupsMask bits with 1’s.

NxActor* CreateGroundPlane()
{
// Create a plane with default descriptor
NxPlaneShapeDesc planeDesc;
NxActorDesc actorDesc;
planeDesc.groupsMask.bits0 = 0xffffffff;
planeDesc.groupsMask.bits1 = 0xffffffff;
planeDesc.groupsMask.bits2 = 0xffffffff;
planeDesc.groupsMask.bits3 = 0xffffffff;
actorDesc.shapes.pushBack(&planeDesc);
return gScene->createActor(actorDesc);
}

Then we create a 4x3x4 stack of boxes.

NxActor** CreateStack(const NxVec3& pos, const NxVec3& stackDim, const


NxVec3& boxDim, NxReal density)
{
NxActor** stack = new NxActor*[(int)(8*stackDim.x*stackDim.y*stackDim.z)];
NxVec3 offset = NxVec3(boxDim.x,0,boxDim.z) + pos;

int count = 0;
for (int i = -stackDim.x; i < stackDim.x; i++)
{
for (int j = 0; j < stackDim.y; j++)
{
for (int k = -stackDim.z; k < stackDim.z; k++)
{
NxVec3 boxPos =
NxVec3(i*boxDim.x*2,j*boxDim.y*2,k*boxDim.z*2);
stack[count++] = CreateBox(boxPos + offset, boxDim, density);
}
}
}

return stack;
}

NxActor* CreateBox(const NxVec3& pos, const NxVec3& boxDim, const NxReal


density)
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
// Add a single-shape actor to the scene
NxActor* actor;
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

NxU32 random = rand();


random &= 1;

// The actor has one shape, a box


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(boxDim.x,boxDim.y,boxDim.z);
boxDesc.localPose.t = NxVec3(0,boxDim.y,0);
if (random)
boxDesc.groupsMask.bits0 = 1;
else
boxDesc.groupsMask.bits1 = 1;
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = density;
actorDesc.globalPose.t = pos;
actor = gScene->createActor(actorDesc);

NxU32 userData = 2;
userData |= random << 16;
actor->userData = (void*)userData;

return actor;
}

By default, groupsMask.bits0..bits3 are all zeros. In our CreateBox() function, we


randomly set groupsMask.bits0 to 1 (= 0001) for some boxes and groupsMask.bits1 to 1
(= 0001) for others. (so, in binary, groupsMask = 0000 0000 0000 0001 for some actors,
and groupsMask = 0000 0000 0001 0000 for others).

We then set the user data on our box actors to reflect which collision group mask they
belong to. For random = 0, bits0 = 1, for random = 1, bits1 = 1. For random = 0, we set
the second bit of userData (userData = 2 | (0 <<16)). For random = 1, we set the second
and sixteenth bits (userData = 2 | (1 << 16)). The second bit of userData denotes the
object is one of our boxes we have set the collision mask bits on. The sixteenth bit of
userData denotes which bits we have set, bits0 if this is 0, bits1 if this is 1.

We extract these bits in our RenderActors() function.

void RenderActors(bool shadows)


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
{
// Render all the actors in the scene
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;

NxU32 userData = NxU32(actor->userData);


if (userData)
{
NxF32 size = NxF32(userData&0xffff);

NxU32 random = userData >> 16;


if (random & 1)
glColor3f(1,1,1);
else
glColor3f(1,0,0);
}
else
{
glColor3f(1,1,1);
}

DrawActor(actor);

glColor3f(1,1,1);

// Handle shadows
if (shadows)
{
DrawActorShadow(actor);
}
}
}

We get userData’s 16th bit.

NxU32 random = userData >> 16;


if (random & 1)
glColor3f(1,1,1);
else
glColor3f(1,0,0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
If the 16th bit is 0 (bits0 is set), we color the stack box white. If the 16th bit is 1 (bits1 is
set), we color the stack box red.

3 The Collision Group Mask


Run the demo. The boxes in the stack will only collide with each other if they are the
same color, the same collision filter bits are set. If they have the same collision filter bits
then our filtering equation:

G0 AND G1 = TRUE

…will be statisfied. If they are different colors, they will fall through each other,
different collision filter bits are set. When the collision filter bits are disjoint, G0 AND
G1 = 0 != TRUE.

Notice that both the white bits0 set of boxes and red bits1 set of boxes collide with the
ground plane. This is because, as described above, we have set ALL the collision filter
bits of the ground plane. Therefore, if G2 is the ground plane collision filter group, G0
AND G2 = TRUE and G1 AND G2 = TRUE.

This means it is possible for a shape to belong to multiple collision groups. Effectively,
there are 128 different collision groups, 1 for each bit in the groups collision mask. It is
possible for a shape to belong to some, none, or all of the collision groups. This is a
crucial difference between the collision filtering method and the deprecated group
method we have been using thusfar (c.f., “Lesson 109: Collision Groups”).

If you always set the collision filtering op2 to “AND”, the way we have here, shapes will
only collide with other shapes when their groups mask bits overlap. You can change
op2, however, as you can change the boolean condition of the equation.

You can also mask the collision group filters you are considering with constant masks.
We have done this in InitNx():

void InitNx()
{

NxGroupsMask zeroMask;
zeroMask.bits0 = zeroMask.bits1 = zeroMask.bits2 = zeroMask.bits3 = 0;
gScene->setFilterConstant0(zeroMask);
gScene->setFilterConstant1(zeroMask);

}

Filter Constant 0 is K0 and Filter Constant 1 is K1 in our filtering equation. As shown


above, we have set op0 and op1 to OR.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
(G0 OR K0) AND (G1 OR K1) = TRUE

Since K0 = K1 = 0, G0 OR K0 = G0 and G1 OR K1 = G1, so our constant masks are


trivial masks, they don’t filter anything.

Try changing your filtering equation like this:

void InitNx()
{

// Set up the filtering equation
gScene ->setFilterOps(NX_FILTEROP_OR, NX_FILTEROP_OR,
NX_FILTEROP_AND);
gScene ->setFilterBool(true);
NxGroupsMask fMask0;
fMask0.bits0 = fMask0.bits1 = fMask0.bits2 = fMask0.bits3 = 0xffffffff;
gScene ->setFilterConstant0(fMask0);
NxGroupsMask fMask1;
fMask1.bits0 = fMask1.bits1 = fMask1.bits2 = fMask1.bits3 = 0xffffffff;
gScene->setFilterConstant1(fMask1);

}

If you run the demo, you’ll notice this change has effectively turned on collision between
all objects no matter what collision group mask they have. You can use the constant
filter masks to turn on/off collisions between different collision filters without changing
the actual collision filters on the shapes. If you want to the grant the player the ability to
walk through walls for a limited amount of time, for instance, you can adjust your
filtering constants accordingly for the duration of the effect, then revert them once the
effect is over, all without having to change any of the base collision groups the objects
belong to.

4 Conclusion
Collision filtering is a powerful and versatile method of grouping objects for collision
and turning collision on/off between the different groups. Its has a wide variety of uses
and features not available to the standard collision groups we have been using so far.

5 Related Classes, Functions, and Parameters


NxGroupsMask
bits0
bits1

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

8
bits2
bits3

NxShapeDesc
groupsMask

NxFilterOp
NX_FILTEROP_AND
NX_FILTEROP_OR
NX_FILTEROP_XOR
NX_FILTEROP_NAND
NX_FILTEROP_NOR
NX_FILTEROP_NXOR

NxScene
setFilterOps()
setFilterBool()
setFilterConstant0()
setFilterConstant1()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

9
Lesson 111 – Dynamic CCD

1 Introduction
Continuous collision detection is required to prevent fast-moving objects from tunneling
through other objects. PhysX does a broad phase collision sweep that catches most
collisions, though small, high-speed objects will still pass through undetected. To
remedy this, you can turn on continuous collision detection. In the current version of the
SDK, CCD is supported for dynamic vs. dynamic objects and dynamic vs. static objects.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Continuous Collision Detection
This effect is known as tunneling. Imagine a bullet traveling towards a thin metal plate, in
the first time step it would be on one side and in the next it would be on the other. The
SDK would never detect a collision and hence not constrain the motion of the bullet. To
deal with this problem, CCD tests an extruded volume which represents the object's
motion during the whole time step. If a collision is detected, the time of impact can be
computed and the object's motion constrained appropriately

To enable CCD computations for the scene, the NX_CONTINUOUS_CD must be set.
Turn on continuous collision detection in InitNx().

// CCD globals
bool bCCDEnabled = true;
...
void InitNx()
{
......
// Turn on continuous collision detection for dynamic objects
gPhysicsSDK->setParameter(NX_CONTINUOUS_CD, bCCDEnabled);
gPhysicsSDK->setParameter(NX_CCD_EPSILON, 0.01);
......
}

NxParameter::NX_CCD_EPSILON is a constant used to avoid numerical imprecision, so


it should be set to a small value.

3 Dynamic-dynamic CCD
Hit “1”, “2”, or “3” to create a small pyramid of boxes, a large pyramid of boxes, or a
tower of boxes.

void SpecialKeys(unsigned char key, int x, int y)


{
switch(key)
{
case '1': CreateStack(10); break;
case '2': CreateStack(15); break;
case '3': CreateTower(10); break;
...
}

Look at CreateStack().

void CreateStack(int size)


{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
const float cubeSize = 0.2f;
const float spacing = 0;
NxVec3 pos(0,0,0);
float offset = -size * (cubeSize * 2.0f + spacing) * 0.5f;
while(size)
{
for(int i=0;i<size;i++)
{
pos.x = offset + (float)i * (cubeSize * 2.0f + spacing);
CreateCCDBox(pos, NxVec3(cubeSize, cubeSize, cubeSize), 10, false);
}

offset += cubeSize;
pos.y += (cubeSize * 2.0f + spacing);
size--;
}
}

CreateStack() calls CreateCCDBox() to generate the boxes that make the stack.

NxActor* CreateCCDBox(const NxVec3& pos, const NxVec3& boxDim, const NxReal


density, bool doDynamicCCD)
{
// Add a single-shape actor to the scene
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// The actor has one shape, a box


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(boxDim.x,boxDim.y,boxDim.z);
boxDesc.localPose.t = NxVec3(0,boxDim.y,0);

// Set box up with CCD skeleton


boxDesc.ccdSkeleton = CreateCCDSkeleton(boxDim.x*0.8f);
if (doDynamicCCD) boxDesc.shapeFlags |=
NX_SF_DYNAMIC_DYNAMIC_CCD; // Activate dynamic-dynamic CCD for this
body

actorDesc.shapes.pushBack(&boxDesc);

if (density)
{
actorDesc.body = &bodyDesc;
actorDesc.density = density;
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
else
{
actorDesc.body = NULL;
}
actorDesc.globalPose.t = pos;
return gScene->createActor(actorDesc);
}

CreateCCDBox() creates a box actor from a box shape, just like our usual CreateBox()
function, only with the addition that we build a CCD skeleton for the box shape.

// Set box up with CCD skeleton


boxDesc.ccdSkeleton = CreateCCDSkeleton(boxDim.x*0.8f);

This CCD skeleton is a convex mesh that conforms to the box.

NxCCDSkeleton* CreateCCDSkeleton(float size)


{
NxU32 triangles[3 * 12] = {
0,1,3,
0,3,2,
3,7,6,
3,6,2,
1,5,7,
1,7,3,
4,6,7,
4,7,5,
1,0,4,
5,1,4,
4,0,2,
4,2,6
};

NxVec3 points[8];

// Static mesh
points[0].set( size, -size, -size);
points[1].set( size, -size, size);
points[2].set( size, size, -size);
points[3].set( size, size, size);

points[4].set(-size, -size, -size);


points[5].set(-size, -size, size);
points[6].set(-size, size, -size);
points[7].set(-size, size, size);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
NxSimpleTriangleMesh stm;
stm.numVertices = 8;
stm.numTriangles = 6*2;
stm.pointStrideBytes = sizeof(NxVec3);
stm.triangleStrideBytes = sizeof(NxU32)*3;

stm.points = points;
stm.triangles = triangles;
stm.flags |= NX_MF_FLIPNORMALS;
return gPhysicsSDK->createCCDSkeleton(stm);
}

Hit space to fire a small, fast box at your stack of boxes.

bool bFireCCDBox = false;


...
void SpecialKeys(unsigned char key, int x, int y)
{
switch(key)
{
...
case ' ':
{
bFireCCDBox = true;
}
break;
}
}

void ProcessInputs()
{
...
if (bFireCCDBox)
{
bFireCCDBox = false;
NxActor* ccdBox = CreateCCDBox(gCameraPos, NxVec3(0.1,0.1,0.1), 10,
true);
NxVec3 vel = gCameraForward;
vel.normalize();
vel *= 344;
ccdBox->setLinearVelocity(vel);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
We call CreateCCDBox() again, to make our projectile box, but this time we flag
doDynamicCCD as true.

// Set box up with CCD skeleton


boxDesc.ccdSkeleton = CreateCCDSkeleton(boxDim.x*0.8f);
if (doDynamicCCD) boxDesc.shapeFlags |=
NX_SF_DYNAMIC_DYNAMIC_CCD; // Activate dynamic-dynamic CCD for this
body

NxShapeFlag::NX_SF_DYNAMIC_DYNAMIC_CCD activates dynamic CCD for the


box we are firing. It will get special attention from the solver to make sure that it
collides with other shapes that are fitted with continuous collision detection skeletons.
The speed of your box is 344 meters/sec, yet it will still collide with the stack of boxes
you create.

Hit “c” to turn off continuous collision detection.

// CCD globals
bool bCCDEnabled = true;
bool bToggleCCDEnabled = false;

void SpecialKeys(unsigned char key, int x, int y)


{
switch(key)
{
case 'c':
{
bToggleCCDEnabled = true;
}
}
}
void ProcessInputs()
{
ProcessForceKeys();

if (bToggleCCDEnabled)
{
bToggleCCDEnabled = false;
bCCDEnabled = !bCCDEnabled;
gPhysicsSDK->setParameter(NX_CONTINUOUS_CD, bCCDEnabled);
RefreshDisplayString();
}
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
When you fire boxes with “v”, they will now pass right through your stacks as they are
moving too fast to collide with Continuous collision detection removed.

CCD is performed with a skeleton mesh embedded within the object, which is simpler
than the geometry used for discrete collision detection. The skeleton stops the object
flying through other objects.To create a CCD skeleton, use createCCDSkeleton() function.
When CCD becomes active (see below for further information about this) it stops ALL
motion for the actor, including lateral sliding motion, which may not be desirable. In
order to stop this from happening too often, you want to make the CCD skeleton smaller
than the actual shape (minus the skin width), so that the CCD skeleton only comes in
contact with other objects when it is in deeper than normal penetration.

4 Conclusion
You should now know how to enable continuous collision detection for your scene, set
your objects up with Continuous collision skeletons, and enable select objects for
dynamic-dynamic continuous collision detection with objects fitted with these skeletons.

5 Related Classes, Functions, and Parameters


NxParameter
NX_CONTINUOUS_CD
NX_CCD_EPSILON

NxShapeFlag
NX_SF_DYNAMIC_DYNAMIC_CCD

NxSimpleTriangleMesh

NxPhysicsSDK
createCCDSkeleton()

NxActor
setLinearVelocity()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

7
Lesson 112 – Overlap

1 Introduction
In addition to providing Raycast and Trigger support, PhysX SDK provides a set of
general methods for testing collision primitive directly against shapes. These are useful
when the other collision methods do not fit the user's needs. In this lesson, we learn this
kind of method of efficiently testing collision: overlap.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Overlap
Overlap provided by NxScene allows efficiently testing against the entire collision
database, while methods of NxShape test against specific objects.
void Update()
{
……

int i = 0;
for (i = 0; i < 3; ++ i)
{
if (i == 0)
{
nbShapes = nbDynamicShapes;
type = NX_DYNAMIC_SHAPES;
switch(gOverlapType)
{
case OVERLAP_AABB:
case OVERLAP_CHECK_AABB:
worldBounds.set(gMIN, gMAX);
break;
case OVERLAP_OBB:
case OVERLAP_CHECK_OBB:
worldBox.center = gBoxCenter;
break;
case OVERLAP_CAPSULE:
case OVERLAP_CHECK_CAPSULE:
worldCapsule = NxCapsule(gCapsuleSegment, gCapsuleRadius);
break;
case OVERLAP_SPHERE:
case OVERLAP_CHECK_SPHERE:
worldSphere = NxSphere(gSphereCenter, gSphereRadius);
break;
}
}
else if (i == 1)
{
nbShapes = nbStaticShapes;
type = NX_STATIC_SHAPES;
switch(gOverlapType)
{
case OVERLAP_AABB:
case OVERLAP_CHECK_AABB:
worldBounds.set(gMIN+NxVec3(-6.0f,0,0),gMAX+NxVec3(-6.0f,0,0));
break;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
case OVERLAP_OBB:
case OVERLAP_CHECK_OBB:
worldBox.center = gBoxCenter+NxVec3(-6,0,0);
break;
case OVERLAP_CAPSULE:
case OVERLAP_CHECK_CAPSULE:
worldCapsule.p0.x = gCapsuleSegment.p0.x - 6.0f;
worldCapsule.p1.x = gCapsuleSegment.p1.x - 6.0f;
break;
case OVERLAP_SPHERE:
case OVERLAP_CHECK_SPHERE:
worldSphere = NxSphere(gSphereCenter + NxVec3(-6,0,0), gSphereRadius);
break;
}
}
else if (i == 2)
{
nbShapes = nbStaticShapes + nbDynamicShapes;
type = NX_ALL_SHAPES;
switch(gOverlapType)
{
case OVERLAP_AABB:
case OVERLAP_CHECK_AABB:
worldBounds.set(gMIN+NxVec3(6.0f,0,0),gMAX+NxVec3(6.0f,0,0));
break;
case OVERLAP_OBB:
case OVERLAP_CHECK_OBB:
worldBox.center = gBoxCenter+NxVec3(6,0,0);
break;
case OVERLAP_CAPSULE:
case OVERLAP_CHECK_CAPSULE:
worldCapsule.p0.x = gCapsuleSegment.p0.x + 6.0f;
worldCapsule.p1.x = gCapsuleSegment.p1.x + 6.0f;
break;
case OVERLAP_SPHERE:
case OVERLAP_CHECK_SPHERE:
worldSphere = NxSphere(gSphereCenter + NxVec3(6,0,0), gSphereRadius);
break;
}
}
}
}

The user can test against static, dynamic, or all shapes by setting the shapeType
parameter to NX_STATIC_SHAPES, NX_DYNAMIC_SHAPES or NX_ALL_SHAPES
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
respectively.

The user has two options for retrieving the overlapping shapes, detailed below:
• Provide a buffer large enough to hold all the overlapping shapes using the shapes
and nbShapes parameters. If this buffer is insufficient in size, then only the first
nbShapes are returned.
• Provide an NxUserEntityReport callback which directs the SDK to call the
callback with sets of shapes as they are detected. A small buffer is allocated on
the stack to return shapes, unless a buffer is provided using the shapes parameter,
in which case the SDK uses this temporarily.

NxScene::overlapSphereShapes()
This method tests a sphere (in the global frame) against shapes in the scene

NxScene::overlapAABBShapes()
This method tests an axis aligned bound box (in global frame) against shapes in
the scene.

NxScene::overlapOBBShapes()
This method tests an oriented bounding box (in global frame) against shapes in
the scene.

NxScene::overlapCapsuleShapes()
This method tests a world space capsule (in global frame) against shapes in the
scene.

NxScene::cullShapes()
This method tests all shapes in the collision database against a set of planes. It
tests objects against the planes and returns them if they are in the plane's negative half
space (i.e., on the side which the normal points away from). A limitation of this function
is that the number of planes should not exceed 32.

NxScene::checkOverlapSphere()
This method tests the sphere against shapes in the collision database.

NxScene::checkOverlapAABB()
This method tests the specified AABB against shapes in the collision database

NxScene::checkOverlapCapsule()
This method tests the capsule against shapes in the collision database

NxScene::checkOverlapOBB()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
This method tests the OBB against shapes in the collision database.

NxShape::checkOverlapSphere()
This method tests the sphere against only a single shape.

NxShape::checkOverlapAABB()
This method tests the specified AABB against only a single shape.

NxShape::checkOverlapOBB()
This method tests the OBB against only a shape.

NxTriangleMeshShape::overlapAABBTriangles() and
NxHeightFieldShape::overlapAABBTriangles()
This method tests an AABB against a triangle mesh shape and retrieves a list of
triangles which intersect the AABB. The user entity report(callback) is called with
batches of triangle indices.

3 Conclusion
We should now know how to use overlap to efficiently detect collision.

4 Related Classes, Functions, and Parameters


NxScene::overlapSphereShapes()
NxScene::overlapAABBShapes()
NxScene::overlapCapsuleShapes()
NxScene::cullShapes()
NxScene::checkOverlapSphere()
NxScene::checkOverlapAABB()
NxScene::checkOverlapCapsule()

NxShape::checkOverlapSphere()
NxShape::checkOverlapAABB()
NxShape::checkOverlapOBB()
NxTriangleMeshShape::overlapAABBTriangles()
NxHeightFieldShape::overlapAABBTriangles()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 113 – Rigidbody and Cloth

1 Introduction
In this lesson, we demonstrate interaction between Cloth and Rigid body.

2 Cloth
Cloth allows for simulation of such items as flags, clothing, etc. There are two way of
interaction with Rigid body. One is called one-way that Rigid body applies force to cloth
and Rigid body isn’t affected. The other is called two-way that Rigid body and cloth
interact each other. When Rigid body applies force to cloth, cloth also applies force to
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
Rigid body at the same time.

void SetupCurtainScene()
{
// sprintf(gTitleString, "Cloth and Shape Interaction Sample");
……

NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(4,7,0);
clothDesc.thickness = 0.2;
//clothDesc.density = 1;
clothDesc.bendingStiffness = 0.5;
clothDesc.stretchingStiffness = 1;
//clothDesc.dampingCoefficient = 0.5;
clothDesc.friction = 0.5;
//clothDesc.collisionResponseCoefficient = 1;
//clothDesc.attachmentResponseCoefficient = 1;
//clothDesc.solverIterations = 5;
//clothDesc.flags |= NX_CLF_STATIC;
//clothDesc.flags |= NX_CLF_DISABLE_COLLISION;
//clothDesc.flags |= NX_CLF_VISUALIZATION;
//clothDesc.flags |= NX_CLF_GRAVITY;
clothDesc.flags |= NX_CLF_BENDING;
//clothDesc.flags |= NX_CLF_BENDING_ORTHO;
//clothDesc.flags |= NX_CLF_DAMPING;
//clothDesc.flags |= NX_CLF_COMDAMPING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;

clothDesc.flags &= ~NX_CLF_HARDWARE;

MyCloth *regularCloth = new MyCloth(gScene, clothDesc, 8, 7, 0.15,


"nvidia.bmp");
gCloths.push_back(regularCloth);

regularCloth->getNxCloth()->attachToShape(*box2->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}

2.1 Creating Cloth Mesh


A cloth is defined using a mesh which specifies cloth particle positions (vertices) and the
constraints between them (edges). Similarly to a triangle mesh, it is necessary to cook a
cloth mesh into a suitable format for simulation.To do so, fill in an NxClothMeshDesc
structure and pass it to NxCookingInterface::NxCookClothMesh().This function returns a
stream of bytes that can either be saved and reused or fed to NxScene::createClothMesh()
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
void MyCloth::generateRegularMeshDesc(NxClothMeshDesc &desc, NxReal w, NxReal
h, NxReal d, bool texCoords, bool tearLines)
{
int numX = (int)(w / d) + 1;
int numY = (int)(h / d) + 1;

desc.numVertices = (numX+1) * (numY+1);


desc.numTriangles = numX*numY*2;
desc.pointStrideBytes = sizeof(NxVec3);
desc.triangleStrideBytes = 3*sizeof(NxU32);
desc.vertexMassStrideBytes = sizeof(NxReal);
desc.vertexFlagStrideBytes = sizeof(NxU32);
desc.points =
(NxVec3*)malloc(sizeof(NxVec3)*desc.numVertices);
desc.triangles =
(NxU32*)malloc(sizeof(NxU32)*desc.numTriangles*3);
desc.vertexMasses = 0;
desc.vertexFlags = 0;
desc.flags = 0;

int i,j;
NxVec3 *p = (NxVec3*)desc.points;
for (i = 0; i <= numY; i++)
{
for (j = 0; j <= numX; j++)
{
p->set(-d*j, 0.0f, -d*i);
p++;
}
}

……

NxU32 *id = (NxU32*)desc.triangles;


for (i = 0; i < numY; i++) {
for (j = 0; j < numX; j++) {
NxU32 i0 = i * (numX+1) + j;
NxU32 i1 = i0 + 1;
NxU32 i2 = i0 + (numX+1);
NxU32 i3 = i2 + 1;
if ((j+i)%2) {
*id++ = i0; *id++ = i2; *id++ = i1;
*id++ = i1; *id++ = i2; *id++ = i3;
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
else {
*id++ = i0; *id++ = i2; *id++ = i3;
*id++ = i0; *id++ = i3; *id++ = i1;
}
}
}
…..
}

2.2 Collision flag between shape and cloth


There are 3 collision flags between shape and cloth:
NX_SF_CLOTH_DRAIN:
NX_SF_CLOTH_TWOWAY:
NX_SF_CLOTH_DISABLE_COLLISION:
You may use these flags like this:
pActor->getShapes()[0]->setFlag(NX_SF_CLOTH_DRAIN, true);

3 Conclusion
We should now know how to create cloth and how Rigid body applies force to cloth.

4 Related Classes, Functions, and Parameters


NxClothMeshDesc
NxClothDesc
NxCookClothMesh()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 114 – Rigidbody and Fluid

1 Introduction
In this lesson, we demonstrate interaction between Fluid and Rigidbody.

2 Fluid
Fluids allow the simulation of liquids and gases using a particle system and emitters.
When a particle collides with a rigid body, a collision impulse is applied to the particle.
By default, collisions with static and dynamic shapes are both considered. To enable
them separately, the user can specify the NX_F_STATIC and the NX_F_DYNAMIC

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
flags in the NxFluidDesc::collisionMethod member; this can also be adjusted during
simulation using NxFluid::setCollisionMethod. It is possible to allow fluids to influence
rigid bodies as they are introduced

NxFluid* CreateFluid(const NxVec3& pos, NxU32 sideNum, NxReal distance,


NxScene* scene)
{
// Create a set of particles
gParticleBufferNum = 0;
NxReal rad = sideNum*distance*0.5f;
for (NxU32 i=0; i<sideNum; i++)
{
for (NxU32 j=0; j<sideNum; j++)
{
for (NxU32 k=0; k<sideNum; k++)
{
NxVec3 p = NxVec3(i*distance,j*distance,k*distance);
if (p.distance(NxVec3(rad,rad,rad)) < rad)
{
p += pos - NxVec3(rad,rad,rad);
gParticleBuffer[gParticleBufferNum++] = p;
}
}
}
}

// Set structure to pass particles, and receive them after every simulation step
NxParticleData particles;
//particles.maxParticles = gParticleBufferCap;
particles.numParticlesPtr = &gParticleBufferNum;
particles.bufferPos = &gParticleBuffer[0].x;
particles.bufferPosByteStride = sizeof(NxVec3);

// Create a fluid descriptor


NxFluidDesc fluidDesc;
fluidDesc.maxParticles = gParticleBufferCap;
fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;
fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;
fluidDesc.motionLimitMultiplier = MOTION_LIMIT_MULTIPLIER;
fluidDesc.packetSizeMultiplier = PACKET_SIZE_MULTIPLIER;
fluidDesc.stiffness = 50;
fluidDesc.viscosity = 22;
fluidDesc.restDensity = 1000;
fluidDesc.damping = 0;
fluidDesc.restitutionForStaticShapes = 0.4;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
fluidDesc.dynamicFrictionForStaticShapes= 0.3;
fluidDesc.collisionResponseCoefficient = 0.5f;
fluidDesc.collisionDistanceMultiplier = 0.1f;
fluidDesc.simulationMethod = NX_F_SPH;
//NX_F_NO_PARTICLE_INTERACTION;

fluidDesc.initialParticleData = particles;
fluidDesc.particlesWriteData = particles;
fluidDesc.flags &= ~NX_FF_HARDWARE;
fluidDesc.flags |= NX_FF_COLLISION_TWOWAY;

NxFluid* fl = gScene->createFluid(fluidDesc);
assert(fl != NULL);
return fl;
}

To enable 2-way interaction between Rigidbody and Fluids, both corresponding shape
flag as well as fluid flag must be set:
fluidDesc.flags |= NxFluidFlag::NX_FF_COLLISION_TWOWAY;
shapeDesc.flags |= NxShapeFlag::NX_SF_FLUID_TWOWAY;

The collision Response Coefficient defines which fraction of the collision impulse
actually gets applied to the colliding rigid body:
fluidDesc.collisionResponseCoefficient = 0.5f;

3 Conclusion
We should now know how to create Fluid and how Fluid collides with Rigidbody.

4 Related Classes, Functions, and Parameters


NxParticleData
NxFluidDesc

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 115 – Rigidbody and Softbody

1 Introduction
In this lesson, we create SoftBody which collides with Rigidbody.

2 SoftBody
SoftBody is a deformable object. It may be used to simulate complex geometry like
a tree with many branches and leaves. In such game scene, there’s always collision
between SoftBody and Rigidbody.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
void SetupAnimalScene()
{
// Create the objects in the scene

NxSoftBodyDesc softBodyDesc;
softBodyDesc.globalPose.t = NxVec3(0.0f, 3.0f, 0.0f);
softBodyDesc.particleRadius = 0.2f;
softBodyDesc.volumeStiffness = 0.5f;
softBodyDesc.stretchingStiffness = 1.0f;
softBodyDesc.friction = 1.0f;
softBodyDesc.attachmentResponseCoefficient = 0.1f;
softBodyDesc.solverIterations = 5;

char *fileName = "froggyNormalized";

char tetFileName[256], objFileName[256], s[256];


sprintf(tetFileName, "%s.tet", fileName);
sprintf(objFileName, "%s.obj", fileName);

ObjMesh *objMesh = new ObjMesh();


objMesh->loadFromObjFile(FindMediaFile(objFileName, s));
gObjMeshes.push_back(objMesh);

NxMat33 rot;
rot.rotX(NxHalfPiF32); //Rotate 90 degrees about X axis;
for (int i = 0; i < 10; i++)
{
softBodyDesc.globalPose.t = NxVec3(0,3+i*3,0);
MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,
FindMediaFile(tetFileName,s), objMesh);
assert(softBody);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
}
else
{
gSoftBodies.push_back(softBody);
NxActor *caps= CreateCapsule(NxVec3(0.0f, 3.0f + i*3.0f, -0.3f), 1.0f,
0.73f, 1.0f);
caps->userData= (void*)&gInvisible;
caps->setGlobalOrientation(rot);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
softBody->getNxSoftBody()->attachToShape(caps->getShapes()[0],
NX_SOFTBODY_ATTACHMENT_TWOWAY);
}
}
}

2.1 Creating SoftBody Mesh


A soft body is defined using a mesh which specifies the soft body's vertices and the
constraints between them. Each constraint is associated with one tetrahedral volume
element and is defined by specifying the 4 vertex indices of the tetrahedron's corner
vertices. Similarly to how triangle meshes are handled in the cloth API, it is necessary
to cook a soft body mesh into a suitable format for simulation. To do so, we need to fill in
an NxSoftBodyMeshDesc structure and pass it to NxCookSoftBodyMesh(). This function
returns a stream of bytes that can either be saved and reused later or fed to
NxScene::createSoftBodyMesh().

bool MySoftBody::generateTetMeshDesc(NxSoftBodyMeshDesc &desc, char *filename,


NxReal scale)
{
static const NxU16 MESH_STRING_LEN = 256;
char s[MESH_STRING_LEN];
int i0,i1,i2,i3;
NxVec3 v;

FILE *f = fopen(filename, "r");


if (!f) return false;

NxArray<NxVec3> tempVertices;
NxArray<NxU32> tempIndices;
NxBounds3 bounds;
bounds.setEmpty();

while (!feof(f)) {
if (fgets(s, MESH_STRING_LEN, f) == NULL) break;

if (strncmp(s, "v ", 2) == 0) { // vertex


sscanf(s, "v %f %f %f", &v.x, &v.y, &v.z);
tempVertices.push_back(v);
bounds.include(v);
}
else if (strncmp(s, "t ", 2) == 0) { // tetra
sscanf(s, "t %i %i %i %i", &i0,&i1,&i2,&i3);
tempIndices.push_back(i0);
tempIndices.push_back(i1);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
tempIndices.push_back(i2);
tempIndices.push_back(i3);
}
}

if(tempVertices.size() == 0) return false;

desc.numVertices = tempVertices.size();
desc.numTetrahedra = tempIndices.size() / 4;
desc.vertexStrideBytes = sizeof(NxVec3);
desc.tetrahedronStrideBytes = 4*sizeof(NxU32);
desc.vertexMassStrideBytes = sizeof(NxReal);
desc.vertexFlagStrideBytes = sizeof(NxU32);
desc.vertices = (NxVec3*)malloc(sizeof(NxVec3)*desc.numVertices);
desc.tetrahedra= (NxU32*)malloc(sizeof(NxU32)*desc.numTetrahedra*4);
desc.vertexMasses = 0;
desc.vertexFlags = 0;
desc.flags = 0;

NxReal diag = bounds.min.distance(bounds.max);


mOffset = (bounds.min + bounds.max) * 0.5f;

NxVec3 *vSrc = (NxVec3*)tempVertices.begin();


NxVec3 *vDest = (NxVec3*)desc.vertices;
for (NxU32 i = 0; i < desc.numVertices; i++, vDest++, vSrc++)
*vDest = ((*vSrc) - mOffset)*scale;
memcpy((NxU32*)desc.tetrahedra, tempIndices.begin(),
sizeof(NxU32)*desc.numTetrahedra*4);

return true;
}

2.2 Collision flag between shape and SoftBody


There are 3 collision flags between shape and cloth:
NX_SF_SOFTBODY_DRAIN:
NX_SF_ SOFTBODY _TWOWAY:
NX_SF_ SOFTBODY _DISABLE_COLLISION:
You may use these flags like this:
pActor->getShapes()[0]->setFlag(NX_SF_ SOFTBODY _DRAIN, true);

3 Conclusion
You should now know how to create SoftBody and how SoftBody collides with
Rigidbody.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
4 Related Classes, Functions, and Parameters
NxSoftBodyMeshDesc
NxSoftBodyDesc
NxCookSoftBodyMesh()
createSoftBodyMesh()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 116 – Rigidbody and ForceField

1 Introduction
In this lesson, we create a ForceField which applies force to Rigidbody. In order to show
ForceField’s track clearly, ForceField’s path is full of particles.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 ForceField
ForceField has two important geometrical properties: one is volume of activity, which is
defined by groups of shapes (similar to actors), and the other is force field kernel
function, which defines the strength of the field throughout the volume of activity.
When an actor enters the volume of ForceField’s activity, force is applied to the actor.
To create a force field, you fill out a descriptor (NxForceFieldDesc) with the desired
parameters and then call NxScene::createForceField:
void SampleVortex::createVortexForceField(const NxVec3& pos, NxActor* actor,
NxScene* scene)
{
assert(scene);
NxForceFieldDesc ffDesc;
NxForceFieldLinearKernelDesc lKernelDesc;
NxForceFieldLinearKernel* linearKernel;

ffDesc.coordinates = NX_FFC_CYLINDRICAL;
ffDesc.actor = actor;
m_forceFieldTransform.id();
ffDesc.pose = m_forceFieldTransform;
lKernelDesc.constant = NxVec3(-30, 4.0f, 0);
lKernelDesc.positionTarget = NxVec3(3,0,0);

NxMat33 m;
m.zero();
m(0,0) = 10;
m(0,1) = -5;
m(0,2) = 0;
lKernelDesc.positionMultiplier = m;
lKernelDesc.noise = NxVec3(5,5,5);
lKernelDesc.velocityTarget = NxVec3(0,0,30);
m.diagonal(NxVec3(1,1,1));
lKernelDesc.velocityMultiplier = m;

lKernelDesc.falloffLinear = NxVec3(5.0f, 0, 0);


lKernelDesc.falloffQuadratic = NxVec3(5.0f, 0, 0);
linearKernel = scene->createForceFieldLinearKernel(lKernelDesc);
ffDesc.kernel = linearKernel;
ffDesc.flags = 0;

m_forceField = scene->createForceField(ffDesc);
assert(m_forceField);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
NxForceFieldShape* s = NULL;
NxBoxForceFieldShapeDesc b;
b.dimensions = NxVec3(5, 7, 5);
b.pose.t = NxVec3(0, 3.5f, 0);
s = m_forceField->getIncludeShapeGroup().createShape(b);

NxForceFieldShapeGroupDesc sgDesc;
sgDesc.flags = NX_FFSG_EXCLUDE_GROUP;
m_excludeGroup = scene->createForceFieldShapeGroup(sgDesc);

NxBoxForceFieldShapeDesc exclude;
exclude.dimensions = NxVec3(2.25f, 1.5f, 1.75f);
exclude.pose.t = NxVec3(8.85f, 1.5f, -10.3f);
m_excludeShape = m_excludeGroup->createShape(exclude);

m_forceField->addShapeGroup(*m_excludeGroup);
}

To create a force field, you fill out a descriptor (NxForceFieldDesc) with the desired
parameters and then call NxScene::createForceField:

• Attach forcefield in an actor (which we use for moving the field around in the
world)

• Attach the force field at the center of the actor

• Constant force of 30 towards the center (which is then counter-acted by radial


forces specified below). constant force of 4 upwards (creating a constant lift on
the objects)

• The target where we want the objects to end up is at radius 3 from the center. We
use Y=0 as the target in along the y-axis together with the m(0,1)=-5 to create a
force directed outwards from the center of forcefield when objects are floating
towards the top of forcefield.

• Setup radial forces, depending on where the objects are positioned

• Set target velocity along the tangent of the vortex to 30 (the other directions to 0)

• You can try some fall-off forces if you e.g. want the vortex to lose power along
the radial direction when the distance from its center increases:

Next step is to define the volume where the force field is active. This volume is
represented by force field shapes which are created inside a group.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
• Attach an include shape, we position this so that it covers the vortex specified
above

• Create an exclude shape, positioned around the shed

3 Conclusion
You now know how to create ForceField and how ForceField affects Rigidbody.

4 Related Classes, Functions, and Parameters


NxForceFieldLinearKernelDesc
NxForceFieldLinearKernel
NxForceFieldDesc
NxForceField
NxBoxForceFieldShapeDesc

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 201 – Revolute Joints

1 Introduction
Joints are connections between rigid bodies. A joint is a point that connects two actors.
In the description of a joint, we tell the actors how they can move with respect to each
other about this point.

A very simple notion of a joint would be to connect the two actors so that they move
perfectly in unison about a point, keeping their orientations aligned and the local vector
between their centers of mass constant. This would be no different than if the two rigid

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
bodies were merged into one big rigid body, putting all the shapes of both actors into one
actor. At the other end of the spectrum, we could imply no relation at all. The two
bodies are free to move however they want with respect to each other about the point.
This is no different than having two separate actors with no relation between them.

A joint lies somewhere between these two extremes, it restricts motion between the two
actors about a point with respect to both rotation and translation. The first joint we learn
about, the revolute joint, allows two actors to rotate with respect to each other about a
single axis through a point. The revolute joint allows a single degree of freedom of
motion: one axis of rotation and no axes of translation. Future joints will unlock other
axes of rotation and translation, allowing freedom of movement over different
combinations of the three axes of rotation and three axes of translation.

2 The Revolute Joint


The first joint we learn about is the revolute joint. This joint is often called the “hinge”
joint, as it can be used to represent the hinge on a door. Take a look at InitNx().

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box1 = NULL;
NxActor* box2 = NULL;

// Joint globals
//NxFixedJoint* fixedJoint = NULL;
NxRevoluteJoint* revJoint = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);


box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

NxVec3 globalAnchor = NxVec3(0.5,5,0);


NxVec3 globalAxis = NxVec3(0,0,1);
revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2

}

We create two actors, a kinematic box suspended in the air and a dynamic box just
beneath it. We create a revolute joint at (0.5,5,0), a point on the left side of both boxes,
on the bottom of the higher one and the top of the lower one. The revolute joint is set to
swing about the positive z-axis (0,0,1). Take a look at CreateRevoluteJoint().

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxRevoluteJointDesc revDesc;

revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);

revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

To create the revolute joint, we need a revolute joint descriptor, NxRevoluteJointDesc.


This descriptor is derived from a generic joint descriptor, NxJointDesc. NxJointDesc
contains basic information about the joint: pointers to the two actors it connects together,
and for each actor, a local anchor (where the joint is located on the actor), a local axis
(the primary axis of rotation about the joint), and a local normal (the secondary axis of
rotation about the joint, perpendicular to the local axis), a joint method describing how
the joint is simulated, and jointFlags, which enable and disable collision between the two
actors connected by the joint and enable and disable visualization of the joint in
wireframe visualization mode. (Note: NxJointDesc has a field called jointFlags, which
are generic joint flags, while NxRevoluteJointDesc, which is a derived class, has an
additional field called flags, which are flags specific to the revolute joint.)

The revolute joint descriptor, NxRevoluteJointDesc, contains information specific to the


revolute joint. It contains joint limit information, joint spring information, and joint
motor information, all of which we will learn about in future lessons. For now all we
need from NxRevoluteJointDesc is the information from NxJointDesc and the fact that
we’re making a revolute joint.

Going over the joint creation then, we connect the two actors to the joint.

revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
We set the global anchor on the joint, the point about which the two actors will rotate
defined in global space.

revDesc.setGlobalAnchor(globalAnchor);

We set the global axis on the joint, the axis around which the two actors will rotate, also
defined in global space.

revDesc.setGlobalAxis(globalAxis);

We enable collision between the two actors that are connected by the joint (by default,
collision between the two actors is disabled).

revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

Finally, we call createJoint() from the scene and pass in our revolute joint descriptor to
get a pointer to our revolute joint.

return (NxRevoluteJoint*)gScene->createJoint(revDesc);

3 Playing Around with the Joint


You have made your first joint. Congratulations! Take your time to play around with
the lesson.

Hit “p” to unpause the demo. Notice box2 swings away from the box1. Use “umijkl”
to push box1 around. Note it is restricted to revolve around the z-axis. Since collision
is enabled, it is stopped when it runs into box2.

Try disabling collision and see how the default revolute joint works free of collision.

Turn box1 into a dynamic actor by removing the kinematic actor flag. Note how both
actors collapse to the ground jointed together. Try applying forces to both actors and
watch how they move with respect to each other and the joint.

Make box1 into a static actor by passing in a density of 0. Try running the demo.
Notice that you get an unhandled exception error. NxScene::createJoint() does not
accept static actors to joint other actors to. You must change the CreateRevoluteJoint()
line in InitNx() by replacing box1 with NULL.

void InitNx()
{

revJoint = CreateRevoluteJoint(NULL, box2, globalAnchor, globalAxis);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4

}

The demo plays exactly as before, since we never moved the kinematic actor, box1. Had
we moved it, we would have pulled box2 along with it. Assigning a static actor to a
joint is redundant. Since the static actor will be baked into the scene and never move,
the point where you define the joint, the globalAnchor, will also never move and remain
frozen in the scene. Therefore, you only need to set the the global position of the joint in
the scene, its axis, and the actor it’s attached to.

Change the global anchor and axis in CreateRevoluteJoint() so the globalAnchor is


between box1 and box2 along the front of both boxes, and the globalAxis is the x-axis.

NxVec3 globalAnchor = NxVec3(0,5,-1);


NxVec3 globalAxis = NxVec3(1,0,0);

The boxes are now connected differently, box1 swinging forward rather than to the left.
See what other different connections you can make changing just globalAnchor and
globalAxis.

4 The Fixed Joint


Note the fixed joint lines in the lesson we have commented out.

// Joint globals
//NxFixedJoint* fixedJoint = NULL;
NxRevoluteJoint* revJoint = NULL;

NxFixedJoint* CreateFixedJoint(NxActor* a0, NxActor* a1, NxVec3 globalAnchor,
NxVec3 globalAxis)
{
NxFixedJointDesc fixedDesc;

fixedDesc.actor[0] = a0;
fixedDesc.actor[1] = a1;
fixedDesc.setGlobalAnchor(globalAnchor);
fixedDesc.setGlobalAxis(globalAxis);

return (NxFixedJoint*)gScene->createJoint(fixedDesc);
}

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

NxVec3 globalAnchor = NxVec3(0.5,5,0);


NxVec3 globalAxis = NxVec3(0,0,1);

// fixedJoint = CreateFixedJoint(box1,box2, globalAnchor, globalAxis);


revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;

}

Comment out the revolute joint lines above them and uncomment the fixed joint lines.
Run the lesson. The boxes are now connected by a fixed joint. This is the SDK’s trivial
joint, a joint that moves its two actors in unison with no rotational or translational
freedom. The boxes behave no differently that a single actor with the two box shapes
added.

Fixed joints are an easy way to “weld” two actors together.

5 Joint Projection
Try adding the following lines to the CreateRevoluteJoint() function.

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
...
revDesc.projectionMode = NX_JPM_POINT_MINDIST;
revDesc.projectionDistance = 1.0f;
revDesc.projectionAngle = 0.0872f; //about 5 degrees in radians.

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

You can use projection modes to specify how much to “tolerate” joint separation.

Bodies constrained by joints are moved iteratively under normal simulation. Because of
errors introduced during this iteration, joints may need to occasionally be forced back to
within a certain projection distance of each other. Joint projection is a method to combat
the inaccuracy of joint simulation. Ideally, the projection distance would be zero, but
this puts a heavy restriction on the dynamic motion of the bodies, and the results can end
up looking forced and non-physical. Instead the projection distance should be set to a
small permissible joint error value. You can set this value to taste depending on how
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

6
much dynamic motion you want from the bodies and how much you can tolerate them
deviating from their joint constraints at any given time step.

You can specify the maximum error you wish to allow in a joint before projection kicks
in.

6 Conclusion
Joints are the next step beyond rigid bodies in physics simulations. They are
connections between the rigid bodies that we populate our worlds with. We will learn
many more types of joints and all their properties, how to constrain them, put springs and
motors on them, etc., etc. They are a very useful and powerful feature of the PhysX
SDK.

7 Related Classes, Functions, and Parameters


NxScene
createJoint()

NxJoint
getActors()
getBreakable()
getGlobalAnchor()
getGlobalAxis()
getName()
getScene()
getState()
getType()
is()
isCylindricalJoint()
isD6Joint()
isDistanceJoint ()
isFixedJoint ()
isPointInPlaneJoint ()
isPointOnLineJoint ()
isPrismaticJoint ()
isPulleyJoint ()
isRevoluteJoint()
isSphericalJoint()
setBreakable()
setGlobalAnchor()
setGlobalAxis()

NxFixedJoint

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

7
NxRevoluteJoint

NxJointDesc
actor[0]
actor[1]
setGlobalAnchor()
setGlobalAxis()
jointFlags

NxFixedJointDesc
NxRevoluteJointDesc

NxJointFlag
NX_JF_COLLISION_ENABLED

NxJointDesc
NX_JOINT_FIXED
NX_JOINT_REVOLUTE
NX_JOINT_SPHERICAL
NX_JOINT_PRISMATIC
NX_JOINT_CYLINDRICAL
NX_JOINT_POINT_ON_LINE
NX_JOINT_POINT_IN_PLANE
NX_JOINT_PULLEY
NX_JOINT_DISTANCE
NX_JOINT_D6

NxJointProjectionMode
NX_JPM_NONE
NX_JPM_POINT_MINDIST

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

8
Lesson 202 – Joint Springs and Motors

1 Introduction
Joint springs are a way to specify an angle between the jointed bodies that the bodies tend
to move toward. They define a natural resting angle between the joint axes of the two
jointed bodies. Joint motors are used to supply a relative torque between two bodies
connected by a revolute or spherical joint.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Joint Springs
Box2 is once again hanging from kinematic box1 and swings from it by a revolute joint
about the z-axis.

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxRevoluteJointDesc revDesc;
revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);

revDesc.flags |= NX_RJF_SPRING_ENABLED;

NxSpringDesc springDesc;
springDesc.spring = 5000;
springDesc.damper = 50;
springDesc.targetValue = 0.5*NxPi;

revDesc.spring = springDesc;

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

box2->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);

NxVec3 globalAnchor = NxVec3(0.5,5,-1);


NxVec3 globalAxis = NxVec3(0,0,-1);
revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;

}

To put a spring on the joint, first we enable a spring on the revolute joint descriptor.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
revDesc.flags |= NX_RJF_SPRING_ENABLED;

Then we attach a spring descriptor to the revolute joint descriptor.

NxSpringDesc springDesc;
springDesc.spring = 5000;
springDesc.damper = 50;
springDesc.targetValue = 0.5*NxPi;

revDesc.spring = springDesc;

The target value on the spring is the angle the local normal of box2 is pushed toward with
respect to the local normal of box1 around the global axis, negative z. This angle is
0.5*NxPi, so box2 rotates about z so that it is perpendicular to the ground once the
simulation begins.

springDesc.targetValue = 0.5*NxPi;

The force that pushes box1 toward its target value is 5000. The spring is damped to 50
so box1 doesn’t keep swinging back and forth around the target angle. It eventually
loses energy and comes to rest.

springDesc.spring = 5000;
springDesc.damper = 50;

Hit “b” to see how the joint axes are aligned. When box2 is at rest, its local normal is
rotated 90 degrees (0.5*NxPi) away from box1’s local normal around box2’s local axis.

Make box1 dynamic by taking out box1->raiseBodyFlag(NX_BF_KINEMATIC) and


watch how the two boxes move with respect to each other. The actors will maintain a 90
degree angle with respect to each other unless otherwise forced apart.

3 Joint Motors
Take a look at the new CreateRevoluteJoint() and InitNx():

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxRevoluteJointDesc revDesc;
revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
revDesc.flags |= NX_RJF_MOTOR_ENABLED;

NxMotorDesc motorDesc;
motorDesc.velTarget = 1000;
motorDesc.maxForce = 500;
motorDesc.freeSpin = true;

revDesc.motor = motorDesc;

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

box2->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
box2->setAngularDamping(0);
box2->setMaxAngularVelocity(700);

NxVec3 globalAnchor = NxVec3(0.5,5,-1);


NxVec3 globalAxis = NxVec3(0,0,-1);
revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;

}

We disable gravity and set the angular damping to 0 so we can see the motor at work
without interference.

box2->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
box2->setAngularDamping(0);

We also do not make the boxes collidable so box2 can spin freely through box1. Finally,
we set the maximum angular velocity to 700 so we can get box2 spinning at a good clip.
The default maximum angular velocity for objects is 7 and is set this low for objects in
general for computational and performance reasons. However, you can set the
maximum angular velocity higher by changing the default or setting it higher on a per-
actor basis, as above, to simulate objects like wheels or a spinning top which need to
rotate at a higher velocity.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
4 Starting the Motor
Run the lesson and hit “p” to unpause the physics. The lower body will start to
accelerate around the kinematic upper body until it comes to a constant rotational
velocity. The torque supplied to box2 is maxForce. The target rotational velocity is
velTarget. The motor is set to freeSpin, so if the motor is turned off during the
simulation (maxForce set to 0), box2 will continue spinning.

NxMotorDesc motorDesc;
motorDesc.velTarget = 1000;
motorDesc.maxForce = 500;
motorDesc.freeSpin = true;

revDesc.motor = motorDesc;

If freeSpin is not set, a braking force will be applied and box2 will eventually come to a
halt.

5 Adjusting the Motor Velocity


When you run the demo, you'll notice that hitting “t” will toggle the speed of the motor.
Hitting “t” calls the ShiftMotorVelocity() function.

void ShiftMotorVelocity()
{
NxRevoluteJointDesc revDesc;
revJoint->saveToDesc(revDesc);

if (revDesc.motor.velTarget == 1000)
revDesc.motor.velTarget = 10;
else
revDesc.motor.velTarget = 1000;

revJoint->loadFromDesc(revDesc);
}

You can adjust the motor on the fly by saving away the joint descriptor, changing the
pertinent values, in this case, velTarget, the motor's target velocity, and loading the
descriptor back into the joint. In this case, we save away the joint descriptor, change
velTarget, the motor's target velocity, and load it back into the joint, adjusting the target
velocity of the joint's motor. You can do this with other object descriptors as well, i.e.,
save away the descriptor, adjust values on the descriptor, then reload it into the object to
change the properties of an object. You will eventually be doing this quite a bit with the
6 DOF joint descriptors to reconfigure the joint parameters on the fly.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
6 Conclusion
Joint springs are very useful for making linked bodies that have a natural resting
orientation the springs will pull them back to. You can make things like a rope or a
mattress using joint springs. Divide the rope into segments, joint them together, then
attach spring descriptors to the joints. You can apply a force to bend the rope, and the
spring forces will cause the jointed bodies composing the rope to return to their rest
configurations once the force is gone.

7 Related Classes, Functions, and Parameters


NxRevoluteJointDesc
spring
flags

NxSpringDesc
spring
damper
targetValue

NxRevoluteJointFlag
NX_RJF_SPRING_ENABLED

NxMotorDesc
velTarget
maxForce
freeSpin

NxRevoluteJointFlag
NX_RJF_MOTOR_ENABLED

NxRevoluteJoint
setMotor()

NxActor
setMaxAngularVelocity()

NxParameter
NX_MAX_ANGULAR_VELOCITY

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Lesson 203 – Spherical Joints

1 Introduction
Until now we have been using just the revolute joint, a joint with one degree of freedom,
it may freely rotate about its primary axis.

We now introduce the spherical joint. Spherical joints allow three degrees of freedom:
freedom to rotate about every joint axis while remaining in a fixed position.

Spherical joints are commonly used to simulate shoulder joints and other ball-and-socket
joints. The joints are free to rotate about any axis, but may have limits placed on the
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
amount they can rotate about each axis. For instance, you can swing your shoulder
through a wide range parallel to your torso facing, but only about 90 degrees
perpendicular to your torso facing. You can then twist your shoulder through about 90
degrees.

2 Spherical Joints
In InitNx(), we make two capsules, one above the other, and attach them by a spherical
joint, much like we have been previously attaching the boxes by a revolute joint.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* capsule1 = NULL;
NxActor* capsule2 = NULL;

// Joint globals
NxSphericalJoint* sphericalJoint = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

capsule1 = CreateCapsule(NxVec3(0,5,0), 3, 0.5, 10);


capsule1->raiseBodyFlag(NX_BF_KINEMATIC);
capsule2 = CreateCapsule(NxVec3(0,1,0), 3, 0.5, 10);
capsule2->setLinearDamping(0.5);

NxVec3 globalAnchor = NxVec3(0,5,0);


NxVec3 globalAxis = NxVec3(0,1,0);
sphericalJoint = CreateSphericalJoint(capsule1, capsule2, globalAnchor, globalAxis);

gSelectedActor = capsule2;

}

We create the spherical joint with CreateSphericalJoint():

NxSphericalJoint* CreateSphericalJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxSphericalJointDesc sphericalDesc;
sphericalDesc.actor[0] = a0;
sphericalDesc.actor[1] = a1;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
sphericalDesc.setGlobalAnchor(globalAnchor);
sphericalDesc.setGlobalAxis(globalAxis);

sphericalDesc.flags |= NX_SJF_SWING_LIMIT_ENABLED;
sphericalDesc.swingLimit.value = 0.3*NxPi;

sphericalDesc.flags |= NX_SJF_TWIST_LIMIT_ENABLED;
sphericalDesc.twistLimit.low.value = -0.05*NxPi;
sphericalDesc.twistLimit.high.value = 0.05*NxPi;

return (NxSphericalJoint*)gScene->createJoint(sphericalDesc);
}

Capsule1 is the upper, kinematic capsule. Capsule2 is the lower, dynamic capsule. The
globalAxis of the spherical joint is (0,1,0) meaning the local axes of capsule1 and
capsule2 both start at (0,1,0). Their local normals start at (0,0,1) and local binormals
start at (-1,0,0).

We enable a swing limit, capsule2’s local axis is allowed to swing about its local normal
and binormal up to 0.3*NxPi from its original position in any direction.

sphericalDesc.flags |= NX_SJF_SWING_LIMIT_ENABLED;
sphericalDesc.swingLimit.value = 0.3*NxPi;

We also enable a twist limit as well, which allows capsule2’s local normal to rotate from
its original position to a minimum value of -0.05*NxPi and a maximum value of
0.05*NxPi.

sphericalDesc.flags |= NX_SJF_TWIST_LIMIT_ENABLED;
sphericalDesc.twistLimit.low.value = -0.05*NxPi;
sphericalDesc.twistLimit.high.value = 0.05*NxPi;

Try playing around with the lesson. Hit “b” and notice how the swing limit is shown as
a red cone around capsule2’s local axis and the twist limit is shown as a red pie slice
around capsule2’s local normal (the local binormal is not shown as it is not involved in
the joint limits).

3 Conclusion
You now know how to construct a spherical joint. We will be keeping the
CreateSphericalJoint() and CreateRevoluteJoint() functions as well as other joint creation
functions that we encounter for use in future lessons.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
4 Related Classes, Functions, and Parameters
NxSphericalJointDesc
swingLimit
twistLimit
flags

NxJointLimitPairDesc
high
low

NxJointLimitDesc
value
restitution

NxSphericalJointFlag
NX_SJF_SWING_LIMIT_ENABLED
NX_SJF_TWIST_LIMIT_ENABLED

NxSphericalJoint

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 204 – Prismatic Joints

1 Introduction
Prismatic joints are the first translational joint we will learn. It is purely translational
and only free to move along its primary axis. A good example of a prismatic joint is a
shock absorber.

2 Prismatic Joints
// Actor globals

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
NxActor* groundPlane = NULL;
NxActor* box1 = NULL;
NxActor* box2 = NULL;

// Joint globals
NxPrismaticJoint* prismaticJoint = NULL;

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(1,2,1), 10);
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,3,0), NxVec3(0.5,2,0.5), 10);
box2->setLinearDamping(0.5);

NxVec3 globalAnchor = NxVec3(0,7,0);


NxVec3 globalAxis = NxVec3(0,1,0);
prismaticJoint = CreatePrismaticJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;

}

We make two box actors and attach them at a point two units up inside the top actor,
(0,7,0).

NxPrismaticJoint* CreatePrismaticJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxPrismaticJointDesc prismaticDesc;
prismaticDesc.actor[0] = a0;
prismaticDesc.actor[1] = a1;
prismaticDesc.setGlobalAnchor(globalAnchor);
prismaticDesc.setGlobalAxis(globalAxis);

NxJoint* joint = gScene->createJoint(prismaticDesc);

joint->setLimitPoint(globalAnchor);
joint->addLimitPlane(-globalAxis, globalAnchor + 1.5*globalAxis);
joint->addLimitPlane(globalAxis, globalAnchor - 1.5*globalAxis);

return (NxPrismaticJoint*)joint;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
We attach a limit point to the top of the lower actor and limit planes 3 meters apart inside
the upper actor and perpendicular to the globalAxis, the axis of motion for the prismatic
joint. If we didn’t put these limiting planes in, the lower box would be free to move
along the primary axis with nothing inherent in the joint to stop it.

Run the lesson and hit “p” to unpause the simulation. Notice the lower box falls and
gets stopped short of the ground by the lower limit plane. If you push it upward with
“u”, it will move upward until it is stopped by the upper limit plane.

3 Conclusion
This lesson demonstrates how to set up a prismatic joint with limit planes defining the
range of motion for the joint along its primary axis.

4 Related Classes, Functions, and Parameters


NxPrismaticJointDesc
NxPrismaticJoint

NxJoint
setLimitPoint()
getLimitPoint()
addLimitPlane()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 205 – Cylindrical and Fixed Joints

1 Introduction
Cylindrical joints are the first joint we introduce with both rotational and translational
motion. The jointed actors are free to rotate about the primary axis like a revolute joint
as well as translate about the primary axis like a prismatic joint.

2 Cylindrical Joints
Take a look at the new InitNx():

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
// Actor globals
NxActor* groundPlane = NULL;
NxActor* capsule1 = NULL;
NxActor* capsule2 = NULL;

// Joint globals
NxCylindricalJoint* cylJoint = NULL;

void InitNx()
{

capsule1 = CreateCapsule(NxVec3(0,5,0), 4, 0.85, 10);
capsule1->raiseBodyFlag(NX_BF_KINEMATIC);
capsule2 = CreateCapsule(NxVec3(0,3,0), 3, 0.5, 10);
capsule2 ->setLinearDamping(0.5);
capsule2->setAngularDamping(0.5);

NxVec3 globalAnchor = NxVec3(0,7,0);


NxVec3 globalAxis = NxVec3(0,1,0);
cylJoint = CreateCylindricalJoint(capsule1, capsule2, globalAnchor, globalAxis);

gSelectedActor = capsule2;

}

The setup for the cylindrical joint is almost identical to the prismatic joint, as is the
cylindrical joint creation function.

NxCylindricalJoint* CreateCylindricalJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxCylindricalJointDesc cylDesc;
cylDesc.actor[0] = a0;
cylDesc.actor[1] = a1;
cylDesc.setGlobalAnchor(globalAnchor);
cylDesc.setGlobalAxis(globalAxis);

NxJoint* joint = gScene->createJoint(cylDesc);

joint->setLimitPoint(globalAnchor);

// Add up-down limiting planes


joint->addLimitPlane(-globalAxis, globalAnchor + 1*globalAxis);
joint->addLimitPlane(globalAxis, globalAnchor - 1*globalAxis);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
return (NxCylindricalJoint*)joint;
}

We add a limit point to the top of the lower capsule and limit planes that slice through the
upper actor parallel to the ground. Use “umijkl” to move the lower actor up and down.
Hit “f” to switch from Force Mode to Torque Mode and see how you can rotate the lower
actor in place as well.

3 Conclusion
Cylindrical joints are very similar to prismatic joints with the exception that the actors are
allowed to rotate about the joint’s primary axis as well as translate along it.

4 Related Classes, Functions, and Parameters


NxCylindricalJointDesc
NxCylindricalJoint

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 206 – Point-on-Line Joints

1 Introduction
You can think of a point-on-line joint as a spherical joint you can translate along one
axis. It is free to rotate about all three joint axes and can translate about one axis. An
example of a prismatic joint would be something like a hanger on a shower curtain rod
(that is free as well to twist along its axis perpendicular to the rod).

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Point-on-Line Joints
InitNx() is now set up as follows:

// Actor globals
NxActor* groundPlane = NULL;
NxActor* wedge = NULL;
NxActor* pyramid = NULL;

// Joint globals
NxPointOnLineJoint* polJoint = NULL;

void InitNx()
{

wedge = CreateDownWedge(NxVec3(0,6,0), NxVec3(5,1,1), 10);
wedge->raiseBodyFlag(NX_BF_KINEMATIC);
pyramid = CreatePyramid(NxVec3(0,4,0), NxVec3(1,1,1), 10);
pyramid->setLinearDamping(0.5);
pyramid->setAngularDamping(0.5);

NxVec3 globalAnchor = NxVec3(0,6,0);


NxVec3 globalAxis = NxVec3(1,0,0);
polJoint = CreatePointOnLineJoint(wedge, pyramid, globalAnchor, globalAxis);

gSelectedActor = pyramid;

}

We make a wedge and a pyramid. CreateDownWedge() creates a long V-shaped convex


object, while CreatePyramid() creates a full, triangular pyramid, unlike the half-pyramid
we used in the convex object lesson. We place the pyramid underneath the wedge so the
tip of the pyramid intersects the bottom of the V-wedge. We then join the two actors by
a point-on-line joint in the middle of the V-wedge, with the primary axis of the joint
pointing along the x-axis (this is the direction the pyramid will move along the wedge).

NxPointOnLineJoint* CreatePointOnLineJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxPointOnLineJointDesc polDesc;
polDesc.actor[0] = a0;
polDesc.actor[1] = a1;
polDesc.setGlobalAnchor(globalAnchor);
polDesc.setGlobalAxis(globalAxis);
polDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
NxJoint* joint = gScene->createJoint(polDesc);

return joint->isPointOnLineJoint();
}

We have enabled collision between the two actors on the joint so the pyramid gets
stopped by the geometry of the wedge if it tries to swing over the top. We have no
collision geometry to stop the pyramid from translating off the wedge however. To stop
the pyramid from going off either end of the wedge we use limit planes.

3 “Upcasting” from an NxPointOnLineJoint to an NxJoint


Instead of setting up the limit planes in the CreatePointOnLineJoint() function, we return
the joint as an NxPointOnLineJoint and upcast it to an NxJoint in InitNx(). We then use
the NxJoint functions to add the limit points and planes to the joint.

We explain this upcasting here as the NxPointOnLineJoint class is not actually derived
from the NxJoint class. NxPointOnLineJoint class contains a pointer to its NxJoint
which you must reference to get access to the limit point and plane functions.

The code contains two methods for this “upcast”.

void InitNx()
{

// UPCAST METHOD #1
NxJoint* jointPtr = &polJoint->getJoint();
jointPtr->setLimitPoint(globalAnchor);
// Add left-right limiting planes
jointPtr->addLimitPlane(-globalAxis, globalAnchor + 5*globalAxis);
jointPtr->addLimitPlane(globalAxis, globalAnchor - 5*globalAxis);

}

The first method accesses the NxJoint attached to the NxPointOnLine joint class by
value. The second method is #if 0’d out.

void InitNx()
{

// UPCAST METHOD #2
NxPointOnLineJoint& polJointRef = *polJoint;
NxJoint& jointRef = polJointRef;
jointRef.setLimitPoint(globalAnchor);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
// Add left-right limiting planes
jointRef.addLimitPlane(-globalAxis, globalAnchor + 5*globalAxis);
jointRef.addLimitPlane(globalAxis, globalAnchor - 5*globalAxis);

}

The second method access the NxJoint attached to the NxPointOnLine joint class by
reference.

Remember this method of upcasting as the other types of joints build on NxJoint in the
same way, and there are other classes in the SDK that use this style of inheritance in
favor of standard C++ inheritance as well.

4 Conclusion
You now know how to use the point-on-line joint. You also know how to “upcast” from
a “derived” joint class to the “base” joint class.

5 Related Classes, Functions, and Parameters


NxPointOnLineJointDesc
NxPointOnLineJoint

NxJointFlag
NX_JOINT_COLLISION_ENABLED

NxJoint
setLimitPoint()
addLimitPlane()
isPointOnLineJoint()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 207 – Point-in-Plane Joints

1 Introduction
The last joint we will learn about is the point-in-plane joint. This joint allows its actors
to rotate about all axes with respect to each other and translate along two axes. An
example of a point-in-plane joint would be a refrigerator magnet, something that moves
along a planar surface and is free to rotate around any axis while attached to the plane.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Point-in-Plane Joints
Instead of the down wedge from the last lesson, we make a kinematic box that we attach
the pyramid to.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;
NxActor* pyramid = NULL;

// Joint globals
NxPointInPlaneJoint* pipJoint = NULL;

void InitNx()
{

box = CreateBox(NxVec3(0,6,0), NxVec3(5,0.5,5), 10);
box->raiseBodyFlag(NX_BF_KINEMATIC);
pyramid = CreatePyramid(NxVec3(0,4,0), NxVec3(1,1,1), 10);
pyramid->setLinearDamping(0.5);
pyramid->setAngularDamping(0.5);

NxVec3 globalAnchor = NxVec3(0,6,0);


NxVec3 globalAxis = NxVec3(0,1,0);
pipJoint = CreatePointInPlaneJoint(box, pyramid, globalAnchor, globalAxis);

gSelectedActor = pyramid;

}

The globalAnchor is at the tip of the pyramid and in the middle and along the bottom
surface of the box.

NxPointInPlaneJoint* CreatePointInPlaneJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxPointInPlaneJointDesc pipDesc;
pipDesc.actor[0] = a0;
pipDesc.actor[1] = a1;
pipDesc.setGlobalAnchor(globalAnchor);
pipDesc.setGlobalAxis(globalAxis);
pipDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

NxJoint* joint = gScene->createJoint(pipDesc);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
return joint->isPointInPlaneJoint();
}

We enable collision between the pyramid and the box, as before, and build the limiting
planes in InitNx().

void InitNx()
{

NxJoint* jointPtr = &pipJoint->getJoint();
jointPtr->setLimitPoint(globalAnchor);
// Add left-right and up-down limiting planes
jointPtr->addLimitPlane(NxVec3(1,0,0), globalAnchor - 5*NxVec3(1,0,0));
jointPtr->addLimitPlane(NxVec3(-1,0,0), globalAnchor + 5*NxVec3(1,0,0));
jointPtr->addLimitPlane(NxVec3(0,0,1), globalAnchor - 5*NxVec3(0,0,1));
jointPtr->addLimitPlane(NxVec3(0,0,-1), globalAnchor + 5*NxVec3(0,0,1));

}

The limiting planes are flush with the edges of the box and perpendicular to the ground.
They catch the pyramid by the tip if it tries to move off any side of the box.

3 Conclusion
This concludes the point-in-plane joint training program. You now know how to build
all the major types of joints in the SDK, attach springs and motors to them as well as
limits in the form of limit values and limit planes. Joints are the heart of the
intermediate lessons, they are the primary method to make connections between rigid
bodies in the PhysX SDK.

4 Related Classes, Functions, and Parameters


NxPointInPlaneJointDesc
NxPointInPlaneJoint

NxJoint
setLimitPoint()
addLimitPlane()
isPointInPlaneJoint()

NxJointFlag
NX_JOINT_COLLISION_ENABLED

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 208 – Pulley Joints

1 Introduction
Pulley joints are joints which simulate a pulley. Normally, this is a device which is a
wheel that has a rope hanging over it that holds up two objects. In our simulation, this
joint consists of two points through which our actors are connected by a “rope”, the two
points feeding rope to the two actors through a pulley mechanism. In this way, you can
make a pulley system more complicated than just a wheel. You can change the
mechanics of the device so the pulley wheel feeds rope to one actor or the other at
different rates. Or you can make it simpler than a wheel, by setting the pulley exits to
the same point. The actors would then be connected by a rope which must pass through
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
a single point in space. We specify the rest length of the rope and the stiffness of the
rope. We can flag whether or not the rope is rigid, i.e., you can use one actor to “push”
the other actor through the pulley assembly.

2 Pulley Joints
In InitNx(), we create two capsules and connect them with a pulley joint.

// Actor globals
...
NxActor* capsule1 = NULL;
NxActor* capsule2 = NULL;

// Motor for pulley joint


NxMotorDesc gMotorDesc;

// Joint globals
NxPulleyJoint* pulleyJoint = NULL;

void InitNx()
{
...
// Create the objects in the scene
...
capsule1 = CreateCapsule(NxVec3(-1,4,0), 1, 0.5, 10);
capsule2 = CreateCapsule(NxVec3(1,4,0), 1, 0.5, 10);

// Motor specs
gMotorDesc.maxForce = NX_MAX_REAL;
gMotorDesc.freeSpin = false;
gMotorDesc.velTarget = 0;

// Create pulley joint


NxVec3 pulley1 = NxVec3(-1,8,0);
NxVec3 pulley2 = NxVec3(1,8,0);
NxVec3 globalAxis = NxVec3(0,-1,0);
pulleyJoint = CreatePulleyJoint(capsule1, capsule2, pulley1, pulley2, globalAxis, 4,
1, gMotorDesc);

gSelectedActor = capsule1;
...
}

We make the two suspension points of the pulley at (-1,8,0) and (1,8,0).

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
NxVec3 pulley1 = NxVec3(-1,8,0);
NxVec3 pulley2 = NxVec3(1,8,0);

We set the global axis to be the negative y-axis.

NxVec3 globalAxis = NxVec3(0,-1,0);

We pass these in to CreatePulleyJoint().

NxPulleyJoint* CreatePulleyJoint(NxActor* a0, NxActor* a1, const NxVec3& pulley0,


const NxVec3& pulley1, const NxVec3& globalAxis, NxReal distance, NxReal ratio,
const NxMotorDesc& motorDesc)
{
NxPulleyJointDesc pulleyDesc;
pulleyDesc.actor[0] = a0;
pulleyDesc.actor[1] = a1;
pulleyDesc.localAnchor[0] = NxVec3(0,2,0);
pulleyDesc.localAnchor[1] = NxVec3(0,2,0);
pulleyDesc.setGlobalAxis(globalAxis);

pulleyDesc.pulley[0] = pulley0; // suspension points of two bodies in world space


pulleyDesc.pulley[1] = pulley1; // suspension points of two bodies in world space
pulleyDesc.distance = distance; // the rest length of the rope connecting the two
objects. The distance is computed as ||(pulley0 - anchor0)|| + ||(pulley1 - anchor1)|| *
ratio.
pulleyDesc.ratio = ratio; // transmission ratio
pulleyDesc.flags = NX_PJF_IS_RIGID; // This is a combination of the bits defined
by ::NxPulleyJointFlag.
pulleyDesc.stiffness = 1; // how stiff the constraint is, between 0 and 1 (stiffest)
pulleyDesc.motor = motorDesc;

// pulleyDesc.projectionMode = NX_JPM_NONE;
// pulleyDesc.projectionMode = NX_JPM_POINT_MINDIST;

pulleyDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

return (NxPulleyJoint*)gScene->createJoint(pulleyDesc);
}

We don't set a global anchor on this joint like we usually do. Our joint is defined by the
distance from an actor's suspension point to its anchor, therefore we set set our anchors
locally.

pulleyDesc.localAnchor[0] = NxVec3(0,2,0);
pulleyDesc.localAnchor[1] = NxVec3(0,2,0);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
Each capsule actor has mid-section height 1 and radius 0.5 for a total height of 2. With
local anchors of (0,2,0), the anchors get placed at the tops of the capsules.

pulleyDesc.pulley[0] = pulley0; // suspension points of two bodies in world space


pulleyDesc.pulley[1] = pulley1; // suspension points of two bodies in world space

The capsules are placed at (-1,4,0) and (1,4,0). Each has a height of 2. Their local
anchors are (0,2,0), so their anchors reside at (-1,6,0) and (1,6,0). pulley0 is (-1,8,0) and
pulley1 is (1,8,0). Therefore, capsule1's pulley rope hangs from (-1,8,0) and attaches to
capsule1 at (-1,6,0). Capsule2's pulley rope hangs from (1,8,0) and attaches to capsule2
at (1,6,0).

pulleyDesc.distance = distance; // the rest length of the rope connecting the two
objects. The distance is computed as ||(pulley0 - anchor0)|| + ||(pulley1 - anchor1)|| *
ratio.

We provide our pulley joint with a distance of 4 meters. This is the length of the rope
and it currently is taut. The length from pulley0 to anchor0 is 2 and the length form
pulley1 to anchor1, times the ratio of 1, is 2, therefore the total distance along the pulley
is 4 meters.

pulleyDesc.ratio = ratio; // transmission ratio

Our transmission ratio is 1, which means pulling capsule1 down 1 meter will cause
capsule2 to rise 1 meter. Try changing the distance to 6 and the ratio to 2.

pulleyJoint = CreatePulleyJoint(capsule1, capsule2, pulley1, pulley2, globalAxis, 6,


2, gMotorDesc);

Run the demo. You'll notice the pulley looks exactly the same as before. If you pull
down on capsule2, the capsule on the right, it will move down twice as far as capsule1 is
pulled up. Therefore, for every 2 meters you move capsule2 away from or toward its
suspension point, you will move capsule1 1 meter toward or away from its suspension
point. This simulates a compound pulley. Capsule2 will move twice as far relative to
capsule1. It will also push and pull capsule1 with twice the force that is applies through
its suspension point.

pulleyDesc.flags = NX_PJF_IS_RIGID; // this is a combination of the bits defined


by ::NxPulleyJointFlag

This marks the pulley rope as rigid. You'll notice you can push through the pulley
mechanism as well as pull through it. If you take away this flag, you will only be able to
pull through it.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
pulleyDesc.motor = motorDesc;

Although we have attached a motor descriptor to our pulley, it currently isn't enabled. It
also has a zero velocity target. Add the NX_PJF_MOTOR_ENABLED to the descriptor
flags and set a velocity target of 10 to power the pulley to raise capsule2 to its suspension
point.

// Motor for pulley joint


NxMotorDesc gMotorDesc;

// Joint globals
NxPulleyJoint* pulleyJoint = NULL;
...
void InitNx()
{
...
// Motor specs
gMotorDesc.maxForce = NX_MAX_REAL;
gMotorDesc.freeSpin = false;
gMotorDesc.velTarget = 10;

// Create pulley joint


NxVec3 pulley1 = NxVec3(-1,8,0);
NxVec3 pulley2 = NxVec3(1,8,0);
NxVec3 globalAxis = NxVec3(0,-1,0);
pulleyJoint = CreatePulleyJoint(capsule1, capsule2, pulley1, pulley2, globalAxis, 4,
1, gMotorDesc);
...
}
...
NxPulleyJoint* CreatePulleyJoint(NxActor* a0, NxActor* a1, const NxVec3& pulley0,
const NxVec3& pulley1, const NxVec3& globalAxis, NxReal distance, NxReal ratio,
const NxMotorDesc& motorDesc)
{
NxPulleyJointDesc pulleyDesc;
...
pulleyDesc.flags = NX_PJF_IS_RIGID | NX_PJF_MOTOR_ENABLED; // this is
a combination of the bits defined by ::NxPulleyJointFlag.
...
}

Set the motor's velocity target to -10 to get the pulley to raise capsule1 to its suspension
point.

pulleyDesc.stiffness = 1; // how stiff the constraint is, between 0 and 1 (stiffest)


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
This is the stiffness of the pulley. At 1, it's at maximum stiffness and the pulley
distances will adhere to the its length and gear ratio exactly. Less than 1, there will be
some give in the rope.

3 Conclusion
The pulley joint is an additional joint supplied for gameplay purposes by the SDK.

4 Related Classes, Functions, and Parameters


NxPulleyJointDesc
pulley[2]
distance
stiffness
ratio
flags
motor

NxPulleyJoint
loadFromDesc()
saveToDesc()
setMotor()
getMotor()
setFlags()

NxPulleyJointFlag
NX_PJF_IS_RIGID
NX_PJF_MOTOR_ENABLED

NxParameter
NX_VISUALIZE_JOINT_LIMITS
NX_VISUALIZE_JOINT_LOCAL_AXES

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Lesson 209 – Distance Joints

1 Introduction
The distance joint connects two actors by a rod. Each end of the rod is attached to an
anchor point on each actor.

2 Distance Joints
The distance joint connects two spheres.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
NxActor* sphere1 = NULL;
NxActor* sphere2 = NULL;
...
NxDistanceJoint* distanceJoint = NULL;
...
NxDistanceJoint* CreateDistanceJoint(NxActor* a0, NxActor* a1, const NxVec3&
anchor0, const NxVec3& anchor1, const NxVec3& globalAxis)
{
NxDistanceJointDesc distanceDesc;
distanceDesc.actor[0] = a0;
distanceDesc.actor[1] = a1;
distanceDesc.localAnchor[0] = anchor0;
distanceDesc.localAnchor[1] = anchor1;
distanceDesc.setGlobalAxis(globalAxis);

NxVec3 dist = a1->getGlobalPose()*anchor1 - a0->getGlobalPose()*anchor0;


distanceDesc.maxDistance = dist.magnitude()*1.5; // maximum rest length of the
rope or rod between the two anchor points
distanceDesc.minDistance = dist.magnitude()*0.5; // minimum rest length of the
rope or rod between the two anchor points
NxSpringDesc spring;
spring.spring = 100;
spring.damper = 0.5;
distanceDesc.spring = spring; // makes the joint springy. The spring.targetValue
field is not used.
distanceDesc.flags = (NX_DJF_MIN_DISTANCE_ENABLED |
NX_DJF_MAX_DISTANCE_ENABLED); // combination of the bits defined by
::NxDistanceJointFlag
distanceDesc.flags |= NX_DJF_SPRING_ENABLED;

// distanceDesc.projectionMode = NX_JPM_NONE;
// distanceDesc.projectionMode = NX_JPM_POINT_MINDIST;

distanceDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

return (NxDistanceJoint*)gScene->createJoint(distanceDesc);
}

void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
gAllocator);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_LIMITS, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_LOCAL_AXES, 1);

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.broadPhas = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;
gScene = gPhysicsSDK->createScene(sceneDesc);

// Create the default material


NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);

// Create the objects in the scene


groundPlane = CreateGroundPlane();
...
sphere1 = CreateSphere(NxVec3(-5,4,0), 1, 5);
sphere1->raiseBodyFlag(NX_BF_FROZEN_ROT);
sphere2 = CreateSphere(NxVec3(-8,4,0), 1, 5);
sphere2->raiseBodyFlag(NX_BF_FROZEN_ROT);

// Create distance joint


NxVec3 anchor1 = NxVec3(0,1,0);
NxVec3 anchor2 = NxVec3(0,1,0);
NxVec3 globalAxis2 = NxVec3(-1,0,0);
distanceJoint = CreateDistanceJoint(sphere1, sphere2, anchor1, anchor2,
globalAxis2);
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
This creates two spheres connected by a distance joint. The anchors for the joint are at
the centers of the spheres. The anchors get set in local coordinates, so (0,1,0) is the
center of a sphere with radius 1.

Remember to get the rest distance between the anchors in global coordinates:

NxVec3 dist = a1->getGlobalPose()*anchor1 – a0->getGlobalPose()*anchor0;

Set the maxDistance parameter to specify the farthest the anchors of the joint are
allowed to separate.

distanceDesc.maxDistance = dist.magnitude()*1.5; // maximum rest length of the


rope or rod between the two anchor points

Set the minDistance parameter to specify the closest the anchors of the joint are allowed
to come together.

distanceDesc.minDistance = dist.magnitude()*0.5; // minimum rest length of the


rope or rod between the two anchor points

Set a spring to add springiness to the joint. With this on, the joint behaves much like a
spring and damper effector. With the spring off, the joint is more like a stiff rod
connecting the two spheres.

NxSpringDesc spring;
spring.spring = 100;
spring.damper = 0.5;
distanceDesc.spring = spring; // makes the joint springy. The spring.targetValue
field is not used.

You need to enable the spring on the flags as well.

distanceDesc.flags |= NX_DJF_SPRING_ENABLED;

3 Conclusion
The distance joint is an additional joint supplied for gameplay purposes by the SDK.

4 Related Classes, Functions, and Parameters


NxDistanceJointDesc
minDistance
maxDistance
spring

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
flags

NxDistanceJoint
loadFromDesc()
saveToDesc()
setFlags()

NxDistanceJointFlag
NX_DJF_MIN_DISTANCE_ENABLED
NX_DJF_MAX_DISTANCE_ENABLED
NX_DJF_SPRING_ENABLED

NxParameter
NX_VISUALIZE_JOINT_LIMITS
NX_VISUALIZE_JOINT_LOCAL_AXES

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 210 – 6DOF Joints

1 Introduction
All the joints you have learned so far are special cases of the Six Degree of Freedom
Joint. Not only can you use this joint to simulate any of the other joints, you can also
reconfigure it to a different type of joint on the fly. That is, you don’t need to destroy it
as one type and recreate it as another. In this lesson, we will create a demo where you
can toggle the joint through three types: fixed, revolute, and spherical.

A final word on this joint: only some of the joints we have used thus far have springs,
motors, and naturally limits attached to them, namely, the revolute and the spherical
joints. The 6DOF Joint has motors, springs, and limits defined on each degree of
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
freedom. With this sort of flexibility, you can create motors to power ragdolls, creating a
character that can be physically driven by an animation. That is, you never make the parts
kinematic, they stay dynamic and you drive them to their animation key frames with
motors on the joints.

2 Six Degree of Freedom Joints


In InitNx(), we make two capsules, one above the other, and attach them by a 6DOF
joint, much as in previous lessons.

void InitNx()
{
……
// Create the objects in the scene
groundPlane = CreateGroundPlane();
……
d6Joint = CreateD6Joint(capsule1, capsule2, globalAnchor, globalAxis);

}

We create the 6DOF joint with CreateD6Joint():

NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,


const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.xMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.projectionMode = NX_JPM_NONE;

NxJoint* d6Joint = gScene->createJoint(d6Desc);

return (NxD6Joint*)d6Joint->is(NX_JOINT_D6);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Capsule1 is the upper, kinematic capsule. Capsule2 is the lower, dynamic capsule. The
globalAxis of the spherical joint is (0,0,-1) meaning the local axes of capsule1 and
capsule2 both start at (0,0,-1). Their local normals start at (1,0,0) and local binormals
start at (0,1,0).

Our 6DOF joint starts out as a fixed joint, with all joint motion fields locked.

3 Reconfiguring the Joint


Run the demo. You’ll notice capsule 2 is indeed fixed to capsule 1. Hit ‘b’ to go to
Debug Wireframe Mode and then ‘t’ to reconfigure the joint to a revolute joint. Capsule
2 will now be able to swing about the blue axis, the local axis of the joint.

When looking at the joint axes with the debug renderer:

red axis = local axis of joint on actor


green axis = normal axis of joint on actor
blue axis = binormal axis of joint on actor

There are two sets of joint local axes, one on each actor. They define how the actors are
oriented with respect to each other about the joint attach point.

When we switch the 6DOF joint from fixed to revolute, we call ReconfigureD6Joint():

void ReconfigureD6Joint()
{

switch (gJointType)
{
case 0: // Fixed Joint
{
// Coming from spherical joint, so reset actor #1
…..
}
break;

case 1: // Revolute Joint


{
……
}
break;

case 2: // Spherical Joint


{
……
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
}
break;

};

d6Desc.projectionMode = NX_JPM_NONE;
……
// Set joint motion display values
gJointMotion[3] = d6Desc.twistMotion;
……
d6Joint->loadFromDesc(d6Desc);
}

Essentially, this code involves saving out the old joint descriptor to oldD6Desc with
“NxD6Joint::saveToDesc()”, extracting the old local axes from oldD6Desc, then setting
up the new descriptor, d6Desc, setting the new motion fields and making new axes based
on the old ones.

The new axes are usually the same as the old axes. Here, however, we convert a revolute
joint to a spherical joint. The revolute joint rotates about its local axis, whereas the
spherical joint rotates about the two axes normal to its local axis. Therefore, when
switching from one to the other, we need to reorient the local axes as seen in the spherical
joint case:

switch (gJointType)
{

case 2: // Spherical Joint
{

d6Desc.localAxis[0] = localBinormal[0];
d6Desc.localNormal[0] = localNormal[0];
d6Desc.localAxis[1] = localBinormal[1];
d6Desc.localNormal[1] = localNormal[1];

}
break;

}

This basically turns the blue local axis up from (0,0,-1) to (0,1,0), keeping the red normal
axis at (1,0,0), see corresponding code in lesson.

When we switch back to the fixed joint configuration, we need to return the lower
capsule to within the confines of the fixed joint (i.e., back at its starting position) and
reset the local axes:
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
switch (gJointType)
{

case 0: // Fixed Joint
{
// Coming from spherical joint, so reset actor #1
NxMat33 orient;
orient.id();
a1->raiseBodyFlag(NX_BF_KINEMATIC);
a1->setGlobalOrientation(orient);
a1->setGlobalPosition(NxVec3(0,3,0));
a1->clearBodyFlag(NX_BF_KINEMATIC);

d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;

// Reset Anchor and Axis


NxVec3 globalAnchor = NxVec3(0,5,0);
NxVec3 globalAxis = NxVec3(0,0,-1);

d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

}
break;

}

The heart of the reconfiguration is in setting the joint motion parameters.

For the fixed joint, all fields are locked:

case 0: // Fixed Joint


{

d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;
}
break;

For the revolute joint, we free up the twist motion of the joint:

case 1: // Revolute Joint


{

d6Desc.twistMotion = NX_D6JOINT_MOTION_FREE;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;

}
break;

For the spherical joint, we free up the two swing motions of the joint:

case 2: // Spherical Joint


{

d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_FREE;

}
break;

Play around with the joint and notice how it transitions from one type to another.

4 Locked, Limited, and Free Joint Motion


Joint motion can be set to locked, limited, and free. Locked means that degree of
freedom is fixed. ‘Free’ means that degree of freedom is completely unconstrained.
Limited means the joint can move in that degree of freedom, but there may be limits
placed on that movement.

5 Conclusion
Our goal with the six degree of freedom joint is to make it the standard joint used by the
SDK. In the end, all the other joint types you have learned will be deprecated or will
simply be manifestations of this one joint. As we have only one rigid body type, we will
also have only one joint.

6 Related Classes, Functions, and Parameters


NxD6JointDesc
NxD6JointMotion
NxD6JointLockFlags
NxD6JointLimitFlags
NxD6JointDriveType
NxD6JointFlag
NxJointLimitPairDesc
NxJointLimitDesc
NxJointDriveDesc
NxJointProjectionMode

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
Lesson 211 – 6DOF Joint Limits,
Springs

1 Introduction
In this lesson you will learn to make full use of the Six Degree of Freedom Joint, creating
limits, springs, and motors for all 6 translational and rotational degrees of freedom.

2 6DOF Joint Translational Limits


We create the two capsules connected by the 6DOF joint as we did in the last lesson.
This time, however, we lock rotation of the joint about all three axes and allow limited
translation along all three axes.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,
const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.xMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LIMITED;

d6Desc.projectionMode = NX_JPM_NONE;

d6Desc.linearLimit.value = 0.8;
d6Desc.linearLimit.restitution = 0;
d6Desc.linearLimit.spring = 0;
d6Desc.linearLimit.damping = 0;

NxJoint* d6Joint = gScene->createJoint(d6Desc);

return (NxD6Joint*)d6Joint;
}

We lock the swing1Motion, swing2Motion, and twistMotion parameters, and limit the
xMotion, yMotion, and zMotion parameters.

Then we set the properties of the limited linear motion:

d6Desc.linearLimit.value = 0.8;
d6Desc.linearLimit.restitution = 0;
d6Desc.linearLimit.spring = 0;
d6Desc.linearLimit.damping = 0;

Hit “b” key to go to debug wireframe mode. You will see a red sphere surrounding the
anchor position of the joint. This is the limiting sphere within which the lower actor is
allowed to move. The value of the limit is 0.8m, so the bottom capsule is allowed to
move within a sphere of radius 0.8m about the joint anchor. If we lock the z motion, the
bottom capsule will be allowed to move in a circle of radius 0.8m on the xy-plane of the
joint. If we lock both the y motion and the z motion, the bottom capsule will be allowed

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
to move on a line segment of radius 0.8m in the x-axis, that is, a line segment 1.6m long
and centered at the joint anchor.

3 6DOF Joint Soft Translational Limits


Hit “t” key to select the next joint configuration. The code to reconfigure the joint to the
next selection is in ReconfigureD6Joint().

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
NxActor* a0 = capsule1;

switch (gJointType)
{
case 0: // Translation Limited Joint
{

d6Desc.xMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LIMITED;

d6Desc.linearLimit.value = 0.8;
d6Desc.linearLimit.restitution = 0;
d6Desc.linearLimit.spring = 0;
d6Desc.linearLimit.damping = 0;
}
break;

case 1: // Translation Soft Limited Joint


{
….

d6Desc.xMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LIMITED;

d6Desc.linearLimit.value = 0.8;
d6Desc.linearLimit.restitution = 0;
d6Desc.linearLimit.spring = 100;
d6Desc.linearLimit.damping = 0.1;
}
break;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3

}

}

We create a new translational limited joint, this time with soft limits. The soft limits are
created by adding a spring and damping to the linear limit.

d6Desc.linearLimit.spring = 100;
d6Desc.linearLimit.damping = 0.1;

With a spring value greater than zero, the limit is now a soft limit rather than a hard limit,
i.e., the lower actor is able to move away from its limiting sphere, held back only by the
force applied by the spring. The force pushes the lower actor back toward its limiting
sphere.

You can see this action better if you disable gravity on the lower capsule. Hit “g” key to
disable gravity on the lower actor. Use “u,m,i,j,k,l” to move the actor around. You will
notice that, after you move it to a particular position, it will gradually drift back to the
closest point on its limiting sphere. You can raise the spring value on the limit to tighten
its adherence to the limiting sphere.

You’ll notice the capsule reach a maximum speed as it drifts toward the sphere. This is
caused by the damping value we have applied. We already set some linear damping on
capsule 2, so its velocity is already damped. It’s velocity with respect to its limit sphere
is now damped as well.

4 6DOF Joint Rotational Limits


Hit “g” to re-enable gravity on the lower capsule and “t” to select the next joint
configuration: “Rotation Limited”.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
NxActor* a0 = capsule1;
NxActor* a1 = capsule2;

NxD6JointDesc d6Desc;

switch (gJointType)
{

case 2: // Rotation Limited Joint
{
d6Desc.twistMotion = NX_D6JOINT_MOTION_LIMITED;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LIMITED;

……
}
break;

}
}

Take a look at the new joint in the demo. Notice that we have precisely recreated the
spherical joint. We have freed up rotation on all axes and locked down translation on all
axes.

d6Desc.twistMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LIMITED;

d6Desc.xMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

As far as which axes are which, x is the local axis, y is the normal to the local axis, z is
the binormal to the local and normal axes. Remember x is red, y is green, z is blue, same
as the orientation axis color scheme.

Setting limits on the swing1Motion and swing2Motion of the joint is setting limits on
rotation of capsule 2 about the joint’s normal and binormal axes, a.k.a, y and z (green and
blue).

d6Desc.swing1Limit.value = 0.3*NxPi;
d6Desc.swing1Limit.restitution = 0;
d6Desc.swing1Limit.spring = 0;
d6Desc.swing1Limit.damping = 0;

d6Desc.swing2Limit.value = 0.3*NxPi;
d6Desc.swing2Limit.restitution = 0;
d6Desc.swing2Limit.spring = 0;
d6Desc.swing2Limit.damping = 0;

The values of the two swing limits is 0.3*NxPi, so we get the symmetrical cone you see
in the debug renderer, limiting the two swings to an arc of 60 degrees. The restitution of
each limit is 0, so there is little “rebound” from the limit when it is hit. The spring value
of each limit is 0, so the limit is a hard limit.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Setting limits on the twistMotion of the joint is setting two limits, one low and one high,
restriction the rotation of capsule 2 about the joint’s local axis, a.k.a, x (red).

d6Desc.twistLimit.low.value = -0.05*NxPi;
d6Desc.twistLimit.low.restitution = 0;
d6Desc.twistLimit.low.spring = 0;
d6Desc.twistLimit.low.damping = 0;

d6Desc.twistLimit.high.value = 0.05*NxPi;
d6Desc.twistLimit.high.restitution = 0;
d6Desc.twistLimit.high.spring = 0;
d6Desc.twistLimit.high.damping = 0;

The twist limit shows up as the red pie slice in the debug renderer perpendicular to the
joint’s local axis. The value of the low limit is -0.05*NxPi and the value of the high
limit is 0.05*NxPi, so the twist motion of the joint is fairly tightly constrained. The
restitution and spring on the low and high limits are both 0 (no rebound and hard limit).

5 6DOF Joint Soft Rotational Limits


Hit “t” to select the next joint configuration: “Rotation Soft Limited”. With the soft
translation limits, the soft rotation limits allow the lower actor to rotate somewhat beyond
its limits, held back by the springs attached to the limits.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
NxActor* a0 = capsule1;
NxActor* a1 = capsule2;

NxD6JointDesc d6Desc;

switch (gJointType)
{

case 3: // Rotation Soft Limited Joint
{
d6Desc.twistMotion = NX_D6JOINT_MOTION_LIMITED;
...
d6Desc.twistLimit.high.restitution = 0;
}
break;

}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
We have added a spring value of 300 and a damping value of 10 to all the joint’s
rotational limits. Hit “g” again to disable gravity on the lower actor. As with the
translational limit spring, the rotational limit spring acts to rotate the lower actor back to
its rotational limit. Notice we have set a spring on the twistLimit’s lower limit and not
its higher limit. You will notice the bottom actor will tend to twist about the joint’s local
axis so that it settles on this lower limit (the left side of the red pie slice as you start the
demo). If putting the spring on the high limit instead, the bottom actor will tend to twist
so that it settles on the high limit (the right side of the red pie slice as you start the demo).

6 Joint Relative Translation and Rotation


Keep in mind that the translational and rotational limits on the joint are always set on the
actor[0] of the joint descriptor, which is capsule1 in this lesson.

NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,


const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);
...
}

void InitNx()
{

NxVec3 globalAnchor = NxVec3(0,5,0);
NxVec3 globalAxis = NxVec3(0,1,0);
d6Joint = CreateD6Joint(capsule1, capsule2, globalAnchor, globalAxis);

}

We have been referring to the joint's local anchor generally. Though they start out in the
same position, there are two local anchors, one on capsule1 and one on capsule2. In our
example, capsule1's local anchor stays fixed, while capule2's local anchor is free to move
within the limiting sphere centered on capsule1's local anchor.

Try switching the order of the actors on the joint, both when they get created and
reconfigured.

void ReconfigureD6Joint()
{
NxActor* a0 = capsule2;
NxActor* a1 = capsule1;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
NxD6JointDesc d6Desc;
...
d6Desc.actor[0] = a1;
d6Desc.actor[1] = a0;
...
}

void InitNx()
{

NxVec3 globalAnchor = NxVec3(0,5,0);
NxVec3 globalAxis = NxVec3(0,1,0);
d6Joint = CreateD6Joint(capsule2, capsule1, globalAnchor, globalAxis);

}

Run the lesson. The limit sphere is now defined about capsule2's local anchor.
Capsule2 is free to move as long as capsule1's local anchor stays within the limiting
sphere which is now centered on capsule2's local anchor.

The same holds true from the joint's local axes. Though they start out in the same
orientation, there are two of them, one attached to capsule1 and one attached to capsule2.
In our example so far, capsule1's local axes stay fixed, while capsule2's local axes are
free to rotate, up to the limits defined on capsule1's local axes.

With the new code, you'll see the rotational limit cone is now defined about caspule2's
local axes. Capsule2 is free to rotate as long as capsule1's local axes stay within the
limiting cone and pie slice which are now defined on capsule2's local axes.

Change you code back so capsule1 is once again the first actor in the joint and capsule2 is
the second.

In the next sections, when we construct translational and rotational motor targets, bear in
mind the situation is similar. The motor target positions and orientations are defined in
the space of the first actor in the joint. Caspule2's local anchor will be motivated toward
the target position defined in capsule1's local space and capsule2's local axes will be
motivated toward the target orientation defined in capsule2's local space.

7 6DOF Joint Translational Motors


Hit “t” to select the next joint configuration: “Translation Motored”.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

8
…….
switch (gJointType)
{
……
case 4: // Translation Motored Joint
{
……
d6Desc.zDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
…….
}
break;

};

}

We lock down the rotational axes and free up the translational axes again. The limit on
the joint is 1, so the local axes of the joint can stray no further than 1 unit away from each
other.

Now we set up the joint motor (or “drive”) with the following code:

d6Desc.zDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
d6Desc.zDrive.spring = 100;
d6Desc.zDrive.damping = 0;
d6Desc.drivePosition.set(0,5,1);

The 6 DOF joint has 3 translational motors: the xDrive, the yDrive, and the zDrive. The
motor we turn on is the zDrive. Our motor will move the lower capsule back and forth
along the z-axis of the joint. It is still free to move along the x and y axes of the joint
since we have not locked motion along either, we are just providing motivation along the
joint’s z-axis.

We set driveType of the zDrive to NX_D6JOINT_DRIVE_POSITION. This means the


motor will be driving the local anchor of the joint toward a particular position, stored in
the joint’s drivePosition. This is world position (0,5,1). The initial position of the joint
anchor is (0,5,0), so the lower capsule will start out being pushed to the right as you look
at it from the demo’s starting position.

The zDrive’s spring value is 100. This is the force the motor will apply to power the
joint. Its damping is 0, so there will be no increased resistance to increased speed of the
motor.

You’ll notice the lower capsule moves to z = 1 on the joint z-axis, then back to z = -1,
then back again, etc. We have added an UpdateJointMotorTarget() which switches the

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

9
drive position of the motor once the joint anchor reaches a world x value greater than 0.4
or less than -0.4 (the z-axis of the joint is aligned along the x-axis of the world)

void UpdateJointMotorTarget()
{
if (gJointType == 4) // Linear Motor
{
……
}
}

We can set the driveType to NX_D6JOINT_DRIVE_VELOCITY as well. This means


the motor will be driving the local anchor of the joint toward a particular joint-relative
velocity. Make the following changes to the code to make the target of the joint motor a
velocity rather than a position.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
NxActor* a0 = capsule1;
NxActor* a1 = capsule2;

NxD6JointDesc d6Desc;
switch (gJointType)
{

case 4: // Translation Motored Joint
{

d6Desc.zDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.zDrive.forceLimit = FLT_MAX;
d6Desc.driveLinearVelocity.set(0,0,5);
}
break;

};

}

The forceLimit is FLT_MAX, the maximum floating point value, so essentially limitless.
The joint will supply as much force as possible to drive the joint to the target velocity.
Note the corresponding update code to the velocity target already in place.

void UpdateJointMotorTarget()
{
if (gJointType == 4) // Linear Motor
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

10
{

else if (d6Desc.zDrive.driveType == NX_D6JOINT_DRIVE_VELOCITY) //
Velocity Target
{
NxVec3 jointPos;
d6Joint->getGlobalAnchor(jointPos);
if ((jointPos.x > 0.4) || (jointPos.x < -0.4))
{
if (jointPos.x > 0.4)
d6Desc.driveLinearVelocity = NxVec3(0,0,-5);
else if (jointPos.x < -0.4)
d6Desc.driveLinearVelocity = NxVec3(0,0,5);
d6Joint->loadFromDesc(d6Desc);
}
}

}

8 6DOF Joint Rotational Motors


Hit “t” to select the next joint configuration: “Rotation Motored”.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
……
switch (gJointType)
{
……
case 5: // Rotation Motored Joint
{
……
// Slerp Drive - Orientation Target
d6Desc.flags = NX_D6JOINT_SLERP_DRIVE;
d6Desc.slerpDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
…….
}
break;

}
}

We lock down the translational axes and free up the rotational axes. The rotational
limits are set as with the previous rotational joint.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

11
We set up the rotational motor with the following code:

d6Desc.flags = NX_D6JOINT_SLERP_DRIVE;
d6Desc.slerpDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
d6Desc.slerpDrive.spring = 200;
d6Desc.slerpDrive.damping = 0;
NxQuat q;
q.fromAngleAxis(90,NxVec3(0,1,0));
d6Desc.driveOrientation = q;

The 6 DOF rotational motor has two modes: swing-twist mode and slerp mode. Slerp
mode has a single motor associated with it: slerpDrive. Swing-twist mode has two
motors associated with it: swingDrive and twistDrive.

We start out in slerp mode. The motor we turn on is the slerpDrive. Our motor will
swing the lower capsule back and forth around the y-axis of the joint. Note, that it is still
free to rotate about the x and z axes of the joint within the set limits.

We set driveType of the slerpDrive to NX_D6JOINT_DRIVE_POSITION. This means


the motor will be driving the local axis of the joint toward a particular orientation, a
quaternion stored in the joint’s driveOrientation. We get a quaternion that is a 90 degree
rotation of the origin about the y-axis.

NxQuat q;
q.fromAngleAxis(90,NxVec3(0,1,0));
d6Desc.driveOrientation = q;

This will make the motor rotate the lower capsule up and to the right when viewed from
the demo starting position.

The slerpDrive’s spring value is 200. This is the torque the motor will apply to power
the joint. Its damping is 0, so there will be no increased resistance to increased angular
speed of the motor.

You’ll notice the lower capsule rotates about the joint’s y-axis about 30 degrees, then
back about the joint’s y-axis to -30, etc. UpdateJointMotorTarget() which switches the
drive orientation of the motor once the world x coordinate of capsule2’s y-axis reaches a
value greater than 0.5 or less than -0.5 (so when the dot product of capsule2’s y-axis with
the world’s x-axis is greater than 0.5 or less than -0.5)

void UpdateJointMotorTarget()
{

else if (gJointType == 5) // Rotational Motor
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

12
else // Swing-twist Angular Drive
{
if (d6Desc.zDrive.driveType == NX_D6JOINT_DRIVE_POSITION) //
Orientation Target
{
if ((axis2.x > 0.5) || (axis2.x < -0.5))
{
if (axis2.x > 0.5)
{
NxQuat q;
q.fromAngleAxis(-90, NxVec3(0,1,0));
d6Desc.driveOrientation = q;
}
else if (axis2.x < -0.5)
{
NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
d6Desc.driveOrientation = q;
}
d6Joint->loadFromDesc(d6Desc);
}
}
}
}
}

The rotational motor mode we have been using so far is slerp mode. The motor rotates
the joint to this orientation along the quickest path.

Comment out the “Slerp Drive – Orientation Target” section of code and uncomment the
“Slerp Drive – Angular Velocity Target” section. This makes the target of the joint
motor an angular velocity rather than an orientation.

// Reconfigure joint, a.k.a., roll joint


void ReconfigureD6Joint()
{
NxActor* a0 = capsule1;
NxActor* a1 = capsule2;

NxD6JointDesc d6Desc;
switch (gJointType)
{

case 5: // Rotation Motored Joint
{
......
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

13
// Slerp Drive - Angular Velocity Target
d6Desc.flags = NX_D6JOINT_SLERP_DRIVE;
d6Desc.slerpDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.slerpDrive.forceLimit = FLT_MAX;
d6Desc.driveAngularVelocity = NxVec3(0,5,0);
......
}
……
}
}

And note the corresponding update code to the velocity target already in place.

void UpdateJointMotorTarget()
{

else if (gJointType == 5) // Rotational Motor
{

}
else // Swing-twist Angular Drive
{
...
}
}
}

There is also a “swing drive” and “twist drive” available. This will use motors on the
swing axes and the twist axis, respectively, to rotate the joint toward a target orientation
or velocity.

Sample initialization code for a swing drive with an orientation target:

// Swing Drive - Orientation Target


d6Desc.flags = 0;
d6Desc.swingDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
d6Desc.swingDrive.spring = 200;
d6Desc.swingDrive.damping = 0;
NxQuat q;
q.fromAngleAxis(90,NxVec3(0,1,0));
d6Desc.driveOrientation = q;

Sample initialization code for a swing drive with an angular velocity target:

// Swing Drive - Angular Velocity Target


d6Desc.flags = 0;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

14
d6Desc.swingDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.swingDrive.forceLimit = FLT_MAX;
d6Desc.driveAngularVelocity = NxVec3(0,5,0);

Sample initialization code for a twist drive with an orientation target:

// Twist Drive - Orientation Target


d6Desc.flags = 0;
d6Desc.twistDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
d6Desc.twistDrive.spring = 200;
d6Desc.twistDrive.damping = 0;
NxQuat q;
q.fromAngleAxis(90,NxVec3(1,0,0));
d6Desc.driveOrientation = q;

Sample initialization code for a twist drive with an angular velocity target:

// Twist Drive - Angular Velocity Target


d6Desc.flags = 0;
d6Desc.twistDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.twistDrive.forceLimit = FLT_MAX;
d6Desc.driveAngularVelocity = NxVec3(5,0,0);

We set rather constrictive limits on the twist motion of the joint, so you probably want to
loosen these up or eliminate them altogether before implementing the twist drive.

9 Conclusion
You now know the complete functionality of the six degree of freedom joint. It is a very
powerful joint that encapsulates all joints just as the actor class encapsulates all rigid
body types. Use the standard joint types as needed and learn how to implement each
type using a 6DOF joint. From this foundation you can move on to create your own
custom-built joints.

10 Related Classes, Functions, and Parameters


NxD6JointDesc

NxD6JointMotion

NxD6JointDriveType

NxD6JointFlag

NxJointLimitPairDesc
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

15
NxJointLimitDesc

NxJointDriveDesc

NxJointProjectionMode

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

16
Lesson 212 – Joint Limit

1 Introduction
In this second intermediate lesson, we teach you about joint limit values. Limit values
are a way to constrain the motion of a joint. In this lesson, they are used to confine the
rotation of the second actor in the joint between two angles about the joint’s global axis.
In the previous example, our joint had no limits applied to it. The lower body could
swing freely about the z-axis, stopping only when it hit the kinematic upper body. When
we disabled collision between the two actors and the lower body could swing freely all
the way around the joint's axis.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
We now place limiting values for the angles through which the lower body can rotate.

2 Joint Limit Values


InitNx() is just a bit different from the last lesson. We have reversed the global axis of
the revolute joint so that it points down the negative z-axis, and put the global anchor on
the front side of both boxes.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box1 = NULL;
NxActor* box2 = NULL;

// Joint globals
NxRevoluteJoint* revJoint = NULL;

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

NxVec3 globalAnchor = NxVec3(0.5,5,-1);


NxVec3 globalAxis = NxVec3(0,0,-1);
revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);

gSelectedActor = box2;

}

Take a look at the new CreateRevoluteJoint().

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxRevoluteJointDesc revDesc;

revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);

// Enable collision between the jointed bodies


revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
// Enable and set up joint limits
revDesc.flags |= NX_RJF_LIMIT_ENABLED;
revDesc.limit.high.value = 0.25*NxPi;
revDesc.limit.high.restitution = 1;
revDesc.limit.low.value = 0;
revDesc.limit.low.restitution = 1;

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

We have added a block of code to the revolute joint descriptor setup, just before the joint
is created.

// Enable and set up joint limits


revDesc.flags |= NX_RJF_LIMIT_ENABLED;
revDesc.limit.high.value = 0.25*NxPi;
revDesc.limit.high.restitution = 1;
revDesc.limit.low.value = 0;
revDesc.limit.low.restitution = 1;

First we set the flags on the revolute joint descriptor saying we want to enable the use of
joint limits on the revolute joint. Then we set the joint limit values, one high and one
low. These are the angles through which the lower body is allowed to rotate with respect
to the upper body. The low angle is 0 and the high angle is 0.25*NxPi, or 45 degrees.

Run the lesson. Notice that the lower body will not swing above 45 degrees to the left.

Hit “b” to enable debug visualization. Notice the red piece-of-pie shaped wireframe to
the left of the joint with a purple arrow swinging inside it. We have added the following
to InitNx(), which allows us to visualize the joint limit angles in wireframe mode.

gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_LIMITS, 1);

The local axis of box2 is drawn with a blue arrow and points down the negative z-axis.
The purple arrow is the “local normal” of box2. It starts out facing down the x-axis and
is perpendicular to the the local normal.

Notice how we have constrained the rotation of box2 about its local axis between 0 and
45 degrees, where the local normal faces along the x-axis at 0 degrees and can rotate 45
degrees around z toward the y-axis. Rotation of the local normal about its local axis is
defined in a left handed manner. The local normal, the x-axis (1,0,0), is the cross
product of the local axis of box2, the negative z-axis (0,0,-1), with the vector between the
centers of mass of the two jointed actors, the negative y-axis (0,-1,0). With the local

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
axis of box2 pointing out of the screen, the “angle” of the local normal increases as it
rotates clockwise about the local axis.

Note how the purple normal axis vector on box2 turns yellow when it hits the red limit
values. The normal axis has hit the limit value and box2’s rotation is stopped.

3 Local Anchors and Axes


The local anchors of a joint are the reference points by which the relative translations
between the actors are measured. The local axes of a joint are the reference axes by
which the relative rotations between the actors are measured.

The local anchor is where the joint is placed on an actor in the actor's local space. For
joints that are purely translational, like the prismatic joint, the local anchors of the joint
can move with respect to each, but the local axes may not rotate with respect to each
other. The local axis of one actor must remain aligned with the local axis of the other.
The two actors cannot rotate with respect to each other, they can only translate.

Similarly, for joints that are purely rotational, like the revolute joint, the local anchors of
two actors connected by a joint are always in the same global position. The actors can
rotate, but their local anchors must stay fastened together. This is by definition. Their
local axes rotate with respect to each other.

Run the lesson. Hit “b” to go to wireframe mode. Then hit “v” which calls the
following.

bool bShowLocalAxes = false;



void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
case 'v':
{
// Show the local axes in wireframe mode
bShowLocalAxes = !bShowLocalAxes;
if (bShowLocalAxes)
{
gPhysicsSDK-
>setParameter(NX_VISUALIZE_JOINT_LOCAL_AXES, 1);
}
else
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
gPhysicsSDK-
>setParameter(NX_VISUALIZE_JOINT_LOCAL_AXES, 0);
}
return;
}

}
}

This toggles wireframe visualization of the local axes on both bodies. Take a look at
these axes, which are in blue, red, and green. Box1’s local axis, in blue, faces along the
negative z-axis, the direction we set globally. Box1’s local normal, in red, faces along
the positive x-axis. Box1’s local binormal is the cross product of its local axis and its
local normal and faces along the positive y-axis.

Box2’s local axes rotates with respect to box1’s. Box2’s local axis stays aligned with
box1’s local axis, while box2’s local normal rotates with respect to box1’s local normal.

4 Jointed Actor Collision


Notice we have left the two jointed actors collidable in the creation of the joint.

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{

// Enable collision between the jointed bodies
revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

}

Make them non-colliding, by commenting out this line. Run the demo. Note the lower
body still stops against the upper body as if they were colliding due to the joint limit low
value of 0. We can joint actors together and keep them non-colliding since we will be
setting limit values for the joint anyway and the actor-to-actor collision would not occur
or be redundant. As well, there are many instances where we want the actors to
interpenetrate to give the illusion that the two actors are one, bendable object. As we
will see in the advanced lessons where we create whole meshes of jointed bodies, joints
are very powerful for simulating cloth, grass, and other objects made of springy, bendable
material, but the illusion is broken if the objects appear disconnected. Hence, we will
often want collision to be turned off between jointed actors, and it is by default.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
5 Actor Ordering and Limit Values
The ordering of actors when creating a joint is important. Change the order box1 and
box2 are passed into CreateRevoluteJoint() in InitNx()

void InitNx()
{

revJoint = CreateRevoluteJoint(box2, box1, globalAnchor, globalAxis);

}

Now change CreateRevoluteJoint() so that the limit high value is 0 and the limit low
value is -45 degrees.

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{

revDesc.limit.high.value = 0;
revDesc.limit.low.value = -0.25*NxPi;

}

Run the lesson. The actors behave exactly as they did before. If you hit “b” to go to
wireframe mode, you will see the difference. The limit values are now attached to box2
and the limit axis is box1’s local normal. When you pass the two actors into
NxScene::createJoint(), the limit values are defined against the local axes of the first actor
while the axes that move freely within these limits are the local axes of the second actor.

6 Joint Restitution
As rigid bodies have a restitution that defines the bounciness of their contacts, joint limit
values have a restitution that defines the bounciness of the joint at its limit.

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{

revDesc.limit.high.restitution = 1;
revDesc.limit.low.restitution = 1;

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

6
Both the limit high and limit low values have a restitution of 1, the maximum restitution.
If the local axis of box2 hits the limit hard, it will bounce, much like a door bounces
against a springy doorstop. Set these restitutions to 0. Note how box2 stops fairly
softly against the revolute joint limits as you swing it back and forth.

7 Conclusion
Joints set actors to rotate or translate with respect to each other about or along certain
axes. Joint limit values set limits on this relative motion, providing upper and lower
(high and low) limits for the rotations and translations.

8 Related Classes, Functions, and Parameters


NxRevoluteJoint

NxRevoluteJointDesc
limit
flags

NxJointLimitPairDesc
high
low

NxJointLimitDesc
value
restitution

NxRevoluteJointFlag
NX_RJF_LIMIT_ENABLED

NxParameter
NX_VISUALIZE_JOINT_LIMITS
NX_VISUALIZE_JOINT_LOCAL_AXES

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

7
Lesson 213 – 6DOF Motion Controller

1 Introduction
The joints’ properties can be reconfigured on the fly. That is, you don’t need to destroy it
as one type and recreate it as another. In this lesson, we will create a demo where you
can match the joint to target actors’ behavior.

2 Six Degree of Freedom Joints


In InitNx(), we create some rigid actors and one six degree of freedom joint.

void InitNx()
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1

d6Joint = CreateD6Joint(capsule1, capsule2, globalAnchor, globalAxis);
……

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

We create the 6DOF joint with CreateD6Joint():

NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,


const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

d6Desc.twistMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_FREE;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_FREE;

d6Desc.xMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.yMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.zMotion = NX_D6JOINT_MOTION_FREE;

d6Desc.projectionMode = NX_JPM_NONE;

return (NxD6Joint*)gScene->createJoint(d6Desc);
}

3 Match target behavior


The joint’s behavior can be changed according to other objects’ movement.

void MatchTarget()
{
NxD6JointDesc d6Desc;
d6Joint->saveToDesc(d6Desc);

// fill the d6 joint descriptor to re-use it.

d6Desc.xDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
d6Desc.xDrive.spring = 200;
d6Desc.xDrive.damping = 10;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
….

NxMat33 orient = ball->getGlobalOrientation();


NxMat33 controlOrient;
controlOrient.setRow(0, orient.getRow(1));
controlOrient.setRow(1, orient.getRow(2));
controlOrient.setRow(2, orient.getRow(0));
NxQuat q(controlOrient);
d6Desc.driveOrientation = q;

d6Joint->loadFromDesc(d6Desc);
}

The drive orientation is changed by loadFromDesc to reflect the ball actor’s movement.

4 Conclusion
We learn how to change the joint’s properties during run-times in this lesson.

5 Related Classes, Functions, and Parameters


NxD6JointDesc

NxD6JointMotion

NxD6JointLockFlags

NxD6JointLimitFlags

NxD6JointDriveType

NxD6JointFlag

NxJointLimitPairDesc

NxJointLimitDesc

NxJointDriveDesc

NxJointProjectionMode

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 214 – Breakable Joints

1 Introduction
Joints can be made to break, simulating things like a wooden door that can be kicked off
its hinges, or an open car door that breaks off when it hits a mailbox or lamppost.

2 Breakable Joints
NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3
globalAnchor, NxVec3 globalAxis)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
{
NxRevoluteJointDesc revDesc;
revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);
revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

NxJoint* joint = gScene->createJoint(revDesc);

NxReal maxForce = 2000;


NxReal maxTorque = 100;
joint->setBreakable(maxForce,maxTorque);

return (NxRevoluteJoint*)joint;
}

Everything is set up the same in CreateRevoluteJoint() as in the last lesson, except we


intercept the joint before it’s returned and we call

NxReal maxForce = 2000;


NxReal maxTorque = 100;
joint->setBreakable(maxForce, maxTorque);

maxForce is the maximum linear force the joint can withstand such as a force pulling the
joint apart. maxTorque is the maximum rotational force the joint can withstand such as
twisting a door beyond a stopping point on its hinge.

Try running the demo and push and twist box2 until the revolute joint breaks and it falls
off. Try changing the maxForce and maxTorque values to see what it takes to get the
joint to break.

3 Conclusion
Breakable joints are important in games as there are many objects that are meant to be
destructible and have parts that fly off them under sufficient force. Breakable joints add
a sense of realism in that the parts of objects won’t break off unless a physically
sufficient force is applied.

4 Related Classes, Functions, and Parameters


NxJoint
setBreakable()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
Lesson 215 – 6DOF Linear Driver

1 Introduction
In this lesson you will learn how to apply linear driver to six degree freedom joint.

2 Linear driver of 6DOF joint


We create the two capsules connected by the 6DOF joint as we did in the last one. This
time, however, we lock rotation of the joint about all three axes and allow limited
translation along all three axes.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,
const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.driveLinearVelocity = NxVec3(0, -2, 0);
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.linearLimit.value = 5;
d6Desc.linearLimit.damping = 50;
d6Desc.linearLimit.restitution = 0.8f;
d6Desc.linearLimit.spring = 100;

d6Desc.yDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.yDrive.forceLimit = 500.0f;

d6Desc.xMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LIMITED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LIMITED;

d6Desc.projectionMode = NX_JPM_NONE;

return (NxD6Joint*)gScene->createJoint(d6Desc);
}

The linear velocity is applied to joint descriptor by following code:

d6Desc.driveLinearVelocity = NxVec3 (0, -2, 0);

d6Desc.yDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;

3 Conclusion
A velocity drive in this case will deflect the drive from the desired position, just as an
external force stretches the spring.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
4 Related Classes, Functions, and Parameters
NxD6JointDesc
NxD6JointMotion

NxD6JointLockFlags
NX_D6JOINT_LOCK_X
NX_D6JOINT_LOCK_Y
NX_D6JOINT_LOCK_Z
NX_D6JOINT_LOCK_LINEAR
NX_D6JOINT_LOCK_TWIST
NX_D6JOINT_LOCK_SWING1
NX_D6JOINT_LOCK_SWING2
NX_D6JOINT_LOCK_ANGULAR

NxD6JointLimitFlags
NX_D6JOINT_LIMIT_TWIST
NX_D6JOINT_LIMIT_SWING
NX_D6JOINT_LIMIT_LINEAR

NxD6JointDriveType
NX_D6JOINT_DRIVE_POSITION
NX_D6JOINT_DRIVE_VELOCITY

NxD6JointFlag
NX_D6JOINT_SLERP_DRIVE
NX_D6JOINT_GEAR_ENABLED

NxJointLimitPairDesc

NxJointLimitDesc

NxJointDriveDesc

NxJointProjectionMode
NX_JPM_NONE
NX_JPM_POINT_MINDIST

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 216 – 6DOF Angular Driver

1 Introduction
In this lesson you will learn how to apply angular driver to six degree freedom joint.

2 Angular driver of 6DOF joint


We create the two capsules connected by the 6DOF joint as we did in the last one. This
time, however, we free the rotation of the joint about all three axes.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
NxD6Joint* CreateD6Joint(NxActor* a0, NxActor* a1, const NxVec3& globalAnchor,
const NxVec3& globalAxis)
{
NxD6JointDesc d6Desc;
d6Desc.actor[0] = a0;
d6Desc.actor[1] = a1;
d6Desc.driveAngularVelocity = NxVec3(0, 2, 0);
d6Desc.setGlobalAnchor(globalAnchor);
d6Desc.setGlobalAxis(globalAxis);

d6Desc.twistMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_FREE;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_FREE;

d6Desc.swingDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;
d6Desc.swingDrive.forceLimit = 500.f;

d6Desc.xMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.projectionMode = NX_JPM_NONE;

return (NxD6Joint*)gScene->createJoint(d6Desc);
}

The angular velocity is applied to joint descriptor by following code:

d6Desc.driveAngularVelocity = NxVec3 (0, 2, 0);

d6Desc.yDrive.driveType = NX_D6JOINT_DRIVE_VELOCITY;

And the twistMotion and swing1Motion/swing2Motion are free.

3 Conclusion
Angular drive differs from linear drive in a fundamental way: it does not have a simple
and intuitive representation free from singularities. For this reason, the d6joint provides
two angular drive models - twist and swing and Spherical Linear Interpolation.

4 Related Classes, Functions, and Parameters


NxD6JointDesc

NxD6JointMotion
NX_D6JOINT_MOTION_LOCKED
NX_D6JOINT_MOTION_LIMITED
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
NX_D6JOINT_MOTION_FREE

NxD6JointLockFlags
NX_D6JOINT_LOCK_X
NX_D6JOINT_LOCK_Y
NX_D6JOINT_LOCK_Z
NX_D6JOINT_LOCK_LINEAR
NX_D6JOINT_LOCK_TWIST
NX_D6JOINT_LOCK_SWING1
NX_D6JOINT_LOCK_SWING2
NX_D6JOINT_LOCK_ANGULAR

NxD6JointLimitFlags
NX_D6JOINT_LIMIT_TWIST
NX_D6JOINT_LIMIT_SWING
NX_D6JOINT_LIMIT_LINEAR

NxD6JointDriveType
NX_D6JOINT_DRIVE_POSITION
NX_D6JOINT_DRIVE_VELOCITY

NxD6JointFlag
NX_D6JOINT_SLERP_DRIVE
NX_D6JOINT_GEAR_ENABLED

NxJointLimitPairDesc

NxJointLimitDesc

NxJointDriveDesc

NxJointProjectionMode
NX_JPM_NONE
NX_JPM_POINT_MINDIST

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 301 – Actor Pair Filtering

1 Introduction
It is possible to provide a custom method for actor pair filtering. This gives you total
control over which actors can interact with which.

2 The Actor Pair Filtering


In ActorPairFilter.h, we define a class called UserActorPairFiltering.

class UserActorPairFiltering
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
public:
UserActorPairFiltering() : bNoCollision(false) {}

virtual void onActorPairs(NxActorPairFilter *filterArray, NxU32 arraySize)


{
……
}

public:
bool bNoCollision;
};

The virtual function onActorPairs() is a callback function. This callback will be called
with arrays of actor pairs, for each of which you may specify whether to filter that pair
out or not.

This class gets instantiated in Lesson301.cpp. Then pass it into the NxScene.

UserActorPairFiltering gActorPairFiltering;
......
void InitNx()
{
……
gScene->setUserActorPairFiltering(&gActorPairFiltering);
……
}

3 Set Actors Flag


Set the NX_AF_USER_ACTOR_PAIR_FILTERING flag for any actors you wish to
involve in custom filtering.

void InitNx()
{
……
box->raiseActorFlag(NX_AF_USER_ACTOR_PAIR_FILTERING);
sphere->raiseActorFlag(NX_AF_USER_ACTOR_PAIR_FILTERING);
capsule->raiseActorFlag(NX_AF_USER_ACTOR_PAIR_FILTERING);
pyramid->raiseActorFlag(NX_AF_USER_ACTOR_PAIR_FILTERING);
……
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
4 Custom Method
We can iterate the filter array and specify whether to filter out or not for each pair. In this
lesson, we omit the pair that involving the ground. For other pairs, we control the filter by
a bool variable. Of cause, you can control it with any complex logic as you like

virtual void onActorPairs(NxActorPairFilter *filterArray, NxU32 arraySize)


{
for (NxU32 i = 0; i < arraySize; ++i)
{
NxActor* pActor0 = filterArray[i].actor[0];
NxActor* pActor1 = filterArray[i].actor[1];
if (groundPlane == pActor0)
{
filterArray[i].filtered = false;
continue;
}
if (groundPlane == pActor1)
{
filterArray[i].filtered = false;
continue;
}

filterArray[i].filtered = bNoCollision;
}
}

5 Change Strategy
Whenever you change the policy used in your callback, or change the callback itself, you
need to also call NxActor::resetUserActorPairFiltering() for each actor that would change
its filtering state with regard to any of its current interactions (to be safe you may want to
simply call it for every actor with the flag set). Otherwise the changed policy would have
no effect until the pair separated and contacted once more.

void SpecialKeys(unsigned char key, int x, int y)


{
switch (key)
{
case 'v': {
gActorPairFiltering.bNoCollision = !gActorPairFiltering.bNoCollision;
box->resetUserActorPairFiltering();
sphere->resetUserActorPairFiltering();
capsule->resetUserActorPairFiltering();
pyramid->resetUserActorPairFiltering();
……

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
break; }
default: { break; }
}
}
Note: User actor pair filtering overrides any other actor pair filtering.

6 Conclusion
The user actor pair filtering allows us to specify our own filter for the collision pair
filtering.

7 Related Classes, Functions, and Parameters


NxUserActorPairFiltering
onActorPairs()
NxActorPairFilter

NxActorFlag
NX_AF_USER_ACTOR_PAIR_FILTERING

NxActor
resetUserActorPairFiltering()

NxScene
setUserActorPairFiltering()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
Lesson 302 – Memory Allocator

1 Introduction
The memory allocator allows us to specify our own memory allocation routines to be
used by the PhysX SDK.

2 The Memory Allocator


In UserAllocator.h, we define a class called UserAllocator.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
class UserAllocator : public NxUserAllocator
{
public:
UserAllocator();
virtual ~UserAllocator();

void reset();

void* malloc(NxU32 size);


void* malloc(NxU32 size, NxMemoryType type);
void* mallocDEBUG(NxU32 size, const char* file, int line);
void* mallocDEBUG(NxU32 size, const char* file, int line, const char*
className, NxMemoryType type);
void* realloc(void* memory, NxU32 size);
void free(void* memory);

NxU32* mMemBlockList;
NxU32 mMemBlockListSize;
NxU32 mMemBlockFirstFree;
NxU32 mMemBlockUsed;

NxI32 mNbAllocatedBytes;
NxI32 mHighWaterMark;
NxI32 mTotalNbAllocs;
NxI32 mNbAllocs;
NxI32 mNbReallocs;
};

We declare a pointer to an object of this class in CommonCode.cpp.

// User report globals


...
UserAllocator* gAllocator = NULL;

We then create a new memory allocator at the beginning of InitNx() and pass it into
NxCreatePhysicsSDK().

void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
3 Memory Operations
Every memory operation called by the SDK, calls the overriden function we provide to
the UserAllocator class. We flesh these out in UserAllocator.cpp.

#define MEMBLOCKSTART 64

UserAllocator::UserAllocator() : mNbAllocatedBytes(0), mHighWaterMark(0),


mTotalNbAllocs(0), mNbAllocs(0), mNbReallocs(0)
{
mMemBlockList = NULL;

#ifdef _DEBUG
// Initialize the Memory blocks list (DEBUG mode only)
mMemBlockList = (NxU32*)::malloc(MEMBLOCKSTART*4);
memset(mMemBlockList, 0, MEMBLOCKSTART*4);
mMemBlockListSize = MEMBLOCKSTART;
mMemBlockFirstFree = 0;
mMemBlockUsed = 0;
#endif
}

UserAllocator::~UserAllocator()
{
if(mNbAllocatedBytes) printf("Memory leak detected: %d bytes non released\n",
mNbAllocatedBytes);
if(mNbAllocs) printf("Remaining allocs: %d\n", mNbAllocs);
printf("Nb alloc: %d\n", mTotalNbAllocs);
printf("Nb realloc: %d\n", mNbReallocs);
printf("High water mark: %d Kb\n", mHighWaterMark/1024);

#ifdef _DEBUG
// Scanning for memory leaks
if(mMemBlockList && mMemBlockUsed)
{
NxU32 NbLeaks = 0;
printf("\n\n ICE Message Memory leaks detected :\n\n");
NxU32 used = mMemBlockUsed;
for(NxU32 i=0, j=0; i<used; i++, j++)
{
while(!mMemBlockList[j]) j++;
NxU32* cur = (NxU32*)mMemBlockList[j];
printf(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", cur+6,
cur[1], (const char*)cur[5], (const char*)cur[2], cur[3]);
NbLeaks++;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
// Free(cur+4);
}
printf("\n Dump complete (%d leaks)\n\n", NbLeaks);
}
// Free the Memory Block list
::free(mMemBlockList);
mMemBlockList = NULL;
#endif
}

void UserAllocator::reset()
{
mNbAllocatedBytes = 0;
mHighWaterMark = 0;
mNbAllocs = 0;
}

void* UserAllocator::malloc(NxU32 size)


{
#ifdef _DEBUG
return mallocDEBUG(size, NULL, 0, "Undefined", NX_MEMORY_PERSISTENT);
#endif

if(!size)
{
printf("Warning: trying to allocate 0 bytes\n");
return NULL;
}

void* ptr = (void*)::malloc(size+8);


mTotalNbAllocs++;
mNbAllocs++;

NxU32* blockStart = (NxU32*)ptr;


blockStart[0] = 0xDeadBabe;
blockStart[1] = size;

mNbAllocatedBytes+=size;
if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark =
mNbAllocatedBytes;

return ((NxU32*)ptr)+2;
#if 0
printf("Obsolete code called!\n");
return NULL;
#endif // 0
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
void* UserAllocator::malloc(NxU32 size, NxMemoryType type)
{
// return ::malloc(size);

#ifdef _DEBUG
return mallocDEBUG(size, NULL, 0, "Undefined", type);
#endif

if(!size)
{
printf("Warning: trying to allocate 0 bytes\n");
return NULL;
}

void* ptr = (void*)::malloc(size+8);


mTotalNbAllocs++;
mNbAllocs++;

NxU32* blockStart = (NxU32*)ptr;


blockStart[0] = 0xDeadBabe;
blockStart[1] = size;

mNbAllocatedBytes+=size;
if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark =
mNbAllocatedBytes;

return ((NxU32*)ptr)+2;
}

void* UserAllocator::mallocDEBUG(NxU32 size, const char* file, int line)


{
printf("Obsolete code called!\n");
return NULL;
}

void* UserAllocator::mallocDEBUG(NxU32 size, const char* file, int line, const char*
className, NxMemoryType type)
{

#ifdef _DEBUG

if(!size)
{
printf("Warning: trying to allocate 0 bytes\n");
return NULL;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
void* ptr = (void*)::malloc(size+24);
mTotalNbAllocs++;
mNbAllocs++;

NxU32* blockStart = (NxU32*)ptr;


blockStart[0] = 0xDeadBabe;
blockStart[1] = size;
blockStart[2] = (NxU32)file;
blockStart[3] = line;

mNbAllocatedBytes+=size;
if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark =
mNbAllocatedBytes;

// Insert the allocated block in the debug memory block list


if(mMemBlockList)
{
mMemBlockList[mMemBlockFirstFree] = (NxU32)ptr;
blockStart[4] = mMemBlockFirstFree;
mMemBlockUsed++;
if(mMemBlockUsed==mMemBlockListSize)
{
NxU32* tps =
(NxU32*)::malloc((mMemBlockListSize+MEMBLOCKSTART)*4);
memcpy(tps, mMemBlockList, mMemBlockListSize*4);
memset((tps+mMemBlockListSize), 0, MEMBLOCKSTART*4);
::free(mMemBlockList);
mMemBlockList = tps;
mMemBlockFirstFree = mMemBlockListSize-1;
mMemBlockListSize += MEMBLOCKSTART;
}
while (mMemBlockList[++mMemBlockFirstFree] &&
(mMemBlockFirstFree<mMemBlockListSize));
if(mMemBlockFirstFree==mMemBlockListSize)
{
mMemBlockFirstFree = (NxU32)-1;
while(mMemBlockList[++mMemBlockFirstFree] &&
(mMemBlockFirstFree<mMemBlockListSize));
}
}
else
{
blockStart[4] = 0;
}
blockStart[5] = (NxU32)className;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
return ((NxU32*)ptr)+6;
#else

// printf("Error: mallocDEBUG has been called in release!\n");


// NX_ASSERT(0);//Don't use debug malloc for release mode code!
// return 0;

return malloc(size, type);

#endif
}

void* UserAllocator::realloc(void* memory, NxU32 size)


{
// return ::realloc(memory, size);

if(!memory)
{
printf("Warning: trying to realloc null pointer\n");
return NULL;
}

if(!size)
{
printf("Warning: trying to realloc 0 bytes\n");
}

#ifdef _DEBUG

NxU32* ptr = ((NxU32*)memory)-6;


if(ptr[0]!=0xDeadBabe)
{
printf("Error: realloc unknown memory!!\n");
}
mNbAllocatedBytes -= ptr[1];
mNbAllocatedBytes += size;

if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark =
mNbAllocatedBytes;

void* ptr2 = ::realloc(ptr, size+24);


mNbReallocs++;
*(((NxU32*)ptr2)+1) = size;
if(ptr==ptr2)
{
return memory;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


7
*(((NxU32*)ptr2)) = 0xDeadBabe;
*(((NxU32*)ptr2)+1) = size;
*(((NxU32*)ptr2)+2) = 0; // File
*(((NxU32*)ptr2)+3) = 0; // Line

NxU32* blockStart = (NxU32*)ptr2;

// Insert the allocated block in the debug memory block list


if(mMemBlockList)
{
mMemBlockList[mMemBlockFirstFree] = (NxU32)ptr;
blockStart[4] = mMemBlockFirstFree;
mMemBlockUsed++;
if(mMemBlockUsed==mMemBlockListSize)
{
NxU32* tps =
(NxU32*)::malloc((mMemBlockListSize+MEMBLOCKSTART)*4);
memcpy(tps, mMemBlockList, mMemBlockListSize*4);
memset((tps+mMemBlockListSize), 0, MEMBLOCKSTART*4);
::free(mMemBlockList);
mMemBlockList = tps;
mMemBlockFirstFree = mMemBlockListSize-1;
mMemBlockListSize += MEMBLOCKSTART;
}
while (mMemBlockList[++mMemBlockFirstFree] &&
(mMemBlockFirstFree<mMemBlockListSize));
if(mMemBlockFirstFree==mMemBlockListSize)
{
mMemBlockFirstFree = (NxU32)-1;
while(mMemBlockList[++mMemBlockFirstFree] &&
(mMemBlockFirstFree<mMemBlockListSize));
}
}
else
{
blockStart[4] = 0;
}
blockStart[5] = 0; // Classname

return ((NxU32*)ptr2)+6;

#else
NxU32* ptr = ((NxU32*)memory)-2;
if(ptr[0]!=0xDeadBabe)
{
printf("Error: realloc unknown memory!!\n");

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


8
}
mNbAllocatedBytes -= ptr[1];
mNbAllocatedBytes += size;

if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark =
mNbAllocatedBytes;

void* ptr2 = ::realloc(ptr, size+8);


mNbReallocs++;
*(((NxU32*)ptr2)+1) = size;
if(ptr==ptr2) return memory;

*(((NxU32*)ptr2)) = 0xDeadBabe;
return ((NxU32*)ptr2)+2;
#endif
}

void UserAllocator::free(void* memory)


{
// ::free(memory); return;

if(!memory)
{
printf("Warning: trying to free null pointer\n");
return;
}

#ifdef _DEBUG
NxU32* ptr = ((NxU32*)memory)-6;
if(ptr[0]!=0xDeadBabe)
{
printf("Error: free unknown memory!!\n");
}
mNbAllocatedBytes -= ptr[1];
mNbAllocs--;

NxU32 MemBlockFirstFree = ptr[4];


NxU32 Line = ptr[3];
const char* File = (const char*)ptr[2];

// Remove the block from the Memory block list


if(mMemBlockList)
{
mMemBlockList[MemBlockFirstFree] = 0;
mMemBlockUsed--;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


9
ptr[0]=0xDeadDead;
ptr[1]=0;
::free(ptr);
#else
NxU32* ptr = ((NxU32*)memory)-2;
if(ptr[0]!=0xDeadBabe)
{
printf("Error: free unknown memory!!\n");
}
mNbAllocatedBytes -= ptr[1];
if(mNbAllocatedBytes<0)
{
printf("Oops (%d)\n", ptr[1]);
}
mNbAllocs--;
ptr[0]=0xDeadDead;
ptr[1]=0;
::free(ptr);
#endif
}

The overridden functions are NxUserAllocator::reset(), NxUserAllocator::malloc(),


NxUserAllocator::free(), and NxUserAllocator::realloc().

4 Example Usage
In the demo, memory gets allocated to build the convex hull for the convex object.

NxActor* CreatePyramid(const NxVec3& pos, const NxVec3& boxDim, const NxReal


density)
{
...
NxInitCooking();
if (0)
{
...
}
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookConvexMesh(convexDesc, buf);
convexShapeDesc.meshData = gPhysicsSDK-
>createConvexMesh(MemoryReadBuffer(buf.data));
}
...

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


10
}

void InitNx()
{

pyramid = CreatePyramid(NxVec3(0,0,0), NxVec3(1,0.5,1.5), 10);

}

The NxCookConvexMesh() function calls NxUserAllocator::malloc() to reserve memory


for the convex mesh.

5 Conclusion
The memory allocator allows us to specify our own memory allocation and deallocation
functions for the SDK to use.

6 Related Classes, Functions, and Parameters


NxUserAllocator
reset()
malloc()
mallocDEBUG()
free()
realloc()

mMemBlockList
mMemBlockListSize
mMemBlockFirstFree
mMemBlockUsed
mNbAllocatedBytes
mHighWaterMark
mTotalNbAllocs
mNbAllocs
mNbReallocs

NxMemoryType

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


11
Lesson 303 – Contact Report

1 Introduction
In this lesson, we learn about the User Contact Report and the Contact Stream Iterator.
We will make the two box actors in our scene into contact pairs, collect information on
how they contact each other, and display this information using the debug renderer and
our own drawing routines.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
2 The Contact Report
We have added a new file to the project, ContactReport.h. This file contains a class
called ContactReport which is derived from NxUserContactReport. We make an instance
of this class which we attached to the SDK in InitNx().

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Create the scene
NxSceneDesc sceneDesc;
sceneDesc.userContactReport = &gContactReport;
gScene = gPhysicsSDK->createScene(sceneDesc);

}

At the end of InitNx() we call gScene->setActorPairFlags() on the two actors to make


them into a contact pair. We will get information on every contact pair we have set up
this way every simulation step that they are in contact. When they are in contact,
onContactNotify() in ContactReport.h is called, passing in a reference to the contact pair
and the nature of the contact, whether the pair just started touching, whether they’ve been
touching, or whether they have stopped touching, set up in setActorPairFlags() by the
NX_NOTIFY_ON_START_TOUCH, NX_NOTIFY_ON_TOUCH, and
NX_NOTIFY_ON_END_TOUCH flags.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box1 = NULL;
NxActor* box2 = NULL;

void InitNx()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

box1 = CreateBox(NxVec3(-3,5,0), NxVec3(0.75,0.75,0.75), 5);


box2 = CreateBox(NxVec3(3,0,0), NxVec3(1,1,1), 5);

GameObject* Object1 = new GameObject;


Object1->id = 1;
Object1->events = 0;
box1->userData = Object1;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
GameObject* Object2 = new GameObject;
Object2->id = 2;
Object2->events = 0;
box2->userData = Object2;

gSelectedActor = box1;

gScene->setActorPairFlags(*box1, *box2, NX_NOTIFY_ON_START_TOUCH |


NX_NOTIFY_ON_TOUCH |
NX_NOTIFY_ON_END_TOUCH);

}

Look at onContactNotify() in ContactReport.h. This is called every time the two boxes,
the contact pair, are in contact. When it is called, userData on each actor is set, so the
actor knows about the nature of the contact through its user data. (User data is a topic we
will see more and more of later. It is important for linking game related data to our
actors, shapes, etc.)

3 The Contact Stream Iterator


The SDK holds something called a contact stream, a list of all contact pairs in the
simulation that are currently in contact, and the shapes, faces, and points of contact on the
actors. In onContactNotify() in ContactReport.h, we get a contact stream iterator, i, that
points to this stream, specifically at the location of the pair of actors in contact that we
pass in. Using i, we can step through each shape, face, and point of contact between the
two actors.

// Iterate through the contact points


NxContactStreamIterator i(pair.stream);

while(i.goNextPair())
{
while(i.goNextPatch())
{
const NxVec3& contactNormal = i.getPatchNormal();
while(i.goNextPoint())
{
const NxVec3& contactPoint = i.getPoint();

}
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
The NxPair has information on the normal forces and frictional forces between the two
actors in the pair. Here we draw the forces at their contact points (as they are applied at
their contact points).

4 Conclusion
New things you should have learned: how to attach an NxUserContactReport to the SDK
to get information on contact pairs, how to make two actors into contact pair and flag
them for particular contact information, how to use the contact stream iterator to get
shapes, faces, and points of contact between two actors in a contact pair.

5 Related Classes, Functions, and Parameters


NxUserContactReport
onContactNotify()

NxContactStreamIterator
goNextPair()
goNextPatch()
goNextPoint()

NxScene
setActorPairFlags()

NxContactPair

NxContactPairFlag
NX_NOTIFY_ON_START_TOUCH
NX_NOTIFY_ON_END_TOUCH
NX_NOTIFY_ON_TOUCH

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
Lesson 304 – Trigger Report

1 Introduction
In this lesson you will learn about triggers. Triggers are shapes used in games to cue
events when particular objects intersect them. Trigger shapes are created by setting the
NX_TRIGGER_ENABLE flag on the shape descriptor. For these shapes to work as
triggers with code you supply, you need to attach a Trigger Report to the scene. A
Trigger Report is a class derived from NxUserTriggerReport. This Trigger Report will
call onTrigger() every frame the trigger is intersected by another shape, passing in a

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
pointer to the trigger shape, the intersecting shape, and the state of the intersection,
whether the intersecting shape has just entered the trigger, whether it has just left the
trigger, or whether it has been inside the trigger for more than one frame.

2 Triggers
We create four regular rigid body actors, a box, a sphere, a capsule, and a pyramid, and
place them around a trigger actor. The trigger actor is constructed in CreateTriggerBox().

// Create a static trigger


NxActor* CreateTriggerBox(const NxVec3& pos, const NxVec3& boxDim)
{
NxActorDesc actorDesc;

NxBoxShapeDesc boxDesc;
boxDesc.dimensions = boxDim;
boxDesc.shapeFlags |= NX_TRIGGER_ENABLE;

actorDesc.shapes.pushBack(&boxDesc);
actorDesc.globalPose.t = pos + NxVec3(0, boxDim.y, 0);

return gScene->createActor(actorDesc);
}

void InitNx()
{

box = CreateBox(NxVec3(-5,0,0), NxVec3(0.5,1,0.5), 10);
box->setLinearDamping(0.5);
sphere = CreateSphere(NxVec3(0,0,5), 0.5, 10);
sphere->setLinearDamping(0.5);
capsule = CreateCapsule(NxVec3(5,0,5), 1, 0.5, 10);
capsule->setLinearDamping(0.5);
pyramid = CreatePyramid(NxVec3(5,0,0), NxVec3(1,0.5,1), 10,
pyramidConvexMeshDesc);
pyramid->setLinearDamping(0.5);

triggerBox = CreateTriggerBox(NxVec3(0,0,0), NxVec3(2,2,2));


triggerBox->userData = (void*)-1;
gScene->setUserTriggerReport(&gTriggerReport);

gSelectedActor = box;

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
The trigger actor is a box and it is static as it has no body descriptor. Trigger shapes are
massless by definition. They are meant as trigger volumes, areas in a game that players
and objects can enter which activate certain events, like a room in a dungeon might have
a trigger shape filling up the doorway, signaling monsters to come out and attack the
player once he enters the room.

Run the demo and push any of the regular rigid body actors into the green trigger box.
Note that it turns red when any of them intersect the box and returns to green once
they’re all out.

We have attached a Trigger Report to our scene in InitNx().

gScene->setUserTriggerReport(&gTriggerReport);

Note we have used NxScene::setUserTriggerReport() to attach the trigger report to the


scene. We could have also added it to the scene descriptor when the scene was first
created.

sceneDesc.userTriggerReport = &gTriggerReport;

These two methods can be used to add user callbacks to the scene. In “Lesson 303:
Contact Report”, we could have used NxScene::setContactReport(&gContactReport) to
attach the contact report to the scene.

3 The Trigger Report


You will find the User Trigger Report, gTriggerReport, defined in TriggerReport.h:

class TriggerReport : public NxUserTriggerReport


{
public:
virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape,
NxTriggerFlag status)
{
if (status & NX_TRIGGER_ON_ENTER)
{
// A body just entered the trigger area
gNbTouchedBodies++;
}
if (status & NX_TRIGGER_ON_LEAVE)
{
// A body just left the trigger area
gNbTouchedBodies--;
}
NX_ASSERT(gNbTouchedBodies>=0);
...

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
}
} gTriggerReport;

onTrigger() is called each frame each trigger shape is intersected by another shape. The
SDK passes in the trigger shape, the intersecting shape, and the status of the intersection
in the form of an NxTriggerFlag (which is an NxShapeFlag), either
NX_TRIGGER_ON_ENTER, NX_TRIGGER_ON_LEAVE, or
NX_TRIGGER_ON_STAY.

We keep track of the number of bodies touching the shape by subtracting the number of
shapes that have left the trigger from the number of shapes that have entered the trigger.

4 Forcefields
Run the demo and hit “v”. You'll notice as you move selected actors into the trigger box,
they get buoyed up as if there's some sort of wind field running through the box. The
force is constant and uniform over the box.

We have created a forcefield from our trigger actor by marking actors that enter the
trigger in our trigger report. The actors are marked by setting the
UD_IS_INSIDE_TRIGGER bit on their user data flags field.

class TriggerReport : public NxUserTriggerReport


{
public:
virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape,
NxTriggerFlag status)
{
if (status & NX_TRIGGER_ON_ENTER)
{
// A body just entered the trigger area
gNbTouchedBodies++;
}
if (status & NX_TRIGGER_ON_LEAVE)
{
// A body just left the trigger area
gNbTouchedBodies--;
}
NX_ASSERT(gNbTouchedBodies>=0);

// Mark actors in the trigger area to apply forcefield forces to them


NxActor* triggerActor = &triggerShape.getActor();
if (((ActorUserData*)(triggerActor->userData))->flags & UD_IS_TRIGGER)
{
if (status & NX_TRIGGER_ON_STAY)
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
NxActor* otherActor = &otherShape.getActor();
((ActorUserData*)(otherActor->userData))->flags |=
UD_IS_INSIDE_TRIGGER;
}
}
}
} gTriggerReport;

In Lesson304.cpp, we then apply forces to the marked actors.

// Simulation globals
...
bool bForcefieldOn = false;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
...
case 'v': { bForcefieldOn = !bForcefieldOn; break; }
...
}
}
...
void ApplyForcefieldToActors(const NxVec3 force)
{
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
for(NxU32 i = 0; i < nbActors; i++)
{
if (actors[i]->userData == (void*)3)
{
actors[i]->addForce(force);
actors[i]->userData = NULL;
}
}
}

void ProcessInputs()
{
...
// Apply an upward force of 1000 to actors caught in the trigger
if (bForcefieldOn)
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
ApplyForcefieldToActors(NxVec3(0,1000,0));
}
...
}

void RenderCallback()
{
...
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}
...
}

You will want to tailor your force function to the type of forcefield you're building. For
something like a gravity field about an object, you will probably want to create a
spherical trigger shape, then in much the fashion done above, collect the actors that
intersect it, find out the distance from the center of the trigger sphere to the actors in the
forcefield, and apply a force toward the center that fades with the square of the distance
of the actors from the center of the sphere.

5 Conclusion
You have now learned how to add trigger shapes to the scene and create a Trigger Report
that will call code when the trigger is intersects other shapes in the scene. Triggers are
important entities in games as many games use them to gauge when the player or other
objects have entered or left an area, or remained there for a certain duration, providing
important information about the whereabouts of the player and other objects and
activating certain game-based events based on this information.

6 Related Classes, Functions, and Parameters


NxUserTriggerReport

NxTriggerFlag (typedef NxShapeFlag NxTriggerFlag)


NX_TRIGGER_ENABLE
NX_TRIGGER_ON_ENTER
NX_TRIGGER_ON_LEAVE
NX_TRIGGER_ON_STAY

NxShape
getActor()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
Lesson 305 – Raycast Report

1 Introduction
Now we move on to the concept of raycasting. Raycasting involves shooting a ray, a line
emanating from a source, through the scene. The information we get from the raycast
includes what shape we hit, how far away the shape is from the origin of the ray, the
point of impact of the ray, the normal to the impact on the shape, and, if the shape hit is a
triangle mesh, the surface triangle hit and the barycentric coordinates of the point of
impact.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
Raycasting is useful for determining distances to objects as well as detecting which
objects are closest and what features of the object are closest. It is used in visibility
determination to calculate what objects in the scene are visible to the player. It is used in
AI to detect targets, navigate the AI through environments, find cover positions, and
sense other items of interest. It is also used for shadow and lighting calculations.

2 Raycasting
We have lined up four dynamic objects in a row on the x-axis: a box, a sphere, a capsule,
and a pyramid from left to right. To the left of the box is a smaller, kinematic box (which
we call “emitter”), from which we shoot our ray out into the scene along the negative x-
axis.

// Keyboard globals

bool bRaycastClosestShape = true;

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;
NxActor* sphere = NULL;
NxActor* capsule = NULL;
NxActor* pyramid = NULL;
NxActor* emitter = NULL;

const NxCollisionGroup groupA = 1;


const NxU32 groupFlagA = 1 << groupA;

const NxCollisionGroup groupB = 2;


const NxU32 groupFlagB = 1 << groupB;

void InitNx()
{

emitter = CreateBox(NxVec3(4,0.75,0),NxVec3(0.5,0.25,0.25), 1);
emitter->raiseBodyFlag(NX_BF_KINEMATIC);

box = CreateBox(NxVec3(2,0,0), NxVec3(0.75,0.75,0.75), 10);


sphere = CreateSphere(NxVec3(0,0,0), 1, 10);
capsule = CreateCapsule(NxVec3(-2,0,0), 1, 0.5, 10);
pyramid = CreatePyramid(NxVec3(-4,0,0), NxVec3(1,1,1), 10);

SetActorGroup(box, groupA);
SetActorGroup(sphere, groupA);
SetActorGroup(capsule, groupB);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
SetActorGroup(pyramid, groupB);

gSelectedActor = box;

}

Note we have assigned the box and sphere to collision group A and the capsule and
pyramid to collision group B. Take a look at RunPhysics().

void RenderCallback()
{

// Cast a ray out of the emitter along its negative x-axis
if (bRaycastClosestShape)
RaycastClosestShapeFromActor(emitter, groupFlagA | groupFlagB);
else
RaycastAllShapesFromActor(emitter, groupFlagA | groupFlagB);

}

The demo calls RaycastClosestShapeFromActor() by default.

void RaycastClosestShapeFromActor(NxActor* actor, NxU32 groupFlag)


{
// Get ray origin
NxVec3 orig = actor->getCMassGlobalPosition();

// Get ray direction


NxVec3 dir;
NxMat33 m = actor->getGlobalOrientation(m);
m.getColumn(0, dir);
dir = -dir;

NxRay ray(orig, dir);


NxRaycastHit hit;
NxReal dist;

// Get the closest shape


NxShape* closestShape = gScene->raycastClosestShape(ray, NX_ALL_SHAPES, hit,
groupFlag);
if (closestShape)
{
const NxVec3& worldImpact = hit.worldImpact;
dist = hit.distance;

}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
We create the ray using the center of mass of the actor passed in as the origin of the ray
and the actor’s negative x-axis as the direction of the ray.

3 Raycasting the Closest Shape


We then call NxScene::raycastClosestShape() using our ray.

// Get the closest shape


NxShape* closestShape = gScene->raycastClosestShape(ray, NX_ALL_SHAPES, hit,
groupFlag);

We pass in the ray, NX_ALL_SHAPES (meaning we want to raycast against both static
and dynamic shapes), hit (the NxRaycastHit structure which contains information on the
distance, point of impact, etc.), and finally the groupFlag. The groupFlag specifies a
filter of groups we want to raycast against.

If you run the demo, you see the emitter box on the left casting a blue ray from its center
to the right (along the negative x-axis). The ray strikes the box immediately to the right
of the emitter and a red sphere indicates the point of impact. Use “umijkl” to move the
objects around and see how the ray strikes the objects.

Using the raycastClosestShape(), the SDK finds the first shape the ray strikes, returns a
pointer to the shape and information about the point of impact through the NxRaycastHit
structure.

4 Raycasting all Shapes and the User Raycast Report


Reset the demo and hit the “v” key. This will toggle off bRaycastClosestShape and
RunPhysics() will now call RaycastAllShapesFromActor() instead of
RaycastClosestShapeFromActor().

void RaycastAllShapesFromActor(NxActor* actor, NxU32 groupFlag)


{
// Get ray origin
NxVec3 orig = actor->getCMassGlobalPosition();

// Get ray direction


NxVec3 dir;
NxMat33 m = actor->getGlobalOrientation(m);
m.getColumn(0, dir);
dir = -dir;

NxRay ray(orig, dir);



// Get all shapes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
NxU32 nbShapes = gScene->raycastAllShapes(ray, gRaycastReport,
NX_ALL_SHAPES, groupFlag);
}

We construct the same ray coming from the center of mass of the actor passed in (the
emitter) for the ray origin and the emitter’s negative x-axis for the ray direction. We use
the ray in another function called NxScene::raycastAllShapes(), passing in
NX_ALL_SHAPES and groupFlag, as before, and this time passing in gRaycastReport,
an NxUserRaycastReport object instead of hit, the NxRaycastHit object. Instead of
sending the ray through the scene and returning the hit as soon as we find the first shape,
we are now sending the ray though the scene and collecting information on all the shapes
that it hits. Every time a shape is hit by the ray, gRaycastReport::onHit() is called,
passing in the NxRaycastHit object for the particular hit.

Take a look at RaycastReport::onHit() in RaycastReport.h.

class RaycastReport : public NxUserRaycastReport


{
virtual bool onHit(const NxRaycastHit& hit)
{
int userData = (int)hit.shape->getActor().userData;
userData |= 1; // Mark as hit
hit.shape->getActor().userData = (void*)userData;

const NxVec3& worldImpact = hit.worldImpact;



}
} gRaycastReport;

We call NxShape::getActor() to get the actor the hit shape belongs to. We then mark the
actor as hit through its userData. In this demo, we do nothing with this data, it is here
only for instructional purposes. You could potentially do any number of things
depending on what the actor’s user data points to: light up the actor, trigger an impact
sound, broadcast an “I’m hit!” message to the game, etc.

We then use the worldImpact point of the hit to create a red dot at the position the ray
intersects the shape. Run the demo and hit “v” to switch from raycasting the closest
shape to raycasting all shapes. Notice the ray now stabs through all four objects in the
scene and red dots appear at the point of impact for each object.

5 Getting the Hit Face from the Triangle Mesh Shape


Note the green triangle that appears when the ray strikes the pyramid. We have rigged
onHit() to display the face it hits if it hits a triangle mesh shape. Take a look at
RaycastReport::onHit() again.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
virtual bool onHit(const NxRaycastHit& hit)
{

// Light up the hit polygon on a convex shape
NxConvexShape *tmShape = hit.shape->isConvexMesh();
if (tmShape)
{
NxU32 verts[3];
NxVec3 pos[3];
NxConvexMesh& tm = tmShape->getConvexMesh();
NxConvexMeshDesc tmDesc;
tm.saveToDesc(tmDesc);

const NxU8* triPtr = (const NxU8*)tmDesc.triangles;

triPtr += hit.faceID*tmDesc.triangleStrideBytes;
verts[0] = ((NxU32*)triPtr)[0];
verts[1] = ((NxU32*)triPtr)[1];
verts[2] = ((NxU32*)triPtr)[2];

const NxVec3* vertPtr = (const NxVec3*)tmDesc.points;

pos[0] = ((NxVec3*)vertPtr)[verts[0]];
pos[1] = ((NxVec3*)vertPtr)[verts[1]];
pos[2] = ((NxVec3*)vertPtr)[verts[2]];

NxMat34 shapePose = hit.shape->getGlobalPose();

RaycastTriangle rt(shapePose*pos[0], shapePose*pos[1], shapePose*pos[2],


NxVec3(0,1,0));
rtArray.pushBack(rt);
}

return true;
}

If the hit shape is a convex shape, we will have an NxConvexShape to get an


NxConvexMesh from.

NxTriangleMeshShape *tmShape = hit.shape->isTriangleMesh();


if (tmShape)
{

NxConvexMesh& tm = tmShape->getConvexMesh();

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
We then use NxConvexMesh::saveToDesc() to get the descriptor of our convex mesh. In
previous lessons we have filled out descriptor information and constructed objects from
them. Now we are extracting the descriptor from a pre-built object.

NxConvexMesh& tm = tmShape->getConvexMesh();
NxConvexMeshDesc tmDesc;
tm.saveToDesc(tmDesc);

saveToDesc() is a common function throughout the SDK and can be called on any object
with a descriptor.

The convex mesh descriptor we get has a “triangles” field which we use to get the vertex
indices of the face using the hit.faceID. We increment the “triangles” pointer, triPtr by
the face number times the triangle stride bytes, the number of bytes of storage per
triangle. We store the indices in our “verts” array.

triPtr += hit.faceID*tmDesc.triangleStrideBytes;
verts[0] = ((NxU32*)triPtr)[0];
verts[1] = ((NxU32*)triPtr)[1];
verts[2] = ((NxU32*)triPtr)[2];

(Note: triangleStrideBytes is currently either 2 or 4 and defaults to 4, so we know the


triangle indices are NxU32 integers. If we set the NX_MF_16_BIT_INDICES flag on
our triangle mesh flags, the triangleStrideBytes will be 2 and the triangle indices will be
NxU16 integers.)

Next, we get the “points” pointer, vertPtr. Using the “verts” indices, we get the vertex
positions of our face and store them in our “pos” array.

const NxVec3* vertPtr = (const NxVec3*)tmDesc.points;

pos[0] = ((NxVec3*)vertPtr)[verts[0]];
pos[1] = ((NxVec3*)vertPtr)[verts[1]];
pos[2] = ((NxVec3*)vertPtr)[verts[2]];

The “pos” vertex positions are in the shape’s local space. We need to get the shape’s
global transformation matrix to convert them to world space.

NxMat34 shapePose = hit.shape->getGlobalPose();

RaycastTriangle rt;
rt.p0 = shapePose*pos[0];
rt.p1 = shapePose*pos[1];
rt.p2 = shapePose*pos[2];
rt.color = NxVec3(0,1,0);
rtArray.pushBack(rt);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


7
The triangle we push onto our triangle array now has the correct world coordinates for
rendering.

6 Filtering Raycasts with Collision Groups


Hit “v” to go back to raycasting the closest shape.

As well learned before, you can put your shapes into collision groups by using the
NxShape::setGroup() function. Shapes are in collision group 0 by default, but you can
give them an integer collision group number from 0 to 31. You can then do things like
turn off collisions between groups if you want certain sets of objects not to collide with
others. You use a collision group number and a collision group flag that you get by
leftshifting 1 by the collision group number

For raycasting, by default you will be raycasting against all shapes in the scene. We have
separated our actors into two collision groups, A and B.

const NxCollisionGroup groupA = 1;


const NxU32 groupFlagA = 1 << groupA;

const NxCollisionGroup groupB = 2;


const NxU32 groupFlagB = 1 << groupB;

void InitNx()
{

SetActorGroup(box, groupA);
SetActorGroup(sphere, groupA);
SetActorGroup(capsule, groupB);
SetActorGroup(pyramid, groupB);

}

Our raycast call looks like this:

void RaycastClosestShapeFromActor(NxActor* actor, NxU32 groupFlag)


{

// Get the closest shape
NxShape* closestShape = gScene->raycastClosestShape(ray,
NX_ALL_SHAPES, hit, groupFlag);

}

void RunPhysics()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


8

RaycastClosestShapeFromActor(emitter, groupFlagA | groupFlagB);

}

So we pass in groupFlag = groupFlagA | groupFlagB to NxScene::raycastClosestShape().


This signals we will be raycasting against the objects in both group A and group B.

The box and sphere are in group A, near the origin of the ray, while the capsule and
pyramid are in group B, on the other side of group A from the origin of the ray. Change
RaycastClosestShapeFromActor() so you are only raycasting against group B.

RaycastClosestShapeFromActor(emitter, groupFlagB);

With groupFlagB as the parameter for RaycastClosestObjectsInGroup(), the ray will


shoot down the x-axis, passing through the box and sphere and hitting the capsule, the
first object it hits in groupB. If you pass in groupFlagA, the ray will hit the box, the first
object it hits in group A.

This is a powerful way to raycast against selected groups or logical combinations of


groups. If you are determining line-of-sight, for instance, you may want to put all
transparent objects into a groupTransparent collision group, then raycast against
everything but groupTransparent to get your line of sight (effectively ignoring
groupTransparent in the line-of-sight raycast).

RaycastClosestShapeFromActor(emitter, ~groupTransparent);

7 Moving the Kinematic Emitter


Hit “r” a few times to select the emitter as the focus object. Use “umijkl” to move it
around. The emitter is a kinematic object, but we have rigged it so that you can move it
around and adjust its direction.

NxVec3 ApplyVelocityToActor(NxActor* actor, const NxVec3& velDir, const NxReal


velStrength, bool velMode)
{
NxVec3 velVec = velStrength*velDir;

if (velMode)
{
actor->moveGlobalPosition(actor->getGlobalPosition() +
0.0001*velStrength*velDir);
}
else
{
NxQuat q;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


9
q.fromAngleAxis(0.001*velStrength,velDir);
NxMat33 deltaM(q);
actor->moveGlobalOrientation(actor->getGlobalOrientation()*deltaM);
}

return velVec;
}

NxVec3 ApplyForceToActor(NxActor* actor, const NxVec3& forceDir, const NxReal


forceStrength, bool forceMode)
{
NxVec3 forceVec = forceStrength*forceDir;

if (forceMode)
actor->addForce(forceVec);
else
actor->addTorque(forceVec);

return forceVec;
}

NxVec3 MoveActor(NxActor* actor, const NxVec3& moveDir, const NxReal


moveStrength, bool moveMode)
{
if (actor->readBodyFlag(NX_BF_KINEMATIC))
return ApplyVelocityToActor(actor, moveDir, moveStrength, moveMode);
else
return ApplyForceToActor(actor, moveDir, moveStrength, moveMode);
}
,,,
void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{

// Force controls
case 'i': {gForceVec = MoveActor(gSelectedActor, NxVec3(0,0,1),
gForceStrength, bForceMode); break; }
}
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


10
We call ApplyVelocityToActor () to move the focus object with translational velocity
0.0001*velStrength in Force Mode and rotational velocity 0.001*velStrength in Torque
Mode.

When pushing the emitter, we call gSelectedActor->moveGlobalPosition(). This sets a


global target position for the emitter “gSelectedActor->getGlobalPosition + gForceVec”,
so a delta vector from the emitter’s global position. In Torque Mode, we set a target
global orientation “gSelectedActor->getGlobalOrientation()*deltaM”, so a delta
orientation matrix from the emitter’s global orientation.

8 Conclusion
Raycasting is very important to games, so it is well worth examining the different
raycasting functions the SDK provides as well as how to use the user raycast report and
the information you get from raycast hits.

9 Related Classes, Functions, and Parameters


NxRay
orig
dir

NxRaycastHit
shape
worldImpact
worldNormal
faceID
distance
u
v
flags

NxScene
raycastClosestShape()
raycastAllShapes()

NxShapesTyep
NX_STATIC_SHAPES
NX_DYNAMIC_SHAPES
NX_ALL_SHAPES = NX_STATIC_SHAPES | NX_DYNAMIC_SHAPES

NxUserRaycastReport
onHit()

NxShape
getActor()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


11
isConvexMesh()
getGlobalPose()

NxConvexMeshShapeDesc
meshData
scale
flags

NxConvexMeshShape
getConvexMesh()
saveToDesc()

NxConvexMeshDesc
numVertices
numTriangles
pointStrideBytes
triangleStrideBytes
points
triangles
flags

NxConvexMesh
saveToDesc()

NxActor
moveGlobalPosition()
getGlobalPosition()
moveGlobalOrientation()
getGlobalOrientation()

NxMeshFlags
NX_MF_16_BIT_INDICES

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


12
Lesson 306 – Entity Report

1 Introduction
PhysX has a wide array of standalone intersection tests you can use to determine whether
a shape or triangles lie within a particular region of the scene.

2 Intersection Tests
In InitNx(), we create our usual set of actors:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;
NxActor* sphere = NULL;
NxActor* capsule = NULL;
NxActor* pyramid = NULL;
...
void InitNx()
{
...
// Create the objects in the scene
groundPlane = CreateGroundPlane();

box = CreateBox(NxVec3(5,0,0), NxVec3(0.5,1,0.5), 20);


sphere = CreateSphere(NxVec3(0,0,5), 1, 10);
capsule = CreateCapsule(NxVec3(-5,0,0), 2, 0.5, 10);
pyramid = CreatePyramid(NxVec3(0,0,0), NxVec3(1,0.5,1.5), 10);
...
}

Look at RenderCallback().

void RenderCallback()
{
// Clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

ProcessCameraKeys();
SetupCamera();

if (gScene && !bPause)


{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

// Display scene
RenderActors(bShadows);

RenderIntersectedEntities();

if (bForceMode)
DrawForce(gSelectedActor, gForceVec, NxVec3(1,1,0));
else

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
DrawForce(gSelectedActor, gForceVec, NxVec3(0,1,1));
gForceVec = NxVec3(0,0,0);

DisplayText();

glFlush();
glutSwapBuffers();
}

After RenderActors(), we call RenderIntersectedEntities(). Take a look at the first case in


this function, the Sphere-Shape Test.

// Intersection test globals


typedef enum
{
SPHERE_SHAPE_TEST,
AABB_SHAPE_TEST,
PLANES_SHAPE_TEST,
SPHERE_SHAPE_CHECK,
AABB_SHAPE_CHECK,
AABB_TRIANGLE_TEST,
OBB_TRIANGLE_TEST,
CAPSULE_TRIANGLE_TEST,
SPHERE_TRIANGLE_TEST,
NUM_INTERSECTION_TESTS
} kIntersectionTestType;

NxU32 gIntersectionTestType = SPHERE_SHAPE_TEST;


const NxU32 gNumIntersectionTestTypes = 9;

// HUD globals
char gDisplayString[512] = "";
char* gIntersectionTestTypeString[gNumIntersectionTestTypes] =
{
"Sphere-Shape Test","AABB-Shape Test",
"Planes-Shape Test","Sphere-Shape Check",
"AABB-Shape Check", "AABB-Triangle Test",
"OBB-Triangle Test", "Capsule-Triangle Test",
"Sphere-Triangle Test"
};
...
void RenderIntersectedEntities()
{
switch (gIntersectionTestType)
{
case SPHERE_SHAPE_TEST:
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
NxSphere worldSphere(NxVec3(0,0,0), 5);
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 nbShapes = gScene->getNbDynamicShapes(); // + gScene-
>getNbStaticShapes()
NxShape** shapes = (NxShape**)NxAlloca(nbShapes*sizeof(NxShape*));
for (NxU32 i = 0; i < nbShapes; i++) shapes[i] = NULL;
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;
gScene->overlapSphereShapes(worldSphere, shapeType, nbShapes, shapes,
&gShapeReport, activeGroups, groupsMask);
DrawWireSphere(&worldSphere, NxVec3(1,0,0));
while (nbShapes--)
{
if (shapes[nbShapes])
{
DrawWireShape(shapes[nbShapes], NxVec3(0,0,0));
}
}
}
break;
...
}
}

We have set up a group of dynamic objects on a plane. With the


NxScene::overlapSphereShapes() call, you can pass in a sphere and get all the shapes it
intersects. Our sphere in this case is ((0,0,0), 5), or a sphere 10 meters wide centered at
the origin.

We check only for the dynamic shapes the sphere intersects by passing in the
NX_DYNAMIC_SHAPES flag. To check only for static shapes intersected by the
sphere, pass in the NX_STATIC_SHAPES flag. To check for all shapes intersected by
the sphere, pass in the NX_ALL_SHAPES flag.

We set the activeGroups and groupsMask parameters to check for shapes in all available
shape groups.

NxU32 activeGroups = 0xffffffff;


NxGroupsMask* groupsMask = NULL;

Refer to “Lesson 110: Collision Filtering” to review how to use these parameters to use
the tests for particular shape groups.

You can retrieve the shapes the sphere intersects in two ways:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
1. Pass a buffer of shape pointers into overlapSphereShapes(). We do this here with the
(shapes*)[] array. The call will fill the shape pointer array with pointers to the shapes
intersected by our sphere.

2. Pass a User Entity Report into overlapSphereShapes(). This is gShapeReport defined


in EntityReport.h. After passing this in and calling
NxScene::simulate(),NxScene::flushStream(),NxScene::fetchResults(), gShapeReport
will have set the user data to (void*)-1 on all shapes intersecting the sphere.

Here we do both. We draw our wire sphere.

DrawWireSphere(&worldSphere, NxVec3(1,0,0));

...and then draw black wireframes around the shapes retrieved from
NxScene::overlapSphereShapes().

while (nbShapes--)
{
if (shapes[nbShapes])
{
DrawWireShape(shapes[nbShapes], NxVec3(0,0,0));
}
}

We also pass in gShapeReport to NxScene::overlapSphereShapes().

gScene->overlapSphereShapes(worldSphere, shapeType, nbShapes, shapes,


&gShapeReport, activeGroups, groupsMask);

3 The User Entity Report


Look at the Entity Report object in EntityReport.h.

class ShapeReport : public NxUserEntityReport<NxShape*>


{
public:
virtual bool onEvent(NxU32 nbShapes, NxShape** shapes)
{
while (nbShapes--)
{
shapes[nbShapes]->userData = (void*)-1;
}

return true;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
} gShapeReport;

gShapeReport is an Entity Report that cycles through all the shapes encountered in the
intersection test. In this case, we pass it into overlapSphereShapes(), so it cycles through
all shapes in the sphere.

gScene->overlapSphereShapes(worldSphere, shapeType, nbShapes, shapes,


&gShapeReport, activeGroups, groupsMask);

For each shape, it points the shapes user data to (void*)-1. We use this flag when we
draw our focus actor to outline it in purple rather than white, showing that it has passed
our intersection test.

void RenderActors(bool shadows)


{
// Render all the actors in the scene
int nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;
DrawIntersectedActor(actor);
...
}

RenderActors() calls DrawIntersectedActor() from DrawShapes.cpp.

void DrawIntersectedActor(NxActor *actor)


{
NxShape** shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();
while (nShapes--)
{
DrawShape(shapes[nShapes]);
}
nShapes = actor->getNbShapes();
if (gSelectedActor && actor == gSelectedActor)
{
while (nShapes--)
{
if (shapes[nShapes]->getFlag(NX_TRIGGER_ENABLE))
DrawWireShape(shapes[nShapes], NxVec3(0,0,1));
else if (shapes[nShapes]->userData == (void*)-1)
DrawWireShape(shapes[nShapes], NxVec3(0.5,0.5,1));
else
DrawWireShape(shapes[nShapes], NxVec3(1,1,1));
shapes[nShapes]->userData = (void*)0;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
}
}
}

When we draw our focus actor, gSelectedActor, if its shapes' user data is (void*)-1, we
draw them in purple, instead of white.

4 The Complete List of Intersection Tests


Hit “v” to toggle through all the RenderIntersectedEntities() cases. There are nine of
them.

1. SPHERE_SHAPE_TEST
2. AABB_SHAPE_TEST
3. PLANES_SHAPE_TEST
4. SPHERE_SHAPE_CHECK
5. AABB_SHAPE_CHECK
6. AABB_TRIANGLE_TEST
7. OBB_TRIANGLE_TEST
8. CAPSULE_TRIANGLE_TEST
9. SPHERE_TRIANGLE_TEST

Each corresponds to one of the following functions in order.

1. NxScene::overlapSphereShapes() - test for shapes in the scene intersecting a sphere


2. NxScene::overlapAABBShapes() - test for shapes in the scene intersecting an AABB
3. NxScene::cullShapes() - test for shapes in the scene culled inside a set of planes (you
can use this for view frustum culling)

4. NxScene::checkOverlapSphere() - test to see if the sphere intersects any shapes in the


scene
5. NxScene::checkOverlapAABB() - test to see if the AABB intersects any shapes in the
scene

We also have triangle mesh shape intersection test functions. This is what you want in
your case, to test for intersection with terrain or level geometry triangles. You call the
intersection test functions directly from your triangle mesh shape. These functions are in
NxTriangleMeshShape.h.

6. NxTriangleMeshShape::overlapAABBTriangles() - test for triangles on the mesh


intersecting an AABB
7. NxTriangleMeshShape::overlapOBBTriangles() - test for triangles on the mesh
intersecting an OBB (oriented bounding box)
8. NxTriangleMeshShape::overlapCapsuleTriangles() - test for triangles on the mesh
intersecting a capsule

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


7
9. NxTriangleMeshShape::overlapSphereTriangles() - test for triangles on the mesh
intersecting a sphere

You set up all of these functions just like we set up the


NxScene::overlappSphereShapes() function above. You just need to create an
appropriate entity array and entity report to pass in to the intersection function to receive
the appropriate data.

5 Sphere-Shape Test
This is NxScene:: overlapSphereShapes(). We have already seen this. It is the first test
in RenderIntersectedEntities().

case SPHERE_SHAPE_TEST:
{
NxSphere worldSphere(NxVec3(0,0,0), 5);
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 nbShapes = gScene->getNbDynamicShapes(); // + gScene-
>getNbStaticShapes()
NxShape** shapes = (NxShape**)NxAlloca(nbShapes*sizeof(NxShape*));
for (NxU32 i = 0; i < nbShapes; i++) shapes[i] = NULL;
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;
gScene->overlapSphereShapes(worldSphere, shapeType, nbShapes, shapes,
&gShapeReport, activeGroups, groupsMask);
DrawWireSphere(&worldSphere, NxVec3(1,0,0));
while (nbShapes--)
{
if (shapes[nbShapes])
{
DrawWireShape(shapes[nbShapes], NxVec3(0,0,0));
}
}
}
break;

We use NxScene::overlapSphereShapes() to get all shapes intersected by worldSphere, a


sphere centered at the origin with radius 5 meters. We pass in our shapes[] array to
retrieve the intersected shapes and our gShapeReport to mark each of them as passing the
sphere intersection test.

6 AABB-Shape Test
This is NxScene::overlapAABBShapes(). This is the second test in
RenderIntersectedEntities().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


8
case AABB_SHAPE_TEST:
{
NxBounds3 worldBounds;
worldBounds.set(NxVec3(-5,-5,-5), NxVec3(5,5,5));
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 nbShapes = gScene->getNbDynamicShapes(); // + gScene-
>getNbStaticShapes()
NxShape** shapes = (NxShape**)NxAlloca(nbShapes*sizeof(NxShape*));
for (NxU32 i = 0; i < nbShapes; i++) shapes[i] = NULL;
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;
gScene->overlapAABBShapes(worldBounds, shapeType, nbShapes, shapes,
&gShapeReport, activeGroups, groupsMask);
NxBox box;
NxMat34 mat;
NxCreateBox(box, worldBounds, mat);
DrawWireBox(&box, NxVec3(1,0,0));
while (nbShapes--)
{
if (shapes[nbShapes])
{
DrawWireShape(shapes[nbShapes], NxVec3(0,0,0));
}
}
}
break;

We use NxScene::overlapAABBShapes() to get all shapes intersected by worldBounds,


an AABB centered at the origin 10 meters on a side. We pass in our (shapes*)[] array to
retrieve the intersected shapes and our gShapeReport to mark each of them as passing the
AABB intersection test.

7 Planes-Shape Test
The third intersection test function in RenderIntersectedEntities() is
NxScene::cullShapes().

case PLANES_SHAPE_TEST:
{
NxU32 nbPlanes = 2;
NxPlane worldPlanes[2];
worldPlanes[0].set(NxVec3(-2,0,2), NxVec3(0,0,1));
worldPlanes[1].set(NxVec3(-2,0,2), NxVec3(1,0,0));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


9
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 nbShapes = gScene->getNbDynamicShapes(); // + gScene-
>getNbStaticShapes()
NxShape** shapes = (NxShape**)NxAlloca(nbShapes*sizeof(NxShape*));
for (NxU32 i = 0; i < nbShapes; i++) shapes[i] = NULL;
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;
gScene->cullShapes(nbPlanes, worldPlanes, shapeType, nbShapes, shapes,
&gShapeReport, activeGroups, groupsMask);
// NxBox box;
// NxMat34 mat;
// NxCreateBox(box, worldBounds, mat);
// DrawWireBox(&box, NxVec3(1,0,0));
while (nbShapes--)
{
if (shapes[nbShapes])
{
DrawWireShape(shapes[nbShapes], NxVec3(0,0,0));
}
}
}
break;

We use NxScene::cullShapes() to get all shapes culled away by worldPlanes[0] and


worldPlanes[1], i.e., all shapes on side opposite the normal of the each plane.
worldPlanes[0] has center (-2,0,2) and normal (0,0,1), so it passes through (-2,0,2), or a
bit away from the screen and to the right of the origin, and has its normal facing down the
z-axis, or away from the screen at startup. worldPlanes[1] has center (-2,0,2) and normal
(1,0,0), so it passes through (-2,0,2) and has its normal facing down the x-axis, or to the
left at startup. We pass in our (shapes*)[] array to retrieve the culled shapes and our
gShapeReport to mark each of them as being culled away by the planes.

In the demo, you'll notice the capsule gets culled away by the planes. It is on the side
opposite the normals of both planes. If you change this code to this:

NxU32 nbPlanes = 1;
NxPlane worldPlanes[1];
worldPlanes[0].set(NxVec3(-2,0,2), NxVec3(0,0,1));

...you'll see the box, the pyramid, and the capsule are culled away by the single plane
behind these objects and facing away from the screen. Adding the next plane

NxU32 nbPlanes = 2;
NxPlane worldPlanes[2];
worldPlanes[0].set(NxVec3(-2,0,2), NxVec3(0,0,1));
worldPlanes[1].set(NxVec3(-2,0,2), NxVec3(1,0,0));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


10
...doesn't cull away the box and the pyramid as they need to be culled away by both
planes. Only the capsule remains culled away by both planes.

Try moving your objects around to get an idea of the area culled away by the planes.

8 Sphere-Shape Check
Next is the sphere-shape check, called with NxScene::checkOverlapSphere().

case SPHERE_SHAPE_CHECK:
{
NxSphere worldSphere(NxVec3(0,0,0), 5);
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;
bool intersection = gScene->checkOverlapSphere(worldSphere, shapeType,
activeGroups, groupsMask);
if (intersection)
DrawWireSphere(&worldSphere, NxVec3(1,0,0));
else
DrawWireSphere(&worldSphere, NxVec3(0,1,0));
}
break;

NxScene::checkOverlapSphere() returns true if any shape of the types its looking for
intersects the sphere.

Hit “v” to get to the sphere-shape check in the demo. Move all the dynamic objects out
of the red sphere. At this point, the sphere will turn green, indicating there are no
dynamic shapes intersecting it.

9 AABB-Shape Check
Next is the AABB-shape check, called with NxScene::checkOverlapSphere().

case AABB_SHAPE_CHECK:
{
NxBounds3 worldBounds;
worldBounds.set(NxVec3(-5,-5,-5), NxVec3(5,5,5));
NxShapesType shapeType = NX_DYNAMIC_SHAPES; //
NX_STATIC_SHAPES, NX_ALL_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


11
gScene->checkOverlapAABB(worldBounds, shapeType, activeGroups,
groupsMask);
NxBox box;
NxMat34 mat;
NxCreateBox(box, worldBounds, mat);
bool intersection = gScene->checkOverlapAABB(worldBounds, shapeType,
activeGroups, groupsMask);
if (intersection)
DrawWireBox(&box, NxVec3(1,0,0));
else
DrawWireBox(&box, NxVec3(0,1,0));
}
break;

Hit “v” to get to the AABB-shape check in the demo. Move all the dynamic objects out
of the red AABB. At this point, the AABB will turn green, indicating there are no
dynamic shapes intersecting it.

10 AABB-Triangle Test
We now move on to the triangle tests. The first test is the AABB-triangle test, called by
NxTriangleMeshShape::overlapAABBTriangle(). We have created a small, flat
heightfield to the right of the origin on which to conduct these tests.

// Actor globals
...
NxActor* heightfield = NULL;
...
NxActor* CreateFlatHeightfield(const NxVec3& pos, const NxU32 length, const NxU32
width, const NxReal stride)
{
NxVec3* hfVerts = NULL;
NxU32* hfFaces = NULL;
NxVec3* hfNormals = NULL;

// Initialize heightfield verts


NxU32 nbVerts = (length+1)*(width+1);
hfVerts = new NxVec3[nbVerts];

// Build flat heightfield


for (NxU32 i = 0; i < width + 1; i++)
{
for (NxU32 j = 0; j < length + 1; j++)
{
hfVerts[i+j*(width+1)] = NxVec3(stride*NxReal(i),0.5,stride*NxReal(j));
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


12
}

// Initialize heightfield faces


NxU32 nbFaces = length*width*2;
hfFaces = new NxU32[nbFaces*3];

NxU32 k = 0;
for (NxU32 i = 0; i < width; i++)
{
for (NxU32 j = 0; j < length; j++)
{
// Create first triangle
hfFaces[k] = i + j*(width+1);
hfFaces[k+1] = i + (j+1)*(width+1);
hfFaces[k+2] = i+1 + (j+1)*(width+1);
k+=3;

// Create second triangle


hfFaces[k] = i + j*(width+1);
hfFaces[k+1] = i+1 + (j+1)*(width+1);
hfFaces[k+2] = i+1 + j*(width+1);
k+=3;
}
}

// allocate heightfield materials -- one for each face


// hfMaterials = new NxMaterialIndex[nbFaces];

// Build vertex normals


hfNormals = new NxVec3[nbFaces];
// NxBuildSmoothNormals(nbFaces, nbVerts, hfVerts, hfFaces, NULL, hfNormals,
true);

NxTriangleMeshDesc hfDesc;
hfDesc.numVertices = nbVerts;
hfDesc.numTriangles = nbFaces;
hfDesc.pointStrideBytes = sizeof(NxVec3);
hfDesc.triangleStrideBytes = 3*sizeof(NxU32);
hfDesc.points = hfVerts;
hfDesc.triangles = hfFaces;

hfDesc.flags = 0;

// Add the mesh material data


// hfDesc.materialIndexStride = sizeof(NxMaterialIndex);
// hfDesc.materialIndices = hfMaterials;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


13
hfDesc.heightFieldVerticalAxis = NX_Y;
hfDesc.heightFieldVerticalExtent = 0;

NxTriangleMeshShapeDesc hfShapeDesc;

NxInitCooking();
if (0)
{
// Cooking from file
bool status = NxCookTriangleMesh(hfDesc, UserStream("c:\\tmp.bin", false));
hfShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(UserStream("c:\\tmp.bin", true));
}
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookTriangleMesh(hfDesc, buf);
hfShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(MemoryReadBuffer(buf.data));
}

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&hfShapeDesc);
actorDesc.globalPose.t = pos;
return gScene->createActor(actorDesc);
}
...
void InitNx()
{
...
heightfield = CreateFlatHeightfield(NxVec3(-20,0,-5), 5, 5, 2);
...
}

In RenderIntersectedEntities(), we get the triangle mesh shape from the “heightfield”


actor. From the triangle mesh shape, we
NxTriangleMeshShape::overlapAABBTriangle() to get the triangles on the mesh
intersected by the axis-aligned bounding box we pass in.

void RenderIntersectedEntities()
{
switch (gIntersectionTestType)
{
case AABB_TRIANGLE_TEST:
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


14
NxBounds3 worldBounds;
worldBounds.set(NxVec3(-13,-2,-2), NxVec3(-17,2,2));

NxShapesType shapeType = NX_ALL_SHAPES; // NX_DYNAMIC_SHAPES,


NX_STATIC_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;

NxShape** shapes = heightfield->getShapes();


NxTriangleMeshShape* tmShape = shapes[0]->isTriangleMesh();

if (!tmShape) break;

NxU32 nbTriangles;
const NxU32* triangles;

// Collide AABB against mesh shape


if (!tmShape->overlapAABBTriangles((const NxBounds3&)worldBounds,
NX_QUERY_WORLD_SPACE, nbTriangles, triangles))
break;

// Loop through triangles


while (nbTriangles--)
{
NxU32 index = *triangles++;

NxTriangle currentTriangle;

NxTriangle edgeTri;
NxU32 edgeFlags;
tmShape->getTriangle(currentTriangle, &edgeTri, &edgeFlags, index);

NxMat34 shapePose = tmShape->getGlobalPose();

NxVec3 pos[3];

pos[0] = currentTriangle.verts[0];
pos[1] = currentTriangle.verts[1];
pos[2] = currentTriangle.verts[2];

DrawLine(pos[0], pos[1], NxVec3(0,1,0));


DrawLine(pos[1], pos[2], NxVec3(0,1,0));
DrawLine(pos[2], pos[0], NxVec3(0,1,0));
}

NxBox box;
NxMat34 mat;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


15
NxCreateBox(box, worldBounds, mat);
DrawWireBox(&box, NxVec3(1,0,0));
}
break;
...
}
}

The bounding box we pass in lies at the center of the heightfield.

NxBounds3 worldBounds;
worldBounds.set(NxVec3(-13,-2,-2), NxVec3(-17,2,2));

The heightfield is a static shape. We can pass in either NX_STATIC_SHAPES or


NX_ALL_SHAPES as our shape type. We pass in NX_ALL_SHAPES.

NxShapesType shapeType = NX_ALL_SHAPES; // NX_DYNAMIC_SHAPES,


NX_STATIC_SHAPES

The activeGroups and groupsMask parameters are set to check for all groups.

NxU32 activeGroups = 0xffffffff;


NxGroupsMask* groupsMask = NULL;

We get the triangle mesh shape from the heightfield actor.

NxShape** shapes = heightfield->getShapes();


NxTriangleMeshShape* tmShape = shapes[0]->isTriangleMesh();

if (!tmShape) break;

...then we call NxTriangleMeshShape::overlapAABBTriangles() from tmShape to collide


our AABB against the heightfield shape.

NxU32 nbTriangles;
const NxU32* triangles;

// Collide AABB against mesh shape


if (!tmShape->overlapAABBTriangles((const NxBounds3&)worldBounds,
NX_QUERY_WORLD_SPACE, nbTriangles, triangles))
break;

This call returns “nbTriangles”, the number of triangles intersected, and “triangles”, the
array of triangle indices intersected by our test.

Note the NX_QUERY_WORLD_SPACE flag we pass in to our test function. This is a


type of NxQueryFlags, which says we are doing our test in world space. The

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


16
worldBounds we pass in, ((-13,-2,-2), (-17,2,2)), is in world coordinates. If this flag were
not set, you would need to pass in ((-13,-2,-2), (-17,2,2)) - (-20,0,-5) = ((7,-2,3),(3,2,7)), a
local coordinates AABB, to get the same results.

We loop through the list of triangles retrieved by the test and draw them.

// Loop through triangles


while (nbTriangles--)
{
NxU32 index = *triangles++;

NxTriangle currentTriangle;

NxTriangle edgeTri;
NxU32 edgeFlags;
tmShape->getTriangle(currentTriangle, &edgeTri, &edgeFlags, index);

NxMat34 shapePose = tmShape->getGlobalPose();

NxVec3 pos[3];

pos[0] = currentTriangle.verts[0];
pos[1] = currentTriangle.verts[1];
pos[2] = currentTriangle.verts[2];

DrawLine(pos[0], pos[1], NxVec3(0,1,0));


DrawLine(pos[1], pos[2], NxVec3(0,1,0));
DrawLine(pos[2], pos[0], NxVec3(0,1,0));
}

Hit “v” to get to the AABB-triangle intersection test. Note that our bounding box
intersects a small square of triangles, highlighted in green, at the center of the heightfield.

11 OBB-Triangle Test
The next test is the OBB-triangle test, called by
NxTriangleMeshShape::overlapOBBTriangles().

case OBB_TRIANGLE_TEST:
{
NxQuat q;
q.fromAngleAxis(45, NxVec3(0,1,0));
NxMat33 orient;
orient.fromQuat(q);
NxBox worldOBB(NxVec3(-15,0,0),NxVec3(2,2,2),orient);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


17
NxShapesType shapeType = NX_ALL_SHAPES; // NX_DYNAMIC_SHAPES,
NX_STATIC_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;

NxShape** shapes = heightfield->getShapes();


NxTriangleMeshShape* tmShape = shapes[0]->isTriangleMesh();

if (!tmShape) break;

NxU32 nbTriangles;
const NxU32* triangles;

// Collide OBB against mesh shape


if (!tmShape->overlapOBBTriangles((const NxBox&)worldOBB,
NX_QUERY_WORLD_SPACE, nbTriangles, triangles))
break;

// Loop through triangles


while (nbTriangles--)
{
NxU32 index = *triangles++;

NxTriangle currentTriangle;

NxTriangle edgeTri;
NxU32 edgeFlags;
tmShape->getTriangle(currentTriangle, &edgeTri, &edgeFlags, index);

NxMat34 shapePose = tmShape->getGlobalPose();

NxVec3 pos[3];

pos[0] = currentTriangle.verts[0];
pos[1] = currentTriangle.verts[1];
pos[2] = currentTriangle.verts[2];

DrawLine(pos[0], pos[1], NxVec3(0,1,0));


DrawLine(pos[1], pos[2], NxVec3(0,1,0));
DrawLine(pos[2], pos[0], NxVec3(0,1,0));
}

DrawWireBox(&worldOBB, NxVec3(1,0,0));
}
break;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


18
Our oriented bounding box is centered at (-15,0,0) with extents (2,2,2) and then rotated
by 45 degrees about the y-axis. It is basically the AABB from the previous case rotated
by 45 degrees about the y-axis.

12 Capsule-Triangle Test
Next is the capsule-triangle test, called by
NxTriangleMeshShape::overlapCapsuleTriangles().

case CAPSULE_TRIANGLE_TEST:
{
NxSegment segment(NxVec3(-15,0.5,-2),NxVec3(-15,0.5,2));
NxCapsule worldCapsule(segment, 1);

NxShapesType shapeType = NX_ALL_SHAPES; // NX_DYNAMIC_SHAPES,


NX_STATIC_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;

NxShape** shapes = heightfield->getShapes();


NxTriangleMeshShape* tmShape = shapes[0]->isTriangleMesh();

if (!tmShape) break;

NxU32 nbTriangles;
const NxU32* triangles;

// Collide capsule against mesh shape


if (!tmShape->overlapCapsuleTriangles((const NxCapsule&)worldCapsule,
NX_QUERY_WORLD_SPACE, nbTriangles, triangles))
break;

// Loop through triangles


while (nbTriangles--)
{
NxU32 index = *triangles++;

NxTriangle currentTriangle;

NxTriangle edgeTri;
NxU32 edgeFlags;
tmShape->getTriangle(currentTriangle, &edgeTri, &edgeFlags, index);

NxMat34 shapePose = tmShape->getGlobalPose();

NxVec3 pos[3];

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


19
pos[0] = currentTriangle.verts[0];
pos[1] = currentTriangle.verts[1];
pos[2] = currentTriangle.verts[2];

DrawLine(pos[0], pos[1], NxVec3(0,1,0));


DrawLine(pos[1], pos[2], NxVec3(0,1,0));
DrawLine(pos[2], pos[0], NxVec3(0,1,0));
}

DrawWireCapsule(&worldCapsule, NxVec3(1,0,0));
}
break;

Our capsules midsection is the line segment running from (-15,0.5,-2) to (-15,0.5,2). The
radius of the ends of the capsule is 1. This gives us a capsule lying down in the middle of
our mesh oriented along the z-axis.

13 Sphere-Triangle Test
The next test is the sphere-triangle test, called by
NxTriangleMeshShape::overlapSphereTriangles().

case SPHERE_TRIANGLE_TEST:
{
NxSphere worldSphere(NxVec3(-15,0,0), 2);

NxShapesType shapeType = NX_ALL_SHAPES; // NX_DYNAMIC_SHAPES,


NX_STATIC_SHAPES
NxU32 activeGroups = 0xffffffff;
NxGroupsMask* groupsMask = NULL;

NxShape** shapes = heightfield->getShapes();


NxTriangleMeshShape* tmShape = shapes[0]->isTriangleMesh();

if (!tmShape) break;

NxU32 nbTriangles;
const NxU32* triangles;

// Collide sphere against mesh shape


if (!tmShape->overlapSphereTriangles((const NxSphere&)worldSphere,
NX_QUERY_WORLD_SPACE, nbTriangles, triangles))
break;

// Loop through triangles

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


20
while (nbTriangles--)
{
NxU32 index = *triangles++;

NxTriangle currentTriangle;

NxTriangle edgeTri;
NxU32 edgeFlags;
tmShape->getTriangle(currentTriangle, &edgeTri, &edgeFlags, index);

NxMat34 shapePose = tmShape->getGlobalPose();

NxVec3 pos[3];

pos[0] = currentTriangle.verts[0];
pos[1] = currentTriangle.verts[1];
pos[2] = currentTriangle.verts[2];

DrawLine(pos[0], pos[1], NxVec3(0,1,0));


DrawLine(pos[1], pos[2], NxVec3(0,1,0));
DrawLine(pos[2], pos[0], NxVec3(0,1,0));
}

DrawWireSphere(&worldSphere, NxVec3(1,0,0));
}
break;

Our sphere is centered at (-15,0,0) with radius 2.

14 Conclusion
To review, we have the following intersection tests:

1. Shape Intersection Tests

AABB vs. world shapes


sphere vs. world shapes
planes vs. world shapes (culling)

2. Triangle Intersection Tests

AABB vs. triangle mesh


OBB vs. triangle mesh
capsule vs. triangle mesh
sphere vs. triangle mesh

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


21
These tests are useful for performing our own collision detection with the scene.

We demonstrate the usefulness of these functions when we build our character controller
in “Lesson 505: Character Controller”. We'll place the character controller kinematic
actor by getting its new position with multiple calls, in software, to
NxScene::overlapAABBShapes() and NxTriangleMeshShape::overlapAABBTriangles().
We didn't want to hardware-accelerate this particular codepath, which is why the
character controller is done this way. This also allows you to be able to modify the
character controller to suit your needs as you'll have access to this source code.

15 Related Classes, Functions, and Parameters


NxScene
overlapSphereShapes()
overlapAABBShapes()
cullShapes()
checkOverlapSphere()
checkOverlapAABB()

NxTriangleMeshShape
overlapAABBTriangles()
overlapOBBTriangles()
overlapCapsuleTriangles()
overlapSphereTriangles()
getTriangle()

NxUserEntityReport<T*>
onEvent()

NxShapesType
NX_DYNAMIC_SHAPES
NX_STATIC_SHAPES
NX_ALL_SHAPES

NxQueryFlags
NX_QUERY_WORLD_SPACE
NX_QUERY_FIRST_CONTACT

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


22
Lesson 307 – Notify

1 Introduction
The NxUserNotify allows you to receive onWake(), onSleep() and onJointBreak() events
from any joints.

2 User Notify
In Lesson307.cpp, we define a class called MyJointNotify.

class MyJointNotify : public NxUserNotify


{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
public:
virtual void onWake(NxActor** actors, NxU32 count)
{
hud.SetDisplayString(3, "actor is awake", 0.05f, 0.88f);
}
virtual void onSleep(NxActor** actors, NxU32 count)
{
hud.SetDisplayString(3, "actor is sleeping", 0.05f, 0.88f);
}

virtual bool onJointBreak(NxReal breakingImpulse, NxJoint& brokenJoint)


{
hud.SetDisplayString(2, "Joint broken!", 0.05f, 0.92f);
return true;
}
}

There are three virtual callback functions. The function onWake() will be called the
actors which have just been woken up. The function onSleep() will be called the actors
which have just been put to sleep. The function onJointBreak() will be called when a
breakable joint breaks.

This class gets instantiated in Lesson307.cpp. Then pass it into the NxScene.

MyJointNotify gJointNotify;
......
void InitNx()
{
……
gScene->setUserNotify(&gJointNotify);
……
}

3 Set Breakable
You can set the breakable property after create a joint.

void SpecialKeys(unsigned char key, int x, int y)


{
……
NxReal maxForce = 100.0f;
NxReal maxTorque = 50.0f;
if (0 != pD6Joint)
pD6Joint->setBreakable(maxForce, maxTorque);
……
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
4 Conclusion
The User Notify is handy when you want to receive some information from joint.

5 Related Classes, Functions, and Parameters


NxUserNotify
onWake()
onSleep()
onJointBreak()

NxJoint
setBreakable()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
Lesson 308 – Output Stream

1 Introduction
The Output Stream allows you to use a custom defined stream class for error logging.

2 Output Stream
In errorstream.h, we define a class called ErrorStream.

class ErrorStream : public NxUserOutputStream


{
public:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
void reportError(NxErrorCode e, const char* message, const char* file, int line)
{
printf("%s (%d) :", file, line);
……
printf(" : %s\n", message);
}

NxAssertResponse reportAssertViolation(const char* message, const char* file,


int line)
{
printf("access violation : %s (%s line %d)\n", message, file, line);
……
}

void print(const char* message)


{
printf(message);
}
};

There are three virtual callback functions. The function reportError() will be called to
report an error code. The function reportAssertViolation() will be called to report an
assertion violation. The function print() will be called to simply prints some debug text.

This class gets instantiated and pass it while create PhysX SDK.

ErrorStream gErrorStream;
......
void InitNx()
{
……
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
gAllocator, & gErrorStream);
……
}

3 Others
Note: No method from the SDK should be called from within the error reporting
functions.
Threading: Errors are only reported in the context of the user thread, so it is not necessary
to provide a thread safe error reporting callback.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
4 Conclusion
The Physics SDK provides the possibility to supply a user defined stream class for error
logging.

5 Related Classes, Functions, and Parameters


NxUserOutputStream
reportError()
reportAssertViolation()
print()

NxCreatePhysicsSDK()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
Lesson 309 – Scheduler

1 Introduction
The custom scheduler allows you to complete control of the way work items are
executed.

2 Scheduler
In CustomScheduler.h, we define a class called CustomScheduler.

class CustomScheduler : public NxUserScheduler


{
public:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
CustomScheduler();
~CustomScheduler();

void CreateThreads(const int count);


void KillThreads();

virtual void addTask(NxTask *task);


virtual void addBackgroundTask(NxTask *task);
virtual void waitTasksComplete();

private:
bool executeTask();
DWORD threadFunc();
static DWORD __stdcall threadFuncStatic(LPVOID userParam);

private:
CRITICAL_SECTION taskQueueCS;
std::deque<NxTask *> taskQueue;

int threadCount;
HANDLE *threadHandles;
HANDLE quitEvent;
HANDLE pendingEvent;

volatile long workingCount;


};

There are three virtual callback functions. The SDK calls addTask() method to add a task
to the applications work queue, calls addBackgroundTask() method to add a background
task to the applications work queue. And the SDK thread calls waitTasksComplete()
function to suspend.

This class gets instantiated in Lesson309.cpp. Then pass it into the NxSceneDesc.

static CustomScheduler gCustomScheduler;


......
void InitNx()
{
……
// Create threads
gCustomScheduler.CreateThreads(2);

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD;
sceneDesc.customScheduler = &gCustomScheduler;
gScene = gPhysicsSDK->createScene(sceneDesc);
……
}

3 Others
To enable custom scheduler, the user must specify the
NX_SF_ENABLE_MULTITHREAD flag when creating the scene.

When specifying a user scheduler, the internalThreadCount and backgroundThreadCount


members of NxSceneDesc should be set to zero and the
pollForWork()/resetPollForWork() functions of NxScene should not be called.

4 Conclusion
The custom scheduler allows you to complete control of the way work items are
executed.

5 Related Classes, Functions, and Parameters


NxUserScheduler
addTask()
addBackgroundTask()
waitTasksComplete()

NxSceneDesc
flags
customScheduler
internalThreadCount
backgroundThreadCount

NxSceneFlags
NX_SF_ENABLE_MULTITHREAD

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
Lesson 401 – Rope

1 Introduction
From the first two chapters, you have learned all about rigid bodies and joints, the
fundamentals of the SDK. You will now use what you have learned to create large scale
physics effects.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
In this lesson, we will create a rope out of rigid bodies connected together by spherical
joints. The rope we create is an interactive effect and can be used to make rope, chains,
firehoses, etc.

In these lessons we concentrate on “effects physics”: physics for the sake of visible
effect. We will be creating highly interactive objects using lots of rigid bodies and joints:
cloth, ragdolls, interlocking gears, gears driving a belt, a tank, and blades of grass. Most
of these are taken straight from PhysX Rocket, and you can see how the PSCL scripts
translate fairly easily to C++ code.

2 Rope
We call a function CreateRope() in InitNx().

// Actor globals
NxActor* groundPlane = NULL;
NxActor** ropeSegs = NULL;

// Joint globals
NxSphericalJoint** ropeLinks = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

void CreateRope(const NxVec3& pos, const NxReal segHeight, const NxReal segRadius,
const int nbSegments, const NxReal density)
{
int i;

ropeSegs = new NxActor*[NbSegments];


ropeLinks = new NxSphericalJoint*[NbSegments-1];

for (i = 0; i < nbSegments; i++)


{
ropeSegs[i] = CreateCapsule(pos + NxVec3(0,i*(2*segRadius+segHeight),0),
segHeight, segRadius, density);
}

for (i = 0; i < nbSegments - 1; i++)


{
ropeLinks[i] = CreateRopeSphericalJoint(ropeSegs[i], ropeSegs[i+1], pos +
NxVec3(0,(i+1)*(2*segRadius+segHeight),0), NxVec3(0,1,0));
}
}

void InitNx()
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
{
..
// Create the objects in the scene
groundPlane = CreateGroundPlane();

CreateRope(NxVec3(0,0,0), 2, 0.5, 10, 10);


// CreateRope(NxVec3(0,0,0), 1, 0.5, 80, 10);
// CreateRope(NxVec3(0,0,0), 0.6, 0.4, 160, 10);

gSelectedActor = ropeSegs[0];

}

CreateRope() takes the ropeSegs capsule-shape actors and links them together with the
ropeLinks spherical joints. Run the lesson and try pulling the rope around with “umijkl”.
Take a look at CreateSphericalJoint() in Joints.cpp.

NxSphericalJoint* CreateRopeSphericalJoint(NxActor* a0, NxActor* a1, const


NxVec3& globalAnchor, const NxVec3& globalAxis)
{
NxSphericalJointDesc sphericalDesc;
sphericalDesc.actor[0] = a0;
sphericalDesc.actor[1] = a1;
sphericalDesc.setGlobalAnchor(globalAnchor);
sphericalDesc.setGlobalAxis(globalAxis);

sphericalDesc.flags |= NX_SJF_TWIST_LIMIT_ENABLED;
sphericalDesc.twistLimit.low.value = -(NxReal)0.1*NxPi;
sphericalDesc.twistLimit.high.value = (NxReal)0.1*NxPi;

sphericalDesc.flags |= NX_SJF_TWIST_SPRING_ENABLED;
NxSpringDesc ts;
ts.spring = 500;
ts.damper = 0.5;
ts.targetValue = 0;
sphericalDesc.twistSpring = ts;

sphericalDesc.flags |= NX_SJF_SWING_LIMIT_ENABLED;
sphericalDesc.swingLimit.value = (NxReal)0.3*NxPi;

sphericalDesc.flags |= NX_SJF_SWING_SPRING_ENABLED;
NxSpringDesc ss;
ss.spring = 500;
ss.damper = 0.5;
ss.targetValue = 0;
sphericalDesc.swingSpring = ss;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
return (NxSphericalJoint*)gScene->createJoint(sphericalDesc);
}

Hit “b” to go into Debug Mode. Notice we have given the joints fairly generous swing
limits (-0.3*NxPi to 0.3*NxPi) with tighter twist limits (-0.1*NxPi to 0.1*NxPi). The
segments can bend fairly easily with respect to each other, but twisting one will soon
cause the others next to it to twist as well.

The swing and twist of the joints have springs on them so the rope tends to want to
straighten out. Enough gravity and friction keeps it twisted.

3 Upping the Resolution


The rope is at a resolution where it looks like a bunch of sausage links. Comment out the
first CreateRope() and uncomment the second.

CreateRope(NxVec3(0,0,0), 1, 0.5, 80, 10);

Note how this already looks a lot more like real rope. Comment out the second line and
uncomment the third.

CreateRope(NxVec3(0,0,0), 0.6, 0.4, 160, 10);

With 160 segments, the rope now looks and feels much better than the first two. In
creating your effects physics, you will need to play around with the resolution of your
physics actors to achieve the results you want at the desired speed.

4 Conclusion
You have created your first large-scale physics effect. Congratulations once again! We
will be creating several of these interactive effects in the coming lessons.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 402 – Jointed Rigid Body Cloth

1 Introduction
We create a large, mattress-like cloth by connecting a grid of rigid bodies together with
spherical joints.

2 Jointed Rigid Body Cloth


We call CreateMattress() in InitNx().

// Actor globals

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
extern NxActor* groundPlane;
NxActor** patches = NULL;

class PatchLink
{
public:
NxSphericalJoint* xJoint;
NxSphericalJoint* zJoint;
};

PatchLink** patchLinks = NULL;



void InitNx()
{

CreateMattress(NxVec3(0,30,0), 16, 16, NxVec3(1,0.2,1));

}

The mattress is made by taking a 16-by-16 grid of box shape actors and connecting them
together with spherical joints.

Run the lesson and hit “b” to go to Debug Mode. Notice how the actors are connected
together by the grid of joints. The joints are given a wide swing and just a little twist.

3 Cloaks and Capes


Cloth like this can be used to create cloaks and capes that you can attach to characters.
When attaching a cloth like this to a character, flagpole, or other object, you need to
remember not to move the cloth too quickly using NxActor::setGlobalPosition() or
NxActor::moveGlobalPosition(). This can cause the cloth to explode or behave
erratically.

Either creating the cloak in the starting position you want, or, if it’s out of position,
moving all the bodies of the cloak kinematically to the new position making sure they
keep within the constraints of their joints.

4 Conclusion
We have seen rope and cloth built from jointing rigid bodies together. Try playing
around with the joint parameters to create different types of objects.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Lesson 403 – Ragdolls

1 Introduction
Ragdolls are often used in games to simulate a character going limp or staggering back
after getting killed or injured. From the start, you want to build your character with the
joints and masses in place. You then turn off the physics effect on the character and
make it move under your animation. When you want the character to go ragdoll, turn off
the animation control, make the bodies in the ragdoll dynamic and turn on their joints.

2 Ragdolls
We use a Ragdoll class to create our ragdoll.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
class Ragdoll
{

NxActor* head;
NxActor* torso;
NxActor* pelvis;
NxActor* leftUpperArm;
NxActor* rightUpperArm;
NxActor* leftForeArm;
NxActor* rightForeArm;
NxActor* leftHand;
NxActor* rightHand;
NxActor* leftThigh;
NxActor* rightThigh;
NxActor* leftCalf;
NxActor* rightCalf;
NxActor* leftFoot;
NxActor* rightFoot;

NxSphericalJoint* neck;
NxSphericalJoint* leftShoulder;
NxSphericalJoint* rightShoulder;
NxSphericalJoint* spine;
NxSphericalJoint* leftHip;
NxSphericalJoint* rightHip;

NxRevoluteJoint* leftElbow;
NxRevoluteJoint* rightElbow;
NxRevoluteJoint* leftWrist;
NxRevoluteJoint* rightWrist;
NxRevoluteJoint* leftKnee;
NxRevoluteJoint* rightKnee;
NxRevoluteJoint* leftAnkle;
NxRevoluteJoint* rightAnkle;
};
...
Ragdoll* guy = NULL;

InitNx()
{
..
guy = new Ragdoll(NxVec3(0,0,0));

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
The ragdoll is made of 15 actors composing the body. The actors are connected by joints,
half spherical and half revolute. The spherical joints are used for the inner connections,
like the neck, shoulders, and hips, while the revolute joints are used for the outer
connections, like the wrists and ankles.

3 Ragdoll Joint Limits


Run the lesson and hit “b” to go to Debug Mode. Note the limits on the spherical joints
and how they tend to match the range of motion for a human. The revolute joints
currently have no limits in place, so our ragdoll is fairly limber. As an exercise, put in
limits on the revolute joints using your own body as a reference.

4 Conclusion
Ragdolls are probably the most mainstream use of jointed physics in games. In future
lessons, we will take an animated character and turn it into a ragdoll. We will also look
at how to drive an animation with physics using the 6 DOF joint.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 404 – Tank

1 Introduction
For the grand finale of the gears group of lessons, we create a tank.

2 Tank
We have a Tread class and Tank class we construct the tank from.

class Tread
{
NxActor* wheel[4];
NxActor* frame;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
NxActor* step[47];

NxRevoluteJoint* wheelJoint[4];
NxRevoluteJoint* stepJoint[47];
};

class Tank
{

Tread* tread[2];
NxActor* chassis;
NxActor* turret;
NxActor* cannon;
NxRevoluteJoint* chassisJoint [2];
NxRevoluteJoint* turretJoint;
NxRevoluteJoint* cannonJoint;
};

The tread is essentially the same as in the last lesson, only now it has 4 wheels. The tank
is created from two treads, a chassis, a turret, and one cannon. The chassis is connected
to the tread by two fixed joints, the turret is connected to the chassis by a revolute joint,
and the cannon is connected to the turret by another revolute joint.

3 Tank Motors
The tank runs on motors set to each tread. We initialize the descriptors for these motors
in InitMotors() called from InitNx().

// Motor globals
NxMotorDesc mNeutralDesc;
NxMotorDesc mForwardDesc;
NxMotorDesc mReverseDesc;
NxMotorDesc mBrakeDesc;

NxMotorDesc mLeftDesc;
NxMotorDesc mRightDesc;
NxMotorDesc mStopDesc;
...
void InitMotors()
{
// Neutral
mNeutralDesc.maxForce = 0;
mNeutralDesc.velTarget = 0;
mNeutralDesc.freeSpin = 1;

// Forward
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
mForwardDesc.maxForce = 750;
mForwardDesc.velTarget = 200;
mForwardDesc.freeSpin = 1;

// Reverse
mReverseDesc.maxForce = 750;
mReverseDesc.velTarget = -200;
mReverseDesc.freeSpin = 1;

// Brake
mBrakeDesc.maxForce = 500;
mBrakeDesc.velTarget = 0;
mBrakeDesc.freeSpin = 0;

// Left
mLeftDesc.maxForce = 100;
mLeftDesc.velTarget = -50;
mLeftDesc.freeSpin = 0;

// Right
mRightDesc.maxForce = 100;
mRightDesc.velTarget = 50;
mRightDesc.freeSpin = 0;

// Stop
mStopDesc.maxForce = 500;
mStopDesc.velTarget = 0;
mStopDesc.freeSpin = 0;
}

void InitNx()
{
...
InitMotors();
...
}

NxRevoluteJoint::setMotor() called from SetMotorOnTread() called from ProcessKeys(),


sets the four wheels on the tread to have one of three motor descriptors: forward, reverse,
or brake. It also sets the motor descriptor on the turret joint to either left or right.

void SetMotorOnTread(int i, const NxMotorDesc& mDesc)


{
tank->tread[i]->wheelJoint[0]->setMotor(mDesc);
tank->tread[i]->wheelJoint[1]->setMotor(mDesc);
tank->tread[i]->wheelJoint[2]->setMotor(mDesc);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
tank->tread[i]->wheelJoint[3]->setMotor(mDesc);
}

void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{
// Motor controls
case 'i': {SetMotorOnTread(0, mForwardDesc); SetMotorOnTread(1,
mForwardDesc); break; }
case 'k': {SetMotorOnTread(0, mReverseDesc);SetMotorOnTread(1,
mReverseDesc); break; }
case ',': {SetMotorOnTread(0, mBrakeDesc);SetMotorOnTread(1,
mBrakeDesc); break; }
case 'l': {SetMotorOnTread(0, mForwardDesc);SetMotorOnTread(1,
mBrakeDesc); break; }
case 'j': {SetMotorOnTread(0, mBrakeDesc);SetMotorOnTread(1,
mForwardDesc); break; }
case 'u': {SetMotorOnTread(0, mReverseDesc);SetMotorOnTread(1,
mForwardDesc); break; }
case 'm': {SetMotorOnTread(0, mForwardDesc);SetMotorOnTread(1,
mReverseDesc); break; }

case 'c': {tank->turretJoint->setMotor(mLeftDesc); break; }


case 'v': {tank->turretJoint->setMotor(mRightDesc); break; }
}
}
}

In the RenderCallback() function, we call SetMotorOnTread() to reset the tank treads to a


neutral motor and NxRevoluteJoint::setMotor() to reset the turret to a stopped motor.

Run the lesson. Use “i” and “k” for forward and reverse, “j” and “l” for left and right,
“u” and “m” for hard left and hard right and “,” to brake. Use “c” and “v” to rotate the
turret left and right.

4 Conclusion
This has been a lesson on constructing a tank using a modification of the gear and tread
from the last lesson.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
Lesson 405 - Grass

1 Introduction
This is a lesson on how to create grass. Much like rope, a blade of grass is a line of rigid
bodies linked together by spherical joints. Making the joint with a limit and a spring
helps define an object that tries to restore its original position.

2 Grass
We have a class Blade that we make our blades of grass from.

class Blade

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
{
public:
Blade(const NxVec3& pos, const NxReal height, const NxU32 numJoints)
{
NxReal partHeight = height / (NxReal)numJoints;
NxReal radius = 0.1;
NxVec3 globalAnchor;

bladeSegs = new NxActor*[numJoints];


bladeLinks = new NxSphericalJoint*[numJoints];

NxU32 i;
for (i = 0; i < numJoints; i++)
{
bladeSegs[i] = CreateBlade(pos+NxVec3(0, partHeight/2+radius+
(partHeight+radius*2)*I , 0), NxVec3(radius/2,(partHeight+2*radius)/2,radius/2),
numJoints-i+1);
bladeSegs[i]->setLinearDamping(10);
SetActorGroup(bladeSegs[i],1);
}

// Anchor blade to ground


globalAnchor = bladeSegs[0]->getCMassGlobalPosition() - NxVec3(0,
partHeight/2, 0);
bladeLinks[0] = CreateBladeLink(bladeSegs[0],NULL,globalAnchor,
NxVec3(0,1,0));

for (i = 1; i < numJoints; i++)


{
globalAnchor = bladeSegs[i]->getCMassGlobalPosition() -
NxVec3(0,partHeight/2,0);
bladeLinks[i] = CreateBladeLink(bladeSegs[i], bladeSegs[i-1], globalAnchor,
NxVec3(0,1,0));
}
}

NxActor** bladeSegs;
NxSphericalJoint** bladeLinks;
};

The segments of the blade are box shapes and the links are spherical joints created in
CreateBladeLink() in Joints.cpp.

NxSphericalJoint* CreateBladeLink(NxActor* a0, NxActor* a1, const NxVec3&


globalAnchor, const NxVec3& globalAxis)
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
NxSphericalJointDesc sphericalDesc;
sphericalDesc.actor[0] = a0;
sphericalDesc.actor[1] = a1;
sphericalDesc.setGlobalAnchor(globalAnchor);
sphericalDesc.setGlobalAxis(globalAxis);

sphericalDesc.flags |= NX_SJF_SWING_LIMIT_ENABLED;
sphericalDesc.swingLimit.value = (NxReal)0.05*NxPi;
sphericalDesc.swingLimit.restitution = 0.75;
sphericalDesc.swingLimit.hardness = 0.5;

sphericalDesc.flags |= NX_SJF_SWING_SPRING_ENABLED;
sphericalDesc.swingSpring.spring = 0.75;
sphericalDesc.swingSpring.damper = 1;

sphericalDesc.flags |= NX_SJF_TWIST_LIMIT_ENABLED;
sphericalDesc.twistLimit.low.value = -(NxReal)0.05*NxPi;
sphericalDesc.twistLimit.low.restitution = 0.75;
sphericalDesc.twistLimit.low.hardness = 0.5;
sphericalDesc.twistLimit.high.value = (NxReal)0.05*NxPi;
sphericalDesc.twistLimit.high.restitution = 0.75;
sphericalDesc.twistLimit.high.hardness = 0.5;

sphericalDesc.flags |= NX_SJF_TWIST_SPRING_ENABLED;
sphericalDesc.twistSpring.spring = 0.75;
sphericalDesc.twistSpring.damper = 1;

return (NxSphericalJoint*)gScene->createJoint(sphericalDesc);
}

Each blade is attached to the ground by the same link.

3 Conclusion
This ends the lessons built from the PSCL scripts used in Rocket. Grass, trees, and other
sorts of bendy vegetation can be built using the SDK.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 406 – Articulated Truck

1 Introduction
We build a fully articulated tractor-trailer truck. The truck is comprised of a tractor actor
and a trailer actor. The tractor actor is hitched to the trailer actor by a revolute joint.
Each wheel is connected by a revolute joint to a shapeless roll-axis actor. The roll-axis
actor is connected to the tractor (or trailer) by a prismatic joint and the roll-axis actor is
held to the tractor (or trailer) by a tight spring, which simulates the vehicle's suspension.
The two front steering wheels are differentiated from the regular wheels in that they are
connected to their roll-axis actor by an additional steer-axis actor via a revolute “steering
joint”, whose global axis is set along the y-axis.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
2 Articulated Truck
We create a wheel material for the truck wheels and a default material for the rest of the
objects in the scene.

#include "Truck.h"

Truck* truck;
...
void InitNx()
{
……

// Create the objects in the scene


groundPlane = CreateGroundPlane();

truck = new Truck();


truck->create(*gScene, *groundPlane);

gSelectedActor = truck->tractor;
UpdateTime();
}

We then create a truck object as defined in Truck.h.

class Truck
{
public:

Truck(): force(0), steerAngle(0) {}

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
NxActorDesc actorDesc;

……

NxJoint* joint = scene.createJoint(distDesc);

// disable collision detection


scene.setActorPairFlags(*wheel, *holder, NX_IGNORE_PAIR);
}

….
};
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
struct SteerWheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
NxActorDesc actorDesc;
…..

// disable collision detection


scene.setActorPairFlags(*wheel.wheel, *holder, NX_IGNORE_PAIR);
}

…..
};

void create(NxScene& scene, NxActor& ground)


{
NxBodyDesc bodyDesc;

…….

// NxJointLimitDesc swingLimit;
scene.createJoint(sphJointDesc);
}

void control(bool keyLeft, bool keyRight, bool keyUp, bool keyDown, float dt)
{
const NxReal maxSteerAngle = 30.0f;
NxReal sangle;

…….

for (i=0; i<2; i++)


steerWheels[i].wheel.rollJoint->setMotor(NxMotorDesc(maxvel, 1.0f *
NxMath::abs(force), false));
}

……

float force;
float steerAngle;
};

The create() method in the Truck class builds the tractor actor and the trailer actor.

class Truck
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
{
...
void create(NxScene& scene, NxActor& ground)
{

...
tractor = scene.createActor(tractorDesc);

// trailer actor (also two shapes)


NxActorDesc trailerDesc;
...
trailer = scene.createActor(traileractorDesc);
...
steerWheels[0].create(scene, NxVec3(3.5,1.5,4), 1.5, tractor);
...
}
...
NxActor* tractor;
NxActor* trailer;
SteerWheel steerWheels[2];
Wheel frontWheels[2];
Wheel backWheels[4];

float force;
float steerAngle;
};

The lower shapes on the tractor and the trailer have collision with the ground turned off
so they don't scrape against the ground when the suspension on the wheels gets pressed.

class Truck
{
...
void create(NxScene& scene, NxActor& ground)
{
...
// remove collision between lower truck parts and ground
scene.setShapePairFlags(*tractor->getShapes()[1], *ground.getShapes()[0],
NX_IGNORE_PAIR);
scene.setShapePairFlags(*trailer->getShapes()[1], *ground.getShapes()[0],
NX_IGNORE_PAIR);
...
}
...
NxActor* tractor;
NxActor* trailer;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
...
};

The wheels at the back of the tractor are made not to collide with the trailer actor.

class Truck
{
...
void create(NxScene& scene, NxActor& ground)
{
...
// additionally remove collision between front wheels and the trailer
scene.setActorPairFlags(*frontWheels[0].wheel,*trailer, NX_IGNORE_PAIR);
scene.setActorPairFlags(*frontWheels[1].wheel,*trailer, NX_IGNORE_PAIR);
...
}
...
};

The trailer gets hitched to the tractor with a spherical joint.

class Truck
{
...
void create(NxScene& scene, NxActor& ground)
{
...
// create rotation joint for the tractor trailer connection
NxSphericalJointDesc sphJointDesc;
sphJointDesc.actor[0] = tractor;
sphJointDesc.actor[1] = trailer;
……

// NxJointLimitDesc swingLimit;
scene.createJoint(sphJointDesc);
...
}
...
};

We then call the Wheel create() method and the SteerWheel create() method to build the
wheels connected to the tractor and the trailer. Each wheel structure creation function
gets passed its position and the actor it's attached to, either the tractor or the trailer.

class Truck
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
...
void create(NxScene& scene, NxActor& ground)
{
...
steerWheels[0].create(scene, NxVec3(3.5,1.5,4), 1.5, tractor);
steerWheels[1].create(scene, NxVec3(3.5,1.5,-4), 1.5, tractor);
frontWheels[0].create(scene, NxVec3(16,1.5,4), 1.5, tractor);
frontWheels[1].create(scene, NxVec3(16,1.5,-4), 1.5, tractor);
...
backWheels[0].create(scene, NxVec3(30.5,1.5, 4), 1.5, trailer);
backWheels[1].create(scene, NxVec3(30.5,1.5,-4), 1.5, trailer);
backWheels[2].create(scene, NxVec3(33.6,1.5, 4), 1.5, trailer);
backWheels[3].create(scene, NxVec3(33.6,1.5,-4), 1.5, trailer);
...
}
...
SteerWheel steerWheels[2];
Wheel frontWheels[2];
Wheel backWheels[4];
...
};

The wheels are built from a sphere shape actor connected to a shapeless roll-axis actor by
a revolute joint.

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;
NxSphereShapeDesc sphereDesc;

bodyDesc.solverIterationCount = 20;

// wheel
sphereDesc.radius = rad;
sphereDesc.materialIndex = wheelMaterialIndex;
actorDesc.shapes.pushBack(&sphereDesc);
bodyDesc.mass = 400;
actorDesc.body = &bodyDesc;
actorDesc.globalPose.t = pos;
wheel = scene.createActor(actorDesc);

// roll axis
bodyDesc.mass = 50;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

6
bodyDesc.massSpaceInertia = NxVec3(1,1,1);
actorDesc.body = &bodyDesc;
actorDesc.shapes.clear();
actorDesc.globalPose.t = pos;
rollAxis = scene.createActor(actorDesc);

// revolute joint connecting wheel with rollAxis


NxRevoluteJointDesc revJointDesc;
revJointDesc.projectionMode = NX_JPM_POINT_MINDIST;
revJointDesc.actor[0] = wheel;
revJointDesc.actor[1] = rollAxis;
revJointDesc.setGlobalAnchor(pos);
revJointDesc.setGlobalAxis(NxVec3(0,0,1));
rollJoint = (NxRevoluteJoint*)scene.createJoint(revJointDesc);
...
}

NxActor* wheel;
NxActor* rollAxis;
NxRevoluteJoint* rollJoint;
};

This roll-axis actor is then connected to the “holder” actor, the tractor or trailer, by a
prismatic joint. The prismatic joint gives an up-down motion to the roll-axis actor to
simulate suspension.

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
// prismatic joint connecting rollAxis with holder
NxPrismaticJointDesc prisJointDesc;
prisJointDesc.actor[0] = rollAxis;
prisJointDesc.actor[1] = holder;
prisJointDesc.setGlobalAnchor(pos);
prisJointDesc.setGlobalAxis(NxVec3(0,1,0));
scene.createJoint(prisJointDesc);
...
}
...
};

The prismatic actor allows the roll-axis to move up and down with respect to the tractor
or trailer. We then attach a spring to restore the rest position of the roll-axis actor. This
prismatic-joint and spring combination is the shock absorber for the wheel.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
Use the distance joint to the suspension.

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
// add distance joint to the suspension (i.e. the related actors)
float springLength = 0.1f;

NxDistanceJointDesc distDesc;

…..
distDesc.setGlobalAnchor(pos);
NxJoint* joint = scene.createJoint(distDesc);
...
}
...
};

The wheel is finally made non-collidable with its holder, the tractor or the trailer.

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
// disable collision detection
scene.setActorPairFlags(*wheel, *holder, NX_IGNORE_PAIR);
...
}
...
};

3 Shapeless Actors
Take a look at the roll-axis actor in our wheel structure again.

struct Wheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;
NxSphereShapeDesc sphereDesc;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

8
...
// roll axis
bodyDesc.mass = 50;
bodyDesc.massSpaceInertia = NxVec3(1,1,1);
actorDesc.body = &bodyDesc;
actorDesc.shapes.clear();
actorDesc.globalPose.t = pos;
rollAxis = scene.createActor(actorDesc);
...
}
...
};

The roll-axis actor is shapeless. Since it has no shapes, we can't assign its body a density
to get a mass and inertia tensor for it. We must assign it's body a mass and inertia tensor.

bodyDesc.mass = 50;
bodyDesc.massSpaceInertia = NxVec3(1,1,1);
actorDesc.body = &bodyDesc;

We make sure it's shapeless by clearing its shapes array.

actorDesc.shapes.clear();

Then we set its position.

actorDesc.globalPose.t = pos;

You can think of a shapeless actor as an object with no collision and all mass and inertia
properties concentrated at a single point in space. In essence, disregarding collision, this
is how all rigid bodies are treated. With its inertia tensor at (1,1,1), we could have built
our roll-axis actor out of a sphere actor, assigned it a mass of 50 and a small token radius.
If we turned collision detection off on the sphere actor, we would see no difference
between the behaviour of our “dummy” sphere actor and the shapeless actor.

4 The Steering Wheels


Two of the wheels we create for our truck, the two in front, are SteerWheels.

class Truck
{
...
void create(NxScene& scene, NxActor& ground)
{
...
steerWheels[0].create(scene, NxVec3(3.5,1.5,4), 1.5, tractor);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

9
steerWheels[1].create(scene, NxVec3(3.5,1.5,-4), 1.5, tractor);
...
}
...
SteerWheel steerWheels[2];
...
};

On top of the regular wheel, a sphere connected to the roll-axis by a revolute joint, the
steer wheel has an extra steer-axis actor. The steer-axis actor, like the roll-axis actor, is
shapeless.

struct SteerWheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;
...
// steer axis
bodyDesc.mass = 50;
bodyDesc.massSpaceInertia = NxVec3(1,1,1);
actorDesc.body = &bodyDesc;
actorDesc.shapes.clear();
...
}
...
};

When we build the base wheel class, we pass in the steer-axis actor as its holder.

struct SteerWheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
actorDesc.globalPose.t = pos;
steerAxis = scene.createActor(actorDesc);

wheel.create(scene, pos, rad, steerAxis);


...
}
...
}

We then connect the steer-axis actor to its holder, the tractor, via a revolute joint.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

10
struct SteerWheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
// revolute joint connecting steerAxis with the holder
NxRevoluteJointDesc revJointDesc;
revJointDesc.projectionMode = NX_JPM_POINT_MINDIST;
revJointDesc.actor[0] = steerAxis;
revJointDesc.actor[1] = holder;
revJointDesc.setGlobalAnchor(pos);
revJointDesc.setGlobalAxis(NxVec3(0,1,0));
steerJoint = (NxRevoluteJoint*)scene.createJoint(revJointDesc);
...
}
...
}

The wheel shape is made non-collidable with the tractor as with the other wheels.

struct SteerWheel
{
void create(NxScene& scene, const NxVec3& pos, float rad, NxActor* holder)
{
...
// disable collision detection
scene.setActorPairFlags(*wheel.wheel, *holder, NX_IGNORE_PAIR);
}
...
}

The steer wheels now have an extra degree of freedom beyond the rolling freedom of the
regular wheels. We can steer them about the y-axis as well

To review:

Wheel = Sphere -> Revolute Joint -> Roll-Axis Actor -> Prismatic Joint
Steering Wheel = (Wheel)->Steer-Axis Actor->Revolute Joint

As you can see, such assemblies of joints and actors can be used to create simple or
complex machinery in much the same way the machinery is built in the real world.

5 Powering the Truck


Hit “v” to set the bTruckIsPowered boolean.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

11
bool bTruckIsPowered = false;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
...
case 'v': { bTruckIsPowered = !bTruckIsPowered; break; }
...
}
}

Once this variable is set, the “ijkl” keys will now provide power to the truck wheels
rather than a force to the focus actor

Truck* truck = NULL;


...
void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

// Force controls
case 'i':
{
if (bTruckIsPowered)
truck->control(false, false, true, false, 1.0f/60.0f);
...
break;
}
case 'k':
{
if (bTruckIsPowered)
truck->control(false, false, false, true, 1.0f/60.0f);
...
break;
}
case 'j':
{
if (bTruckIsPowered)
truck->control(true, false, false, false, 1.0f/60.0f);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

12
...
break;
}
case 'l':
{
if (bTruckIsPowered)
truck->control(false, true, false, false, 1.0f/60.0f);
...
break;
}
...
}
}

Each “I,j,k,l” key calls the truck's control() method.

“j” and “l” set keyLeft and keyRight respectively, activating springs on the steer joints of
both steering wheels to turn the wheels left and right.

class Truck
{
...
void control(bool keyLeft, bool keyRight, bool keyUp, bool keyDown, float dt)
{
const NxReal maxSteerAngle = 30.0f;
NxReal sangle;

if (keyLeft) steerAngle += 1.0f;


if (keyRight) steerAngle -= 1.0f;

if (steerAngle > 0.0)


sangle = NxMath::min(steerAngle, maxSteerAngle);
else
sangle = NxMath::max(steerAngle, -maxSteerAngle);
steerAngle = sangle;

steerWheels[0].steerJoint->setSpring(NxSpringDesc(5e4, 100,
NxMath::degToRad(sangle)));
steerWheels[1].steerJoint->setSpring(NxSpringDesc(5e4, 100,
NxMath::degToRad(sangle)));
...
}
...
float steerAngle;
};
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

13
“i” and “k” set keyUp and keyDown, respectively, activating motors on the steer wheels
and the front wheels of the truck tractor.

class Truck
{
...
float gdvAcc = dt * 30.0f;
float gdvBrk = dt * 10.0f;

if (keyUp && !keyDown) force += gdvAcc;


else if (!keyUp && keyDown) force -= gdvBrk;
else force = 0.0f;

unsigned i;
float maxvel = NxMath::sign(force) * 1e5f;
for (i=0; i<2; i++)
frontWheels[i].rollJoint->setMotor(NxMotorDesc(maxvel, 1.0f *
NxMath::abs(force), false));
for (i=0; i<2; i++)
steerWheels[i].wheel.rollJoint->setMotor(NxMotorDesc(maxvel, 1.0f *
NxMath::abs(force), false));
}
...
float force;
...
};

Try driving the truck around with the “ijkl” keys to get a feel for how we steer and
provide power to the truck wheels.

6 Conclusion
We have created an articulated truck from a truck object class that creates a group of
actors and joints which it connects together much like an actual truck is assembled.

You will see that by looking at vehicle and other machine schematics and seeing how the
various machine components map to PhysX actors, joints, and springs, you can build
your own articulated objects based on real-world machinery.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

14
Lesson 407 – Particle Systems

1 Introduction
We create a particle emitter class and a particle class. The particle emitter object spawns
new particle objects on every frame. The particles are actors that get updated on their
own every frame.

2 Particle Systems
In InitNx(), we build our particle emitter object.

const NxCollisionGroup cParticleCollisionGroup = 1;


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
...
// Particle system globals
ParticleEmitter* gParticleEmitter = NULL;
NxReal fStartingParticleSpeed = 5.0f; // scale of initial particle velocity, in m/s
NxReal fVerticalThermoDynamicAccel = 5.0f; // scale of false vertical thermodynamic
acceleration (smoke accelerates upward at this rate in m/s^2 )
...
void InitNx()
{
...
// Create the objects in the scene
groundPlane = CreateGroundPlane();

// This
CreateStaticBox();

// Ok, now add our emitter


gParticleEmitter = new ParticleEmitter( gScene, NxVec3( -5.0f, 5.0f, 0.0f ),
fStartingParticleSpeed, NxVec3( 0.0f, fVerticalThermoDynamicAccel, 0.0f ));

// cParticleCollisionGroup == 1
// this is a group specifically for particles
// at the moment, it collides with everything but itself
// so that smoke particles don't collide with themselves (an optimization)
// you might want to also make it not collide with certain other objects
gPhysicsSDK->setGroupCollisionFlag(cParticleCollisionGroup,
cParticleCollisionGroup, false);
...
}

CreateStaticBox() builds a roof above the particle emitter so the particles have an
environmental object to collide with as they rise through the air.

NxActor* CreateStaticBox()
{
// Add a single-shape actor to the scene
NxActorDesc actorDesc;

// The actor has one shape, a box, fCubeSide meters on a side


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set( 10.0f, 1.0f, 10.0f );
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = NULL; // make her static


actorDesc.density = 10.0f;
actorDesc.globalPose.t.set( 0.0f, 20.0f, 0.0f );
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
return gScene->createActor(actorDesc);
}

The particles created by the emitter will be assigned to the “cParticleCollisionGroup”


group of collision shapes. This group is made non-intersecting so the particles can self-
intersect and won't interfere with each other.

gPhysicsSDK->setGroupCollisionFlag(cParticleCollisionGroup,
cParticleCollisionGroup, false);

We create a particle emitter just below the static roof at (-5,5,0) and supply it with a
starting speed for our particles as well as a thermo-dynamic acceleration to make the
particles accelerate as they rise through the air.

gParticleEmitter = new ParticleEmitter(gScene, NxVec3(-5,5,0),


fStartingParticleSpeed, NxVec3(0,fVerticalThermoDynamicAccel,0));

3 Particle Emitter Class


Take a look at the particle emitter constructor in Particle.cpp.

ParticleEmitter::ParticleEmitter(NxScene* pSceneToSet, NxVec3& vStartingPos,


NxReal fStartingVelScale, NxVec3& vThermoDynamicAcceleration)
{
_iHeadIndex = 0;
_iTailIndex = 0;
_iParticleCount = 0;
_pScene = pSceneToSet;
_vStartingPos = vStartingPos;
_fStartingVelScale = (1.0f / NxReal(RAND_MAX)) * fStartingVelScale;
_fNewParticleTimer = 0.0f;
_vThermoDynamicAcceleration = vThermoDynamicAcceleration;
}

We start out with a zero particle count. We give our emitter a pointer to the scene so it
can create particle actors in its update call. The starting velocity is randomly determined
on a scale of a maximum starting velocity which we passed in to the constructor. We set
its thermodynamic acceleration, which will specifies how fast the particles will rise
through the air.

Once created, the particle emitter class gets updated in RunPhysics().

void RunPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
// Run collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
gScene->fetchResults(NX_RIGID_BODY_FINISHED, true);

gParticleEmitter->update(deltaTime);
}

This calls the particle emitter's update method, passing in the time slice for the frame.

void ParticleEmitter::update(NxReal fDeltaTime)


{
_fNewParticleTimer -= fDeltaTime;
if (_fNewParticleTimer < 0.0f)
{
addParticle();
_fNewParticleTimer += cfTimeBetweenParticles;
}

// Update all particles


int iParticlesUpdated = 0;
for (int iParticleIndex = _iTailIndex; iParticlesUpdated < _iParticleCount;
iParticleIndex = ((iParticleIndex+1) % ciMaxParticles))
{
_aParticleArray[iParticleIndex]->update(fDeltaTime);
++iParticlesUpdated;
}
}

4 Particle Class
The emitter's update method calls it's addParticle() method when the time for a new
particle is hit. This creates a particle object.

void ParticleEmitter::update(NxReal fDeltaTime)


{
_fNewParticleTimer -= fDeltaTime;
if (_fNewParticleTimer < 0.0f)
{
addParticle();
_fNewParticleTimer += cfTimeBetweenParticles;
}
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
void ParticleEmitter::addParticle()
{
if ((_iTailIndex == _iHeadIndex) && (_iParticleCount != 0)) // FIFO is full
{
removeParticle();
// Now there is a slot free
}

// Add a single-shape actor to the scene


NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

NxSphereShapeDesc sphereDesc;
sphereDesc.radius = 1.0f;
sphereDesc.group = cParticleCollisionGroup; // this group does not collide with itself
actorDesc.shapes.pushBack(&sphereDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = 10.0f;
actorDesc.globalPose.t = _vStartingPos;

NxActor* pNewActor = _pScene->createActor(actorDesc);

// Give it an initial linear velocity, scaled by _fStartingVelScale


NxVec3 vRandVec(NxReal(rand()*_fStartingVelScale),
NxReal(rand()*_fStartingVelScale), NxReal(rand()*_fStartingVelScale));
pNewActor->setLinearVelocity(vRandVec);

// Turn off gravity for smoke


pNewActor->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);

_aParticleArray[_iHeadIndex] = new Particle(pNewActor,


_vThermoDynamicAcceleration);

_iHeadIndex = (_iHeadIndex+1) % ciMaxParticles;


++_iParticleCount;
}

The new particle is a sphere-shape actor. Gravity is disabled on it. The particle's
constructor gets the newly created actor to build it.

Particle::Particle(NxActor* pToSet, NxVec3& vThermoDynamicAcceleration)


{
setActor(pToSet);

// F = M*a
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

5
_vThermoDynamicForce = pToSet->getMass()*vThermoDynamicAcceleration;
}

void Particle::setActor(NxActor* pToSet)


{
_pActor = pToSet;
}

The emitter then updates all the particles in its particle array.

void ParticleEmitter::update(NxReal fDeltaTime)


{
...
// Update all particles
int iParticlesUpdated = 0;
for (int iParticleIndex = _iTailIndex; iParticlesUpdated < _iParticleCount;
iParticleIndex = ((iParticleIndex+1) % ciMaxParticles))
{
_aParticleArray[iParticleIndex]->update(fDeltaTime);
++iParticlesUpdated;
}
}

Particles get updated according to the particle object's update method.

void Particle::update(NxReal fDeltaTime)


{
// particles have a false, thermodynamic force applied which pushes them up every
frame
_pActor->addForce(_vThermoDynamicForce);

// Use our down-caster to get a sphere pointer (and make sure it is a sphere)
NxSphereShape* pSphere = _pActor->getShapes()[0]->isSphere();
const NxReal fMaxRadius = 3.0f;
const NxReal fGrowthRate = 0.6f;
if (pSphere)
{
// Grow the radius at growth rate fGrowthRate m/s, until it reaches fMaxRadius
meters
NxReal fCurrentRadius = pSphere->getRadius();
if (fCurrentRadius < fMaxRadius)
{
pSphere->setRadius(pSphere->getRadius() + fGrowthRate*fDeltaTime);
if (pSphere->getRadius() > fMaxRadius)
pSphere->setRadius(fMaxRadius);
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

6
}
}

The particle object's update method supplies the upward thermodynamic force to the
particle actor and increases its radius until the particle has reached a maximum radius.

5 Conclusion
We have created a particle system feature built on top of the SDK. The SDK has a built-
in system of fluid particles which can be made self-interacting or non-selfinteracting. As
we will see, the system we have created is very much like a system of non-selfinteracting
fluid particles generated by the SDK, only with parameters we have specified outside of
the SDK. Our system is not as fast or efficient as the SDK's streamlined particle system.
It serves to demonstrate, however, that SDK is very versatile and offers many paths to
choose from to accomplish the effects you want.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

7
Lesson 408 – Fragmenting Objects

1 Introduction
In many games, you want to create destructible objects that break apart or explode into
component and/or prefractured pieces. In this lesson we show how to construct an actor
from pieces, the actor's shapes conforming to the pieces, then disengage the shapes from
the main actor and building them into their own autonomous actors.

2 Creating the Cube of Cubes


We create a cube out of many smaller cubes, and then break off cubes one at a time to
fragment the larger object.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
In InitNx(), we call CreateMainObject().

This function loops through the cubes that will form the large, final cube. The number of
cubes is specified by the cube of the global iNumBoxesOnSide, at the top of
Lesson313.cpp.

The line:

boxDesc[ iCurrentIndex ].localPose.t.set( NxVec3( ((i-1) * fCubeSide * 2.0f), ((j-


1) * fCubeSide * 2.0f), ((k-1) * fCubeSide * 2.0f)));

sets the local pose of each sub-cube so that they fit together to make a large cube of
cubes. Each of these shapes is then pushed onto the array of shapes in actorDesc.

3 Breaking off a Cube


When you hit “g”, ReleaseRandomShape() is called. First, we check the number of
shapes in our actor with:

NxU32 uiNumShapes = mainBox->getNbShapes();

and get the array of pointers to those shapes with:

NxShape** ppShapeArray = mainBox->getShapes();

You don't want to remove a shape if there is only one left, as that will leave you with a
shape-less actor. We now record the global pose of the shape we are removing, so we can
use it as the starting position of the new shape. You release a shape with:

mainBox->releaseShape( *pShapeToRelease );

where mainBox is your actor pointer, and pShapeToRelease is a pointer to a valid shape
(in that actor) that you want to release. Here, we choose the first shape in the array
ppShapeArray, but you might want to base it on other information (such as a contact
point with a pointer to a shape). Now that the shape is released, it is important to call:

mainBox->updateMassFromShapes(fBoxDensity, 0.0f );

in order to recalculate the mass and inertial tensor for the object. You can either pass this
function a density with the mass 0.0f, or the density 0.0f with a total mass. Rather than
calculate the new mass ourselves, we just send the density (which is the same for every
cube in our compound).

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Finally, we create a new, simple box with the same dimensions and global pose as the
shape we just released. You might consider giving it the same linear and angular velocity
of the larger object, or a separating velocity, but in this demo it starts with zero velocity.

4 Maintaining “Hidden” Kinematic Piece Actors to Avoid


Recalculating the Broad Phase
You will get a performance hit the more actors you add to the scene collision geometry
on any given frame. This is because you are inserting new elements to be considered for
collision, and the broad phase of collision detection has to sort these elements into arrays
of potentially colliding objects.

You can see the potential hazard for this in this lesson and the next. In this lesson, we see
the potential for spawn multiple piece actors in place of one large actor constructed from
the pieces. In the next lesson, where we create a particle system, we see the potential for
adding a great deal of objects into the scene simultaneously from a point source.

Maintaining “hidden” kinematic actors with contact generation enabled but collision
response turned off is one way to avoid a massive recalculation of the broad phase when
introducing new objects into a scene. You will eventual “unhide” these actors as you
make them collidable and dynamic. For this example, you can spawn all of the piece
actors up front, make them kinematic, then enable contact generation and disable
collision response on them. You will need to set their positions and orientations each
frame to shadow the movement of the shapes of the actor they will eventually replace
when those shapes are removed.

The kinematic actors that form the piece actors have contact generation enabled, so they
will be updated properly in the broad phase potentially collidable objects list as the
simulation progresses. Because of this, introducing them into the scene by making them
dynamic and turning on collision response will have no impact on the broad phase.

5 Conclusion
This is a method to simulate fragmentation. It costs very little while the object is not
fragmented, but during the process of fragmenting there will be overhead in the form of
creating a new actor and recalculating the old compound actor.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Lesson 501 – Debug Renderer

1 Introduction
The debug renderer allows us to visualize our physics scene. At the end of each frame of
the simulation, the SDK constructs relevant points, lines, and triangles of the simulation
to be drawn. It stores these in its “debug renderable” structure.

This renderer is for debug purposes only. In release mode, you don't want the SDK to
spend extra time building the debug visuals at the end of each frame of simulation.

When we draw our scene fully textured, lit, etc., we draw it in parallel with our physics
simulation. We let the physics run to completion, get the results for the frame, then draw
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
the results while we are process the next frame of the simulation. The debug render lines
serve as a good check to see how well the visual representation of our objects matches
their physical representation.

2 The Debug Renderer


In DebugRenderer.h, we define a class called DebugRenderer.

class DebugRenderer
{
public:
void renderData(const NxDebugRenderable& data) const;
};

This class gets instantiated in CommonCode.cpp.

// User report globals


DebugRenderer gDebugRenderer;

We call the renderData() function on the debug renderer in ProcessInputs(), called from
RenderCallback(), and pass in the scene's debug renderable. Hitting “b” toggles the call
per frame to DebugRenderer::renderData() on and off.

// Physics SDK globals


...
NxScene* gScene;
...
// User report globals
DebugRenderer gDebugRenderer;
...
// Simulation globals
...
bool bDebugWireframeMode;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
...
switch (key)
{
...
case 'b': { bDebugWireframeMode = !bDebugWireframeMode; break; }
...
}
...
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
void ProcessInputs()
{
...
// Show debug wireframes
if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

void RenderCallback()
{
...
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}
...
}

The scene's debug renderable is a structure containing points, lines, and triangles. We
specify how we want these points, lines, and triangles drawn in our
DebugRenderer::renderData() call defined in DebugRenderer.cpp.

NX_INLINE void setupColor(NxU32 color)


{
NxF32 Blue = NxF32((color)&0xff)/255.0f;
NxF32 Green = NxF32((color>>8)&0xff)/255.0f;
NxF32 Red = NxF32((color>>16)&0xff)/255.0f;
glColor3f(Red, Green, Blue);
}

void DebugRenderer::renderData(const NxDebugRenderable& data) const


{
glLineWidth(1.0f);

glPushMatrix();
glDisable(GL_LIGHTING);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Render points
NxU32 NbPoints = data.getNbPoints();
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
if(NbPoints)
{
const NxDebugPoint* Points = data.getPoints();

glBegin(GL_POINTS);
while(NbPoints--)
{
setupColor(Points->color);
glVertex3fv(&Points->p.x);
Points++;
}
glEnd();
}

// Render lines
NxU32 NbLines = data.getNbLines();
if(NbLines)
{
const NxDebugLine* Lines = data.getLines();

glBegin(GL_LINES);
while(NbLines--)
{
setupColor(Lines->color);
glVertex3fv(&Lines->p0.x);
glVertex3fv(&Lines->p1.x);
Lines++;
}
glEnd();
}

// Render triangles
NxU32 NbTris = data.getNbTriangles();
if(NbTris)
{
const NxDebugTriangle* Triangles = data.getTriangles();

glBegin(GL_TRIANGLES);
while(NbTris--)
{
setupColor(Triangles->color);
glVertex3fv(&Triangles->p0.x);
glVertex3fv(&Triangles->p1.x);
glVertex3fv(&Triangles->p2.x);
Triangles++;
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
glEnd();
}

// Reset the color


glColor3f(1,1,1);

glEnable(GL_LIGHTING);
glPopMatrix();
}

3 Visualization Parameters
We pass in visualization parameters to NxPhysicsSDK::setParameter() in InitNx() to tell
the debug renderable what points, lines, and triangles to build for debug visualization. In
our application, we start out with just three visualization parameters set.

void InitNx()
{
...
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);
if (!gPhysicsSDK) return;
...
// Set the debug visualization parameters
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);
...
}

To see any visualization, you need to set NX_VISUALIZATION_SCALE to a non-zero


value first.

gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);

For now, this means the actor axes and face normals will be drawn with a length of 1
meter. Since our actors are each a few meters in size, this length serves us nicely. If our
actors were on the order of centimeters, this size would be too large. If they were on the
order of kilometers, this size would be too small.

Next we set the collision shapes of our actors to be visualized.

gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Run the demo and hit “b” to go to debug visualization mode. You will see white
wireframes outlining each of your collision shapes. You will also notice a red, green, and
blue actor axis at the bottom of each of your actors. This is set here.

gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

Finally, you will notice blue lines representing face normals on the convex object in the
middle of the scene. This is set up for visualization here.

gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);

We have just set these four for this demo, but there are many more. The following is a
list of all the debug visualization parameters you can set with a short description next to
each.

NX_VISUALIZATION_SCALE

This overall visualization scale gets multiplied with the individual scales. Setting
to zero turns ignores all visualizations. Default is 0.

The below settings permit the debug visualization of various simulation properties. The
setting is either zero, in which case the property is not drawn. Otherwise it is a scaling
factor that determines the size of the visualization widgets. Only bodies and joints for
which visualization is turned on using setFlag(VISUALIZE) are visualized. Contacts are
visualized if they involve a body which is being visualized. Default is 0.

Notes:

1. To see any visualization, you have to set NX_VISUALIZATION_SCALE to nonzero


first.
2. The scale factor has been introduced because it's difficult (if not impossible) to come
up with a good scale for 3D vectors. Normals are normalized and their length is
always 1. But it doesn't mean we should render a line of length 1. Depending on your
objects/scene, this might be completely invisible or extremely huge. That's why the
scale factor is here, to let you tune the length until it's ok in your scene.
3. However, things like collision shapes aren't ambiguous. They are clearly defined for
example by the triangles & polygons themselves, and there's no point in scaling that.
So the visualization widgets are only scaled when it makes sense.

NX_VISUALIZE_WORLD_AXES

Visualize the world axes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
Body Visualizations

NX_VISUALIZE_BODY_AXES

Visualize a body's axes

NX_VISUALIZE_BODY_MASS_AXES

Visualize a body's mass axes

NX_VISUALIZE_BODY_LIN_VELOCITY

Visualize the body's linear velocity

NX_VISUALIZE_BODY_ANG_VELOCITY

Visualize the body's angular velocity

NX_VISUALIZE_BODY_LIN_MOMENTUM

Visualize the body's linear momentum

NX_VISUALIZE_BODY_ANG_MOMENTUM

Visualize the body's angular momentum

NX_VISUALIZE_BODY_LIN_FORCE

Visualize linear forces applied to bodies

NX_VISUALIZE_BODY_ANG_FORCE

Visualize angular forces applied to bodies

NX_VISUALIZE_BODY_REDUCED

Visualize bodies, reduced set of visualizations


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
NX_VISUALIZE_BODY_JOINT_GROUPS

Visualize joint groups

NX_VISUALIZE_BODY_CONTACT_LIST

Visualize the contact list

NX_VISUALIZE_BODY_JOINT_LIST

Visualize the body joint list

NX_VISUALIZE_BODY_DAMPING

Visualize body damping

NX_VISUALIZE_BODY_SLEEP

Visualize sleeping bodies

Joint Visualizations

NX_VISUALIZE_JOINT_LOCAL_AXES

Visualize joint local axes

NX_VISUALIZE_JOINT_WORLD_AXES

Visualize joint world axes

NX_VISUALIZE_JOINT_LIMITS

Visualize joint limits

NX_VISUALIZE_JOINT_ERROR

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
Visualize joint errors

NX_VISUALIZE_JOINT_FORCE

Visualize joint forces

NX_VISUALIZE_JOINT_REDUCED

Visualize joints, reduced set of visualizations

Contact Visualizations

NX_VISUALIZE_CONTACT_POINT

Visualize contact points

NX_VISUALIZE_CONTACT_NORMAL

Visualize contact normals

NX_VISUALIZE_CONTACT_ERROR

Visualize contact errors

NX_VISUALIZE_CONTACT_FORCE

Visualize contact forces

Actor Visualizations

NX_VISUALIZE_ACTOR_AXES

Visualize actor axes

NX_VISUALIZE_COLLISION_AABBS

Visualize bounds (AABBs in world space)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
NX_VISUALIZE_COLLISION_SHAPES

Shape visualization

NX_VISUALIZE_COLLISION_AXES

Shape axis visualization

NX_VISUALIZE_COLLISION_COMPOUNDS

Compound visualization (compound AABBs in world space)

NX_VISUALIZE_COLLISION_VNORMALS

Mesh & convex vertex normals

NX_VISUALIZE_COLLISION_FNORMALS

Mesh & convex face normals

NX_VISUALIZE_COLLISION_EDGES

Active edges for meshes

NX_VISUALIZE_COLLISION_SPHERES,

Bounding spheres

NX_VISUALIZE_COLLISION_SAP

Sweep-and-prune structure

NX_VISUALIZE_COLLISION_STATIC

Static pruning structures

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
10
NX_VISUALIZE_COLLISION_DYNAMIC

Dynamic pruning structures

NX_VISUALIZE_COLLISION_FREE

“Free” pruning structures

NX_VISUALIZE_COLLISION_CCD

Visualize the CCD tests

NX_VISUALIZE_COLLISION_SKELETONS

Visualize the CCD skeletons

Fluid Visualizations

NX_VISUALIZE_FLUID_EMITTERS

Emitter visualization

NX_VISUALIZE_FLUID_POSITION

Particle position visualization

NX_VISUALIZE_FLUID_VELOCITY

Particle velocity visualization

NX_VISUALIZE_FLUID_KERNEL_RADIUS

Particle kernel radius visualization

NX_VISUALIZE_FLUID_BOUNDS

Fluid AABB visualization

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
11
4 Conclusion
The debug renderer collects renderable data with which we can view our scene.

5 Related Classes, Functions, and Parameters


NxDebugRenderable
numPoints
numLines
numTriangles
points
lines
triangles
getNbPoints()
getPoints()
getNbLines()
getLines()
getNbTriangles()
getTriangles()

NxDebugColor
NX_ARGB_BLACK
NX_ARGB_RED
NX_ARGB_GREEN
NX_ARGB_BLUE
NX_ARGB_YELLOW
NX_ARGB_MAGENTA
NX_ARGB_CYAN
NX_ARGB_WHITE

NxDebugPoint
p
color

NxDebugLine
p0
p1
color

NxDebugTriangle
p0
p1
p2
color

NxScene
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
12
getDebugRenderable()

NxParameter
NX_VISUALIZATION_SCALE
NX_VISUALIZE_WORLD_AXES
NX_VISUALIZE_BODY_AXES
NX_VISUALIZE_BODY_MASS_AXES
NX_VISUALIZE_BODY_LIN_VELOCITY
NX_VISUALIZE_BODY_ANG_VELOCITY
NX_VISUALIZE_BODY_LIN_MOMENTUM
NX_VISUALIZE_BODY_ANG_MOMENTUM
NX_VISUALIZE_BODY_LIN_ACCEL
NX_VISUALIZE_BODY_ANG_ACCEL
NX_VISUALIZE_BODY_LIN_FORCE
NX_VISUALIZE_BODY_ANG_FORCE
NX_VISUALIZE_BODY_REDUCED
NX_VISUALIZE_BODY_JOINT_GROUPS
NX_VISUALIZE_BODY_CONTACT_LIST
NX_VISUALIZE_BODY_JOINT_LIST
NX_VISUALIZE_BODY_DAMPING
NX_VISUALIZE_BODY_SLEEP
NX_VISUALIZE_JOINT_LOCAL_AXES
NX_VISUALIZE_JOINT_WORLD_AXES
NX_VISUALIZE_JOINT_LIMITS
NX_VISUALIZE_JOINT_ERROR
NX_VISUALIZE_JOINT_FORCE
NX_VISUALIZE_JOINT_REDUCED
NX_VISUALIZE_CONTACT_POINT
NX_VISUALIZE_CONTACT_NORMAL
NX_VISUALIZE_CONTACT_ERROR
NX_VISUALIZE_CONTACT_FORCE
NX_VISUALIZE_ACTOR_AXES
NX_VISUALIZE_COLLISION_AABBS
NX_VISUALIZE_COLLISION_SHAPES
NX_VISUALIZE_COLLISION_AXES
NX_VISUALIZE_COLLISION_COMPOUNDS
NX_VISUALIZE_COLLISION_VNORMALS
NX_VISUALIZE_COLLISION_FNORMALS
NX_VISUALIZE_COLLISION_EDGES
NX_VISUALIZE_COLLISION_SPHERES
NX_VISUALIZE_COLLISION_SAP
NX_VISUALIZE_COLLISION_STATIC
NX_VISUALIZE_COLLISION_DYNAMIC
NX_VISUALIZE_COLLISION_FREE
NX_VISUALIZE_COLLISION_CCD
NX_VISUALIZE_COLLISION_SKELETONS
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
13
NX_VISUALIZE_FLUID_EMITTERS
NX_VISUALIZE_FLUID_POSITION
NX_VISUALIZE_FLUID_VELOCITY
NX_VISUALIZE_FLUID_KERNEL_RADIUS
NX_VISUALIZE_FLUID_BOUNDS

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
14
Lesson 502 – Visual Remote Debugger

1 Introduction
The Visual Remote Debugger allows you to use a separate application to monitor the
PhysX SDK as it is running.

2 Visual Remote Debugger


Connect to the Visual Remote Debugger right after creation of the SDK.

// User report globals

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
...
extern NxRemoteDebugger* gRemoteDebugger;
...
void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);
gRemoteDebugger = gPhysicsSDK->getFoundationSDK().getRemoteDebugger();
gRemoteDebugger->connect("localhost");
...
}

This establishes connection between the SDK and your remote application. You can set
up the SDK to send information to the remote debugger via the remote debugger object.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
If you run the VRD and then run the demo, you're physics objects will show up in the
VRD, as in the above picture.

From the VRD, you can view object's properties and adjust them as well.

3 Conclusion
The Visual Remote Debugger is handy when you want to run an application alongside
your PhysX application to monitor your PhysX appliction.

4 Related Classes, Functions, and Parameters


NxFoundationSDK
getRemoteDebugger()

NxRemoteDebugger
connect()
flush()
isConnected()
frameBreak()
createObject()
removeObject()
addChild()
removeChild()
writeParameter()
getMask()
getPickedObject()
getPickPoint()

NxRemoteDebuggerObjectType
NX_DBG_OBJECTTYPE_GENERIC
NX_DBG_OBJECTTYPE_ACTOR
NX_DBG_OBJECTTYPE_PLANE
NX_DBG_OBJECTTYPE_BOX
NX_DBG_OBJECTTYPE_SPHERE
NX_DBG_OBJECTTYPE_CAPSULE
NX_DBG_OBJECTTYPE_CYLINDER
NX_DBG_OBJECTTYPE_CONVEX
NX_DBG_OBJECTTYPE_MESH
NX_DBG_OBJECTTYPE_WHEEL
NX_DBG_OBJECTTYPE_JOINT
NX_DBG_OBJECTTYPE_CONTACT
NX_DBG_OBJECTTYPE_BOUNDINGBOX
NX_DBG_OBJECTTYPE_VECTOR
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
NX_DBG_OBJECTTYPE_CAMERA

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 503 – Core Dump

1 Introduction
In the third chapter on Tools, we describe how to do a core dump. The core dump is a
means to save the state of a simulation to a file and load in the simulation state from a
file. We realize the core dump function using the NxuStream which is provided in source
codes under Tools\NxuStream2.

2 Core Dump
Look at ProcessInputs() in Lesson503.cpp.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
bool bCoreDump = false;
bool bLoadCore = false;
bool bInstantiateCore = false;
...
void ProcessInputs()
{
...
// Core dump
if (bCoreDump)
{
bool ok = NXU::coreDump(gPhysicsSDK, fnameCD, NXU::FT_XML,
true, false);
if(ok) {
printf("Output core dump successfully!\n");
} else {
printf("Output core dump failed!\n");
}
bCoreDump = false;
}

// Load core to core container


if (bLoadCore)
{
if(gPhysicsCollection) {
NXU::releaseCollection(gPhysicsCollection);
gPhysicsCollection = NULL;
}
gPhysicsCollection = LoadCoreDump(fnameCD);
if(!gPhysicsCollection) {
printf("Unable to load the core dump, please first save a core
dump.\n");
}
bLoadCore = false;
}

if(bInstantiateCore) {
if(gPhysicsCollection) {
if(gPhysicsSDK) {
NxReleasePhysicsSDK(gPhysicsSDK);
gPhysicsSDK = CreatePhysics();
}
if(InstantiateCoreDump(gPhysicsSDK, gPhysicsCollection)) {
if(gPhysicsSDK->getNbScenes()) {
gScene = gPhysicsSDK->getScene(0);
AddUserDataToActors(gScene);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
2
NxActor** actors = gScene->getActors();
gSelectedActor = *actors;
while(!IsSelectable(gSelectedActor))
{
gSelectedActor = *actors++;
}
}
}
}
bInstantiateCore = false;
}
...
}
...
void SpecialKeys(unsigned char key, int x, int y)
{
switch (key)
{
case 'c': { printf("Coredumping...\n"); bCoreDump = true; break; }
case 'v': { printf("Loading Core Dump to Container...\n"); bLoadCore =
true; break; }
case 'n': { printf("Instantiating Core...\n"); bInstantiateCore = true; break;
}
}
}

We have three options here:

1. Save out a core dump file.


2. Load a core dump file to a core container object.
3. Instantiate a scene from a loaded core container object.

We discuss each option in detail.

3 Saving a Core Dump


Hit “c” to save out a core dump file. Hitting “c” sets the bCoreDump flag which calls
NXU::coreDump().

We pass in four parameters. The first parameter is the physics sdk. The second is the
name of the file we will be writing the core dump to, “MyCoreDump.xml”. The third
flags whether or not the core dump file is binary. In this case the file is text (.xml) and
not binary (.CDB). The fourth flags whether or not we will be saving away cooked
mesh data. In this case, we won't be saving the cooked data.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
Look over the CoreDump::coreDump() function to see how the scene data is saved.

After hitting “c”, take a look at the “MyCoreDump.xml” file in the “Core_Dump”
directory of the training programs. You'll see all the objects in the simulation listed
there along with their saved positions and velocities.

4 Loading a Core Dump to a Core Container


Hit “v” to load a core dump file to a core container. Hitting “v” sets the bLoadCore flag
which calls LoadCoreDump() which is based on NXU::loadCollection().

NXU::NxuPhysicsCollection* LoadCoreDump(const char * strFileName,


NXU::NXU_FileType fileType = NXU::FT_XML)
{
return NXU::loadCollection(strFileName, fileType);
}

Hitting “v” will load “MyCoreDump.xml” file from the “Core_Dump” directory of the
training programs to the CoreContainer object.

5 Instantiating a Core Dump


Hit “n” to instantiate the core dump loaded to the core containter. Hitting “n” sets the
bInstantiateCore flag which calls InstantiateCoreDump () which is based on
NXU::instantiateCollection().

bool InstantiateCoreDump(NxPhysicsSDK* pSDK, NXU::NxuPhysicsCollection* c)


{
bool success = false;
NXU_userNotify notify;
success = NXU::instantiateCollection( c, *pSDK, NULL, NULL, &notify );
//NXU::releaseCollection(c);
return success;
}

Hitting “n” will now instantiate the CoreContainer object. We loaded this with the
“MyCoreDump.xml” file from the “Core_Dump” directory. So the program will erase
the current scene and reproduce the scene as it existed when you hit “v” to save the core
dump file.

6 Conclusion
The core dump is a means to save away and load back a snapshot of the simulation.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 504 – Scene Export

1 Introduction
In this lesson we use the NxuSteam to save a scene out to a binary file and load it from
the binary file. NxuSteam is provided in source codes.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Scene Export
Look at the new InitNx().

// Include exporter here


#include "NXU_helper.h"

// Physics SDK globals


NxPhysicsSDK* gPhysicsSDK = NULL;

const NxU32 gMaxScenes = 3;


NxScene* gScenes[gMaxScenes] = { NULL, NULL, NULL };
NxU32 gCurrentScene = 0;
...
// Data Directory Paths (solution, binary, install)
char fname[] = "../../../../Data/bin";
char fname1[] = "../../../Data/bin";
char fname2[] = "../../TrainingPrograms/Programs/Data/bin";
char fname3[] = "TrainingPrograms/Programs/Data/bin";...

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

// Create the scenes


for (NxU32 i=0;i<gMaxScenes;++i)
{
gCurrentScene = i;
InitBlankScene();
}
gCurrentScene = 0;

// Set export directory


int set = 0;

#ifdef WIN32

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
set = SetCurrentDirectory(&fname[0]);
if (!set) set = SetCurrentDirectory(&fname1[0]);
if (!set) set = SetCurrentDirectory(&fname2[0]);
if (!set) set = SetCurrentDirectory(&fname3[0]);
#elif LINUX
set = chdir(&fname[0]);
if (set != 0) set = chdir(&fname2[0]);
if (set != 0) set = chdir(&fname3[0]);
#endif

// Get the current time


UpdateTime();

// Start the first frame of the simulation


// Create the scenes
for (NxU32 i=0;i<gMaxScenes;++i)
{
gCurrentScene = i;
StartPhysics(i);
}
gCurrentScene = 0;

// Initialize HUD
InitializeHUD();
InitializeSpecialHUD();
}

We create three blank scenes.

const NxU32 gMaxScenes = 3;


NxScene* gScenes[gMaxScenes] = { NULL, NULL, NULL };
NxU32 gCurrentScene = 0;
...
void InitBlankScene()
{
if (gPhysicsSDK)
{
// Create a blank scene
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScenes[gCurrentScene] = gPhysicsSDK->createScene(sceneDesc);
if(!gScenes[gCurrentScene])
{
sceneDesc.simType =
NX_SIMULATION_SW;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
gScenes[gCurrentScene] = gPhysicsSDK-
>createScene(sceneDesc);
}
NxMaterial* defaultMaterial = gScenes[gCurrentScene]-
>getMaterialFromIndex(0);
defaultMaterial->setRestitution(0);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);

// Create the objects in the scene


CreateGroundPlane();
}
}

Use <space>, “8”, “9”, “0”, to create stacks of objects in Scene 0. Once you have built
the objects for your scene, hit “1” to export the scene.

bool gSave = false;


bool gLoad = false;
bool gClear = false;
...
void KeyboardCallback(unsigned char key, int x, int y)
{
gKeys[key] = true;

switch (key)
{
case ' ':
{
gCreateCube = true;
}
break;

case '8':
{
gCreateStack10 = true;
}
break;

case '9':
{
gCreateStack30 = true;
}
break;

case '0':
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
{
gCreateTower30 = true;
}
break;

case 'v':
{
gCreateCubeCam = true;
}
break;

case '1':
{
gSave = true;
}
break;

case '3':
{
gLoad = 1;
}
break;

case 'c':
{
gClear = true;
}
break;

case 't':
{
// Switch to the next scene
++gCurrentScene;
if (gCurrentScene == gMaxScenes)
{
gCurrentScene = 0;
}
// Add scene number to HUD
char ds[512];
sprintf(ds, "Scene #: %d\n", gCurrentScene);
hud.SetDisplayString(2, ds, 0.015f, 0.92f);
}

default: { break; }
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
...
void ProcessInputs()
{
ProcessForceKeys();

//Check if a request was made to clear, save or load a scene


if (gClear)
{
if (gScenes[gCurrentScene])
{
gPhysicsSDK->releaseScene(*gScenes[gCurrentScene]);
}
InitBlankScene();
gClear = false;
}
else if (gSave)
{
char scratch[512];
sprintf(scratch,"SceneSave%d", gCurrentScene );
SaveScene(scratch);
gSave = false;
}
else if (gLoad)
{
switch ( gLoad )
{
case 1:
if ( 1 )
{
char scratch[512];
sprintf(scratch,"SceneSave%d.nxb", gCurrentScene
); // load the binary saved version.
LoadScene(scratch, NXU::FT_BINARY);
}
break;
case 2:
LoadScene("test.xml", NXU::FT_XML);
break;
case 3:
LoadScene("test.dae", NXU::FT_COLLADA);
break;
case 4:
LoadScene("test.nxb", NXU::FT_BINARY);
break;
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
else if (gCreateCube)
{
CreateCube(NxVec3(0.0f, 20.0f, 0.0f), 1+(rand()&3));
gCreateCube = false;
}
else if (gCreateStack10)
{
CreateStack(10);
gCreateStack10 = false;
}
else if (gCreateStack30)
{
CreateStack(30);
gCreateStack30 = false;
}
else if (gCreateTower30)
{
CreateTower(30);
gCreateTower30 = false;
}
else if (gCreateCubeCam)
{
NxVec3 t = gCameraPos;
NxVec3 Vel = gCameraForward;
Vel.normalize();
Vel *= 200.0f;
CreateCube(t, 8, &Vel);
gCreateCubeCam = false;
}

// ~Physics code

// Show debug wireframes


if (bDebugWireframeMode)
{
NxScene* scene = gScenes[gCurrentScene];
if (scene) gDebugRenderer.renderData(*scene->getDebugRenderable());
}
}

To export your scene, hit “1”. Hitting “1” calls SaveScene(“pdump1.bin”). This is the
heart of where the scene export happens.

//==============================================================
====================

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
// This is the function that calls the exporter to save out the state of the currently selected
scene to a file
void SaveScene(const char *pFilename)
{
char SaveFilename[512];
GetTempFilePath(SaveFilename);
strcat(SaveFilename, pFilename);

NXU::NxuPhysicsCollection *c =
NXU::extractCollectionScene(gScenes[gCurrentScene]);

if (c)
{
char scratch[512];
sprintf(scratch, "%s.xml", SaveFilename);
printf("Saving NxuStream XML file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_XML, false, false);

sprintf(scratch, "%s.nxb", SaveFilename);


printf("Saving NxuStream BINARY file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_BINARY, true, true);

sprintf(scratch, "%s.dae", SaveFilename);


printf("Saving COLLADA Physics 1.4.1 file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_COLLADA, false, false);

NXU::releaseCollection(c);

We have set the output directory to “TrainingPrograms/Programs/Data/bin”, this is where


“pdump1.bin” will be written. Please note that the scene serialization is supported by
NxuStream tool in source codes.

3 Scene Import
Hit “3” to import “pdump1.bin”, the scene you just exported. Hitting “3” calls
LoadScene(“pdump1.bin”). This is the where the scene import happens. It is also based
on NxuSteam.

//==============================================================
====================

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
// This is the function that calls the importer to load the state of the currently selected
scene from a file
NX_BOOL LoadScene(const char *pFilename,NXU::NXU_FileType type)
{
NX_BOOL success = false;

if (gPhysicsSDK)
{
char LoadFilename[512];
FindMediaFile(pFilename, LoadFilename);

NXU::NxuPhysicsCollection *c = NXU::loadCollection(LoadFilename,
type );

if ( c )
{
if (gScenes[gCurrentScene])
{
// Release old scene
releaseScene(*gScenes[gCurrentScene]);
gScenes[gCurrentScene] = 0;
}

if (gPhysicsSDK)
{
success = NXU::instantiateCollection( c, *gPhysicsSDK, 0,
0, &gUserNotify );
}
NXU::releaseCollection(c);
}
else
{
}
}

gLoad = 0;

if ( success )
printf("Scene %d loaded from file %s.\n", gCurrentScene, pFilename);

return success;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
4 Conclusion
You now know how to export scene data to a binary file and import data from the binary
file using NxuSteam.

5 Related Classes, Functions, and Parameters


NXU_errorReport
NXU_userNotify
enum NXU::NXU_FileType
FT_BINARY
FT_XML
FT_COLLADA
NXU namespace
void setErrorReport (NXU_errorReport *er)
NxuPhysicsCollection * loadCollection (const char *fname, NXU_FileType
type, void *mem=0, int len=0)
bool saveCollection (NxuPhysicsCollection *c, const char *fname,
NXU_FileType type, bool save_defaults=true, bool cook=false)
void * saveCollectionToMemory (NxuPhysicsCollection *c, const char
*collectionId, NXU_FileType type, bool save_defaults, bool cook, void *mem,
int len, int &outputLength)
void releaseCollectionMemory (void *mem)
bool instantiateCollection (NxuPhysicsCollection *c, NxPhysicsSDK &sdk,
NxScene *scene, const NxMat34 *rootNode, NXU_userNotify *un)
bool instantiateScene (NxuPhysicsCollection *c, const char *sceneName,
NxPhysicsSDK &sdk, NxScene *scene, NxMat34 *rootNode, NXU_userNotify
*un)
void releaseCollection (NxuPhysicsCollection *c)
bool coreDump (NxPhysicsSDK *sdk, const char *fname, NXU_FileType type,
bool save_defaults=true, bool cook=false, NXU_userNotify *un=0)
NxuPhysicsCollection * extractCollectionSDK (NxPhysicsSDK *sdk, const char
*collectionId=0, NXU_userNotify *un=0)
NxuPhysicsCollection * extractCollectionScene (NxScene *scene, const char
*collectionId=0, const char *sceneId=0)
void setCollectionID (NxuPhysicsCollection &c, const char *collectionId)
NxuPhysicsCollection * createCollection (const char *collectionId=0, const char
*userProperties=0)
bool addParameter (NxuPhysicsCollection &c, NxU32 param, float value)
bool addPhysicsSDK (NxuPhysicsCollection &c, NxPhysicsSDK &sdk)
bool addScene (NxuPhysicsCollection &c, NxScene &s, const char
*userProperties=0, const char *sceneId=0)
bool addEntireScene (NxuPhysicsCollection &c, NxScene &s, const char
*userProperties=0, const char *sceneId=0, NXU_userNotify *un=0)
bool addActor (NxuPhysicsCollection &c, NxActor &a, const char
*userProperties=0, const char *actorId=0)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
10
bool addJoint (NxuPhysicsCollection &c, NxJoint &j, const char
*userProperties=0, const char *jointId=0)
bool addCloth (NxuPhysicsCollection &c, NxCloth &cloth, const char
*userProperties=0, const char *clothId=0)
bool addSoftBody (NxuPhysicsCollection &c, NxSoftBody &softBody, const
char *userProperties=0, const char *softBodyId=0)
bool addSoftBody (NxuPhysicsCollection &c, const ::NxSoftBodyDesc
&softBody, const ::NxSoftBodyMeshDesc &softMesh, const char
*userProperties=0, const char *softBodyId=0)
bool addFluid (NxuPhysicsCollection &c, NxFluid &f, const char
*userProperties=0, const char *fluidId=0)
bool addMaterial (NxuPhysicsCollection &c, NxMaterial &m, const char
*userProperties=0, const char *materialId=0)
bool addConvexMesh (NxuPhysicsCollection &c, NxConvexMesh &mesh,
const char *userProperties=0, const char *convexId=0)
bool addTriangleMesh (NxuPhysicsCollection &c, NxTriangleMesh &mesh,
const char *userProperties=0, const char *meshId=0)
bool addHeightField (NxuPhysicsCollection &c, NxHeightField &hmesh, const
char *userProperties=0, const char *heightFieldId=0)
bool addCCDSkeleton (NxuPhysicsCollection &c, NxCCDSkeleton &skel,
const char *userProperties=0, const char *skeletonId=0)
bool addEffector (NxuPhysicsCollection &c, NxEffector &e, const char
*userProperties=0, const char *effectorId=0)
bool addForceField (NxuPhysicsCollection &c, NxForceField &e, const char
*userProperties=0, const char *forceFieldId=0)
bool addPairFlag (NxuPhysicsCollection &c, NxPairFlag &f, const char
*userProperties=0, const char *pairFlagId=0)
bool addGroupCollisionFlag (NxuPhysicsCollection &c, NxU32 group1, NxU32
group2, bool enable)
bool addActorGroupFlag (NxuPhysicsCollection &c, NxU32 group1, NxU32
group2, NxU32 flags)
bool setFilterOps (NxuPhysicsCollection &c, bool active,::NxFilterOp
filterOp0,::NxFilterOp filterOp1,::NxFilterOp filterOp2, const ::NxGroupsMask
&filterConstant0, const ::NxGroupsMask &filterConstant1)
bool addCompartment (NxuPhysicsCollection &c, NxCompartment &cmp,
const char *compartmentId=0)
bool addClothMesh (NxuPhysicsCollection &c, NxClothMesh &cmp, const char
*clothMeshId=0)
bool addSoftBodyMesh (NxuPhysicsCollection &c, NxSoftBodyMesh &cmp,
const char *softBodyMeshId=0)
bool addSceneInstance (NxuPhysicsCollection &c, const char *instanceId, const
char *sceneName, const NxMat34 &rootNode, bool ignorePlane=true, const char
*userProperties=0)
bool addToCurrentSceneInstance (NxuPhysicsCollection &c, const char
*instanceId, const char *sceneName, const NxMat34 &rootNode, bool
ignorePlane=true, const char *userProperties=0)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
11
bool closeCurrentSceneInstance (NxuPhysicsCollection &c)
const char * getCollectionName (NxuPhysicsCollection &c)
NxU32 getSceneCount (NxuPhysicsCollection &c)
const char * getSceneName (NxuPhysicsCollection &c, NxU32 sceneNo)
NxU32 getSceneInstanceCount (NxuPhysicsCollection &c)
const char * getSceneInstanceName (NxuPhysicsCollection &c, NxU32
sceneNo)
const char * getSceneInstanceSceneName (NxuPhysicsCollection &c, NxU32
sceneNo)
void releasePersistentMemory (void)
void setUseClothActiveState (bool state)
void setUseSoftBodyActiveState (bool state)
void setAutoGenerateCCDSkeletons (bool state, NxReal shrink, NxU32 maxV)
NxU32 createCCDSkeletons (NxuPhysicsCollection &c, NxReal shrink=0.9f,
NxU32 maxV=16)
bool autoGenerateCCDSkeletons (NxActor *actor, NxReal shrink=0.9f, NxU32
maxV=16)
const char ** parseUserProperties (const char *userProperties, unsigned int
&count)
const char * getKeyValue (const char *userProperties, const char *key)
void setEndianMode (bool processorEndian, bool endianSave=false)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
12
Lesson 505 – Asset Export

1 Introduction
In addition to exporting and importing whole scenes, we can export and import scene
assets. In this lesson we use the NxuStream to save a scene out, asset by asset, to a
binary file and load one in, asset by asset, from a binary file.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
Assets can be interspersed in other files to be imported and exported with the same
machinery.

2 Asset Export
Hit “1” to export the scene asset by asset.

// Include exporter here


#include "NXU_helper.h" // NxuStream helper functions.
...
void KeyboardCallback(unsigned char key, int x, int y)
{
gKeys[key] = true;

switch (key)
{
...
case '1':
{
gSave = true;
}
break;
...
}
}
...
void ProcessInputs()
{
...
else if (gSave)
{
scratch[512];
sprintf(scratch,"SceneSave%d", gCurrentScene );
SaveScene(scratch);
gSave = false;
}
...
}
...

// This is the function that calls the exporter to save out the state of the currently selected
scene to a file
void SaveScene(const char *pFilename)
{
if ( gScenes[gCurrentScene] )

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
{
NxScene *scene = gScenes[gCurrentScene];
char SaveFilename[512];
GetTempFilePath(SaveFilename);
strcat(SaveFilename, pFilename);

NXU::NxuPhysicsCollection *c = NXU::createCollection();

if (c)
{

// this code demonstrates how to build a collection of just individual


actors, joints, or mesh data.

#if NXU_SAVE_MESHES
saveMeshes(c,scene);
#endif

#if NXU_SAVE_SCENE_DESC
saveSceneDesc(c,scene); // save the scene
descriptor
#endif

#if NXU_SAVE_GROUP_FLAGS
saveGroupFlags(c,scene);
#endif

#if NXU_SAVE_MATERIALS
saveMaterials(c,scene); // save the materials
#endif

#if NXU_SAVE_COMPARTMENTS && NX_SDK_VERSION_NUMBER >=


260
saveCompartments(c,scene);
#endif

#if NXU_SAVE_ACTORS
saveActors(c,scene); // save the actors
#endif

#if NXU_SAVE_JOINTS
saveJoints(c,scene); // save the joints
#endif

#if NXU_SAVE_PAIR_FLAGS // save body pair flags, collision filters, etc.


etc.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
savePairFlags(c,scene);
#endif

#if NXU_SAVE_FLUIDS && NX_USE_FLUID_API


saveFluids(c,scene); // save the fluids
#endif

#if NXU_SAVE_CLOTHES
saveClothes(c,scene); // save the clothes
#endif

#if NXU_SAVE_EFFECTORS
saveEffectors(c,scene); // save the effectors.
#endif

char scratch[512];
sprintf(scratch, "%s.xml", SaveFilename);
printf("Saving NxuStream XML file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_XML, false, false);

sprintf(scratch, "%s.nxb", SaveFilename);


printf("Saving NxuStream BINARY file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_BINARY, true, true);

sprintf(scratch, "%s.dae", SaveFilename);


printf("Saving COLLADA Physics 1.4.1 file to '%s'\r\n", scratch);
NXU::saveCollection(c, scratch, NXU::FT_COLLADA, false, false);

NXU::releaseCollection(c);

}
}
}

This will write out your scene to “pdump1.bin”, actor by actor, using
NxuPhysicsExport::Write().

3 Asset Import
Hit “3” to import “pdump1.bin” asset by asset.

void KeyboardCallback(unsigned char key, int x, int y)


{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
gKeys[key] = true;

switch (key)
{
...
case '3':
{
gLoad = true;
}
break;
...
}
}
...
void ProcessInputs()
{
...
else if (gLoad)
{

char scratch[512];
sprintf(scratch,"SceneSave%d.nxb", gCurrentScene ); // load the binary saved
version.
LoadScene(scratch, NXU::FT_BINARY);

}
...
}
...

// This is the function that calls the importer to load the state of the currently selected
scene from a file
NX_BOOL LoadScene(const char *pFilename,NXU::NXU_FileType type)
{
NX_BOOL success = false;

if (gPhysicsSDK)
{
char LoadFilename[512];
FindMediaFile(pFilename, LoadFilename);

NXU::NxuPhysicsCollection *c = NXU::loadCollection(LoadFilename,
type );

if ( c )
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
if (gScenes[gCurrentScene])
{
// Release old scene
releaseScene(*gScenes[gCurrentScene]);
gScenes[gCurrentScene] = 0;
}

if (gPhysicsSDK)
{
success = NXU::instantiateCollection( c, *gPhysicsSDK, 0,
0, &gUserNotify );
}
NXU::releaseCollection(c);
}
else
{
}
}

gLoad = 0;

if ( success )
printf("Scene %d loaded from file %s.\n", gCurrentScene, pFilename);

return success;
}

4 Conclusion
We can use our NxuStream to import and export scenes wholesale. Or we can import
and export PhysX asset singly as we have done in this lesson.

5 Related Classes, Functions, and Parameters


NXU_errorReport
NXU_userNotify
enum NXU::NXU_FileType
FT_BINARY
FT_XML
FT_COLLADA
NXU namespace
void setErrorReport (NXU_errorReport *er)
NxuPhysicsCollection * loadCollection (const char *fname, NXU_FileType
type, void *mem=0, int len=0)
bool saveCollection (NxuPhysicsCollection *c, const char *fname,
NXU_FileType type, bool save_defaults=true, bool cook=false)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
void * saveCollectionToMemory (NxuPhysicsCollection *c, const char
*collectionId, NXU_FileType type, bool save_defaults, bool cook, void *mem,
int len, int &outputLength)
void releaseCollectionMemory (void *mem)
bool instantiateCollection (NxuPhysicsCollection *c, NxPhysicsSDK &sdk,
NxScene *scene, const NxMat34 *rootNode, NXU_userNotify *un)
bool instantiateScene (NxuPhysicsCollection *c, const char *sceneName,
NxPhysicsSDK &sdk, NxScene *scene, NxMat34 *rootNode, NXU_userNotify
*un)
void releaseCollection (NxuPhysicsCollection *c)
bool coreDump (NxPhysicsSDK *sdk, const char *fname, NXU_FileType type,
bool save_defaults=true, bool cook=false, NXU_userNotify *un=0)
NxuPhysicsCollection * extractCollectionSDK (NxPhysicsSDK *sdk, const char
*collectionId=0, NXU_userNotify *un=0)
NxuPhysicsCollection * extractCollectionScene (NxScene *scene, const char
*collectionId=0, const char *sceneId=0)
void setCollectionID (NxuPhysicsCollection &c, const char *collectionId)
NxuPhysicsCollection * createCollection (const char *collectionId=0, const char
*userProperties=0)
bool addParameter (NxuPhysicsCollection &c, NxU32 param, float value)
bool addPhysicsSDK (NxuPhysicsCollection &c, NxPhysicsSDK &sdk)
bool addScene (NxuPhysicsCollection &c, NxScene &s, const char
*userProperties=0, const char *sceneId=0)
bool addEntireScene (NxuPhysicsCollection &c, NxScene &s, const char
*userProperties=0, const char *sceneId=0, NXU_userNotify *un=0)
bool addActor (NxuPhysicsCollection &c, NxActor &a, const char
*userProperties=0, const char *actorId=0)
bool addJoint (NxuPhysicsCollection &c, NxJoint &j, const char
*userProperties=0, const char *jointId=0)
bool addCloth (NxuPhysicsCollection &c, NxCloth &cloth, const char
*userProperties=0, const char *clothId=0)
bool addSoftBody (NxuPhysicsCollection &c, NxSoftBody &softBody, const
char *userProperties=0, const char *softBodyId=0)
bool addSoftBody (NxuPhysicsCollection &c, const ::NxSoftBodyDesc
&softBody, const ::NxSoftBodyMeshDesc &softMesh, const char
*userProperties=0, const char *softBodyId=0)
bool addFluid (NxuPhysicsCollection &c, NxFluid &f, const char
*userProperties=0, const char *fluidId=0)
bool addMaterial (NxuPhysicsCollection &c, NxMaterial &m, const char
*userProperties=0, const char *materialId=0)
bool addConvexMesh (NxuPhysicsCollection &c, NxConvexMesh &mesh,
const char *userProperties=0, const char *convexId=0)
bool addTriangleMesh (NxuPhysicsCollection &c, NxTriangleMesh &mesh,
const char *userProperties=0, const char *meshId=0)
bool addHeightField (NxuPhysicsCollection &c, NxHeightField &hmesh, const
char *userProperties=0, const char *heightFieldId=0)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
bool addCCDSkeleton (NxuPhysicsCollection &c, NxCCDSkeleton &skel,
const char *userProperties=0, const char *skeletonId=0)
bool addEffector (NxuPhysicsCollection &c, NxEffector &e, const char
*userProperties=0, const char *effectorId=0)
bool addForceField (NxuPhysicsCollection &c, NxForceField &e, const char
*userProperties=0, const char *forceFieldId=0)
bool addPairFlag (NxuPhysicsCollection &c, NxPairFlag &f, const char
*userProperties=0, const char *pairFlagId=0)
bool addGroupCollisionFlag (NxuPhysicsCollection &c, NxU32 group1, NxU32
group2, bool enable)
bool addActorGroupFlag (NxuPhysicsCollection &c, NxU32 group1, NxU32
group2, NxU32 flags)
bool setFilterOps (NxuPhysicsCollection &c, bool active,::NxFilterOp
filterOp0,::NxFilterOp filterOp1,::NxFilterOp filterOp2, const ::NxGroupsMask
&filterConstant0, const ::NxGroupsMask &filterConstant1)
bool addCompartment (NxuPhysicsCollection &c, NxCompartment &cmp,
const char *compartmentId=0)
bool addClothMesh (NxuPhysicsCollection &c, NxClothMesh &cmp, const char
*clothMeshId=0)
bool addSoftBodyMesh (NxuPhysicsCollection &c, NxSoftBodyMesh &cmp,
const char *softBodyMeshId=0)
bool addSceneInstance (NxuPhysicsCollection &c, const char *instanceId, const
char *sceneName, const NxMat34 &rootNode, bool ignorePlane=true, const char
*userProperties=0)
bool addToCurrentSceneInstance (NxuPhysicsCollection &c, const char
*instanceId, const char *sceneName, const NxMat34 &rootNode, bool
ignorePlane=true, const char *userProperties=0)
bool closeCurrentSceneInstance (NxuPhysicsCollection &c)
const char * getCollectionName (NxuPhysicsCollection &c)
NxU32 getSceneCount (NxuPhysicsCollection &c)
const char * getSceneName (NxuPhysicsCollection &c, NxU32 sceneNo)
NxU32 getSceneInstanceCount (NxuPhysicsCollection &c)
const char * getSceneInstanceName (NxuPhysicsCollection &c, NxU32
sceneNo)
const char * getSceneInstanceSceneName (NxuPhysicsCollection &c, NxU32
sceneNo)
void releasePersistentMemory (void)
void setUseClothActiveState (bool state)
void setUseSoftBodyActiveState (bool state)
void setAutoGenerateCCDSkeletons (bool state, NxReal shrink, NxU32 maxV)
NxU32 createCCDSkeletons (NxuPhysicsCollection &c, NxReal shrink=0.9f,
NxU32 maxV=16)
bool autoGenerateCCDSkeletons (NxActor *actor, NxReal shrink=0.9f, NxU32
maxV=16)
const char ** parseUserProperties (const char *userProperties, unsigned int
&count)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
const char * getKeyValue (const char *userProperties, const char *key)
void setEndianMode (bool processorEndian, bool endianSave=false)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
Lesson601_Forcefield_Creation

1 Introduction
Forcefields are SDK objects akin to actors, which affect cloth, soft bodies, fluid and rigid
bodies that enter their area of influence. Force fields allow you to implement for example
gusts of wind, dust devils, vacuum cleaners or anti-gravity zones.

Several factors should be concerned about in creating a Forcefield:


• Forcefield shapes, which determine the volume of space in which the force field is
active.
• Forcefield kernels, which define how to computes a force and torque on a particle
(or volume element or rigid body) with a particular position and linear velocity.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
• Force field Coordinate Systems. You can use different kinds of coordinate
systems to help creating various effects.
• Forcefield scaling. The force values obtained from the kernel are then scaled.
Each kind of object (fluid, cloth, soft body, rigid body) has its own scaling
properties

This lesson focuses on Forcefield shapes and ShapeGroups, and teaches you how to
create a simple forcefield with several Forcefield shapes.

2 Forcefield shapes and shape group


As with actor shapes, force field shapes come in different types; for force fields, the
available shapes are:
• Sphere
• Capsule
• Box
• Convex mesh
Unlike actors, you can specify some of the shape groups as exclude groups using the flag
NX_FFSG_EXCLUDE_GROUP. Thus, the actual volume used is the union of all
include shapes' volumes, minus the union of all exclude shapes' volumes. This provides a
flexible means of shaping the force field just like you want it, with dead zones, irregular
edges and the like.
A shape group is simply a collection of force field shapes. The rationale for shape groups
is that a given force field (say, one that causes particles to swirl around an actor) may be
instanced multiple times, once for each of a collection of actors - but each field is likely
to have the same set of exclude shapes. To free the application from having to add this
collection to each field it creates, a group of exclude shapes may be created which may
then be attached to several fields.
The pose of the shapes in a force field group is relative to the world frame. The group
flag (NX_FFSG_EXCLUDE_GROUP) defines whether all shapes inside this group are
exclude volumes or include volumes. There is one special include group per force field
which cannot be shared among other force fields and which is moving with the force
field. The pose of the shapes in this include group is relative to the force field pose and
the shapes are include volumes only.

3 Creating a simple Forcefield


The following code segment shows you how to create a simple Forcefield. There are 2
shape groups in this forcefield. The left shape group contains 1 box shape, 1 capsule
shape, and 1 sphere shape. The right contains 1 box shape and 1 capsule shape, and its
flag is set to NX_FFSG_EXCLUDE_GROUP. Therefore, no force is applied on the right
box.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
NxForceField* CreateForcefield()
{
// Create a Forcefield kernel
NxForceFieldLinearKernelDesc linearKernelDesc;
linearKernelDesc.constant = NxVec3(0.0f,19.6f,0.0f);
NxForceFieldLinearKernel* pLinearKernel;
pLinearKernel = gScene->createForceFieldLinearKernel(linearKernelDesc);

// The Forcefield descriptor


NxForceFieldDesc fieldDesc;
fieldDesc.kernel = pLinearKernel;
fieldDesc.rigidBodyType = NX_FF_TYPE_GRAVITATIONAL;

// A box forcefield shape descriptor


NxBoxForceFieldShapeDesc box;
box.dimensions.set(2, 2, 2);
box.pose.t.set(6, 2, 0);

// Create a Forcefield
// There is one special include group per force field which cannot be shared among other force
fields and which is moving with the force field.
// The pose of the shapes in this include group is relative to the force field pose and the shapes are
include volumes only.
// The special include group contains a box shape
fieldDesc.includeGroupShapes.push_back(&box);
NxForceField *pForceField;
pForceField = gScene->createForceField(fieldDesc);

// A capsule forcefield shape descriptor


NxCapsuleForceFieldShapeDesc capsule;
capsule.height = 1;
capsule.radius = 2.5f;
capsule.pose.t.set(6, 9, 0);

// A sphere forcefield shape descriptor


NxSphereForceFieldShapeDesc sphere;
sphere.radius = 4;
sphere.pose.t.set(6, 24, 0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
// Add two shapes to the first shape group
NxForceFieldShapeGroupDesc sgDesc1;
sgDesc1.shapes.pushBack(&capsule);
sgDesc1.shapes.pushBack(&sphere);
NxForceFieldShapeGroup* pIncludeGroup;
pIncludeGroup = gScene->createForceFieldShapeGroup(sgDesc1);

box.pose.t.set(-6, 2, 0);
capsule.pose.t.set(-6, 9, 0);

// Add two shapes to the second shape group


// The group flag (NX_FFSG_EXCLUDE_GROUP) defines that all shapes inside this group are
exclude volumes.
NxForceFieldShapeGroupDesc sgDesc2;
sgDesc2.flags = NX_FFSG_EXCLUDE_GROUP;
sgDesc2.shapes.pushBack(&box);
sgDesc2.shapes.pushBack(&capsule);
NxForceFieldShapeGroup* pExcludeGroup;
pExcludeGroup = gScene->createForceFieldShapeGroup(sgDesc2);

// Add these two shape groups to the forcefield


pForceField->addShapeGroup(*pIncludeGroup);
pForceField->addShapeGroup(*pExcludeGroup);

return pForceField;
}

4 Conclusion
You have created your first Forcefield. Congratulations! Now, Please watch the boxes in
the Forcefield. The left box moves up and down, since it is subjected to the gravity and
the force from the Forcefield. The right box is keeping static, and it is because it is
located in the Forcefield shapes whose flags are set to NX_FFSG_EXCLUDE_GROUP.

5 Related Classes, Functions, and Parameters


NxForceFieldDesc
NxMat34 pose
NxActor * actor
NxForceFieldCoordinates coordinates
NxArray< NxForceFieldShapeDesc * > includeGroupShapes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
NxArray< NxForceFieldShapeGroup * > shapeGroups
NxCollisionGroup group
NxGroupsMask groupsMask
NxForceFieldKernel * kernel
NxForceFieldVariety forceFieldVariety
NxForceFieldType fluidType
NxForceFieldType clothType
NxForceFieldType softBodyType
NxForceFieldType rigidBodyType
NxU32 flags
const char * name
void * userData

NxForceFieldShapeDesc
NxMat34 pose
void * userData
const char * name

NxBoxForceFieldShapeDesc

NxCapsuleForceFieldShapeDesc

NxConvexForceFieldShapeDesc

NxSphereForceFieldShapeDesc

NxForceFieldShapeGroupDesc
NxU32 flags
NxArray< NxForceFieldShapeDesc * > shapes
const char * name
void * userData

NxForceFieldShapeGroupFlags
NX_FFSG_EXCLUDE_GROUP

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson602_Forcefield_Coordinate

1 Forcefield coordinate systems


The force field does support different kinds of coordinate systems to help creating
various effects with the linear kernel and to simplify custom kernel code. The force field
class transforms the position and velocity of intersecting objects (rigid body, fluid, cloth,
softbody) into the selected coordinate system and the force field's local frame. Those two
variables are then passed to the force field kernel function as input parameters.
The variables are provided in the local, "tangential" coordinate system of the force field
at the position of the object. For the coordinate type NX_FFC_CARTESIAN this is

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
equivalent to the force field's own frame; for the other types the local axes shift
depending on position:
• NX_FFC_SPHERICAL: The X axis is directed outwards from the center of the
sphere; the Y and Z axes are the tangents of the spherical surface.
• NX_FFC_CYLINDRICAL: The X axis is directed outwards from the cylinder
axis. The Y axis is the cylinder axis, and the Z axis is the cylindrical surface
tangent (directed to make XYZ a right-handed system).
• NX_FFC_TOROIDAL: The X axis is directed outwards from the closest point on
the torus' equator; the Y axis is the tangent of the equator at its closest point
(directed in the clockwise direction as seen from the direction of the torus' axis),
and the Z axis is normal to both forming a right-handed system XYZ.

2 Creating Forcefields with different coordinate systems


The following code segment shows you how to use coordinate systems. There are 4
forcefields in the scene with CARTESIAN, SPHERICAL, CYLINDRICAL and
TOROIDAL, respectively.

void CreateForcefield()
{
// Create a Forcefield kernel
NxForceFieldLinearKernelDesc linearKernelDesc;
linearKernelDesc.constant = NxVec3(1.0f,-1.0f,-1.0f);
NxForceFieldLinearKernel* pLinearKernel;
pLinearKernel = gScene->createForceFieldLinearKernel(linearKernelDesc);

// A box forcefield shape descriptor


NxBoxForceFieldShapeDesc box;
box.dimensions.set(3, 3, 3);

// Create the 1st forcefield using NX_FFC_CARTESIAN Coordinate


// The force field class transforms the position and velocity of intersecting objects into the selected
coordinate system and the force field's local frame.
NxForceFieldDesc fieldDesc1;
fieldDesc1.kernel = pLinearKernel;
fieldDesc1.coordinates = NX_FFC_CARTESIAN;
box.pose.t.set(12, 3, 0);
fieldDesc1.includeGroupShapes.push_back(&box);
gScene->createForceField(fieldDesc1);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
// Create the 2nd forcefield using NX_FFC_SPHERICAL Coordinate
// NX_FFC_SPHERICAL: The X axis is directed outwards from the center of the sphere; the Y and
Z axes are the tangents of the spherical surface. The y and z components of the p and v vectors in the
function are always 0, and target position/velocity in these directions is ignored.
NxForceFieldDesc fieldDesc2;
fieldDesc2.kernel = pLinearKernel;
fieldDesc2.coordinates = NX_FFC_SPHERICAL;
box.pose.t.set(4, 3, 0);
fieldDesc2.includeGroupShapes.push_back(&box);
gScene->createForceField(fieldDesc2);

// Create the 3rd forcefield using NX_FFC_CYLINDRICAL Coordinate


// NX_FFC_CYLINDRICAL: The X axis is directed outwards from the cylinder axis. The Y axis is the
cylinder axis, and the Z axis is the cylindrical surface tangent (directed to make XYZ a right-handed system).
The z component of the p vector in the function is always 0, and the target position in this direction is
ignored.
NxForceFieldDesc fieldDesc3;
fieldDesc3.kernel = pLinearKernel;
fieldDesc3.coordinates = NX_FFC_CYLINDRICAL;
box.pose.t.set(-4, 3, 0);
fieldDesc3.includeGroupShapes.push_back(&box);
gScene->createForceField(fieldDesc3);

// Create the 4th forcefield using NX_FFC_TOROIDAL Coordinate


// NX_FFC_TOROIDAL: The X axis is directed outwards from the closest point on the torus'
equator; the Y axis is the tangent of the equator at its closest point (directed in the clockwise direction as
seen from the direction of the torus' axis), and the Z axis is normal to both forming a right-handed system
XYZ. See the figure below. The y and z components of the p vector in the function are always 0 and the
target position in these directions is ignored. Note that this coordinate system is only available for linear
kernels!
NxForceFieldDesc fieldDesc4;
fieldDesc4.kernel = pLinearKernel;
fieldDesc4.coordinates = NX_FFC_TOROIDAL;
box.pose.t.set(-12, 3, 0);
fieldDesc4.includeGroupShapes.push_back(&box);
gScene->createForceField(fieldDesc4);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
3 Conclusion
You have finished this lesson. Great works! Now, please run the program, and you can
find the different behaviors among 4 boxes in Forcefields with different coordinate
systems.

4 Related Classes, Functions, and Parameters


NxForceFieldDesc
NxMat34 pose
NxActor * actor
NxForceFieldCoordinates coordinates
NxArray< NxForceFieldShapeDesc * > includeGroupShapes
NxArray< NxForceFieldShapeGroup * > shapeGroups
NxCollisionGroup group
NxGroupsMask groupsMask
NxForceFieldKernel * kernel
NxForceFieldVariety forceFieldVariety
NxForceFieldType fluidType
NxForceFieldType clothType
NxForceFieldType softBodyType
NxForceFieldType rigidBodyType
NxU32 flags
const char * name
void * userData

NxForceFieldCoordinates
NX_FFC_CARTESIAN
NX_FFC_SPHERICAL
NX_FFC_CYLINDRICAL
NX_FFC_TOROIDAL

NxForceFieldShapeDesc

NxBoxForceFieldShapeDesc

NxCapsuleForceFieldShapeDesc

NxConvexForceFieldShapeDesc

NxSphereForceFieldShapeDesc

NxForceFieldShapeGroupDesc

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson603_Forcefield_Kernels

1 Forcefield kernels
The kernel is the function which computes a force and torque on a particle (or volume
element or rigid body) with a particular position and linear velocity. The particle has to
be inside the force field's activity volume defined by the shape groups. The force fields
operate either with a linear kernel or a custom kernel.
The linear kernel is calculated through the following formula:

fi = [K + M p(P-p) + M v(V-v) + N a noise ] i / [1 + li |P i -p i | + q i (P i -p i )2]


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
• f is the resulting force (or acceleration, see below) on the particle
• K is the constant part, from NxForceFieldLinearKernelDesc.constant
• p is the position of the object, in the force field coordinate system
• M p is the position dependence matrix, from
NxForceFieldLinearKernelDesc.positionMultiplier
• P is the position target, from NxForceFieldLinearKernelDesc.positionTarget
• v is the velocity of the object, in the force field coordinate system
• M v is the velocity dependence matrix, from
NxForceFieldLinearKernelDesc.velocityMultiplier
• V is the velocity target, from NxForceFieldLinearKernelDesc.velocityTarget
• a noise is the noise multiplier, from NxForceFieldLinearKernelDesc.noise
• N is a 3x3 matrix whose diagonal elements are three random numbers in the range
[0, 1]
• l is the linear falloff term, from NxForceFieldLinearKernelDesc.falloffLinear
• q is the quadratic falloff term, from
NxForceFieldLinearKernelDesc.falloffQuadratic
In words, the SDK calculates the difference between actual and target position/velocity,
and uses the matrices M p and M v to calculate the response to these differences.
The falloff terms are used to attenuate the magnitude of the force with the distance to the
target position.
The SDK arrow user to defined a custom kernel for convenient usage. A header file
named "NxForceFieldKernelDefs.h" has to be included when using custom kernel. For
more details, please refer to PhysX Documentation.

2 Using Forcefield kernels


• The following code segment shows you how to create a linear kernel.
NxForceFieldLinearKernel* CreateFFLinearKernel()
{
// Create a Linear kernel
NxMat33 m;
m.id();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
NxForceFieldLinearKernelDesc lKernelDesc;
lKernelDesc.positionMultiplier = m;
lKernelDesc.positionTarget = NxVec3(0, 3.5, 0);

gLinearKernel = gScene->createForceFieldLinearKernel(lKernelDesc);
return gLinearKernel;
}

• The following code segment shows you how to create a custom kernel.
//define a custom forcefield---in Lesson603.h
#include "NxForceFieldKernelDefs.h"
NX_START_FORCEFIELD(Custom)
NxVConst(InitForce);
NX_START_FUNCTION
// If an object's velocity is zero, give it the initial force
force = InitForce;
NxVec3 v = Velocity;
NxFinishIf(v == NxVec3(0,0,0));

// While the object is moving, give it the force that is relative to its position
NxVec3 p = Position;
force = NxVec3(0,3.5,0) - p;
NX_END_FUNCTION
NX_END_FORCEFIELD(Custom)

NxForceFieldKernelCustom* CreateFFCustomKernel()//---in Lesson603.cpp


{
// Create a Custom Kernel
gCustomKernel = new NxForceFieldKernelCustom;
gCustomKernel->setInitForce(NxVec3(0, 0, 500));
return gCustomKernel;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
3 Conclusion
Good! You have learned how to create a linear kernel and a custom kernel. In this
program, you can switch between linear kernel and custom kernel by pressing “c” key.

4 Related Classes, Functions, and Parameters


NxForceFieldDesc
NxMat34 pose
NxActor * actor
NxForceFieldCoordinates coordinates
NxArray< NxForceFieldShapeDesc * > includeGroupShapes
NxArray< NxForceFieldShapeGroup * > shapeGroups
NxCollisionGroup group
NxGroupsMask groupsMask
NxForceFieldKernel * kernel
NxForceFieldVariety forceFieldVariety
NxForceFieldType fluidType
NxForceFieldType clothType
NxForceFieldType softBodyType
NxForceFieldType rigidBodyType
NxU32 flags
const char * name
void * userData

NxForceFieldLinearKernelDesc
NxVec3 constant
NxMat33 positionMultiplier
NxVec3 positionTarget
NxMat33 velocityMultiplier
NxVec3 velocityTarget
NxReal torusRadius
NxVec3 falloffLinear
NxVec3 falloffQuadratic
NxVec3 noise
const char * name
void * userData

NxForceFieldKernel

NxForceFieldShapeDesc

NxBoxForceFieldShapeDesc

NxForceFieldShapeGroupDesc

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson604_Forcefield_Scaling

1 Forcefield scaling
The force value obtained from the kernel are then scaled. Each kind of object (fluid,
cloth, soft body, rigid body) has its own scaling properties. Conceptually scaling depends
on two orthogonal criteria: resolution independence (a property of the simulation) and
charge value (a property of the way the field acts upon the object). Hence there are two
scaling properties:
• Field Type: An integer defining the field type per feature:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
o NX_FF_TYPE_GRAVITATIONAL: Scales the force by the mass of the
particle or body.
o NX_FF_TYPE_OTHER: Does not scale the value from the force field.
o NX_FF_TYPE_NO_INTERACTION: Does disable force field interaction
with a specific feature.

• Volumetric Scaling: A flag which indicates whether the force is scaled by the
amount of volume represented by the object if NX_FF_TYPE_OTHER is
selected. This is true by default for all features. This allows fields to act on cloth,
fluids and soft bodies to be independent of the discretization.
o NX_FFF_VOLUMETRIC_SCALING_FLUID: Flag to enable volumetric
scaling for fluids.
o NX_FFF_VOLUMETRIC_SCALING_CLOTH: Flag to enable volumetric
scaling for cloths.
o NX_FFF_VOLUMETRIC_SCALING_SOFTBODY: Flag to enable
volumetric scaling for softbodies.
o NX_FFF_VOLUMETRIC_SCALING_RIGIDBODY: Flag to enable
volumetric scaling for rigid bodies.

• Scaling Table: In addition there is a scaling table where the force field's variety
and the feature's force field material index are used to index the table.

2 Using scaling Table


• The following code segment shows you how to use scaling table.

// Forcefield, ForceField Material, and ForceField Variety


NxForceField* gForceField;
NxForceFieldVariety gVar1;
NxForceFieldMaterial gMat1;
NxForceFieldVariety gVar2;

// Scaling table initiating


void InitScalingTable()
{
// create a new ForceField Variety index
gVar1 = gScene->createForceFieldVariety();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
gVar2 = gScene->createForceFieldVariety();

// create a new ForceField Material index (index 0 is already created)


gMat1 = gScene->createForceFieldMaterial();

// change the scaling for the created pair


gScene->setForceFieldScale(gVar1, 0, 0.1f);
gScene->setForceFieldScale(gVar1, gMat1, 2.0f);
gScene->setForceFieldScale(gVar2, 0, 8.0f);
gScene->setForceFieldScale(gVar2, gMat1, 40.0f);
}

//press ‘c’ key to call this function to switch scaling


void ChangeScaling()
{
// Reset the box
NxMat33 m;
m.id();
box1->setGlobalOrientation(m);
box1->setGlobalPosition(NxVec3(4,3.5,0));
box1->setLinearVelocity(NxVec3(0,0,0));
box1->setAngularVelocity(NxVec3(0,0,0));

switch(gCounter%4)
{
case 0:
// change the variety of a force field
gForceField->setForceFieldVariety(gVar1);
// change the material of an object (box)
box1->setForceFieldMaterial(0);
hud.SetDisplayString(2, "forcefiedl scaling is 0.1\n", 0.14f, 0.92f);
break;
case 1:
// change the variety of a force field
gForceField->setForceFieldVariety(gVar1);
// change the material of an object (box)
box1->setForceFieldMaterial(gMat1);
hud.SetDisplayString(2, "forcefiedl scaling is 2.0\n", 0.14f, 0.92f);
break;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
case 2:
// change the variety of a force field
gForceField->setForceFieldVariety(gVar2);
// change the material of an object (box)
box1->setForceFieldMaterial(0);
hud.SetDisplayString(2, "forcefiedl scaling is 8.0\n", 0.14f, 0.92f);
break;
case 3:
// change the variety of a force field
gForceField->setForceFieldVariety(gVar2);
// change the material of an object (box)
box1->setForceFieldMaterial(gMat1);
hud.SetDisplayString(2, "forcefiedl scaling is 40.0\n", 0.14f, 0.92f);
break;
}

gCounter++;
}

3 Conclusion
Congratulations! You have finished the last lesson. In this program, you can switch
among different scaling levels by pressing “c” key.

4 Related Classes, Functions, and Parameters


NxForceFieldDesc
NxMat34 pose
NxActor * actor
NxForceFieldCoordinates coordinates
NxArray< NxForceFieldShapeDesc * > includeGroupShapes
NxArray< NxForceFieldShapeGroup * > shapeGroups
NxCollisionGroup group
NxGroupsMask groupsMask
NxForceFieldKernel * kernel
NxForceFieldVariety forceFieldVariety
NxForceFieldType fluidType
NxForceFieldType clothType
NxForceFieldType softBodyType
NxForceFieldType rigidBodyType
NxU32 flags
const char * name
void * userData
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
NxForceFieldLinearKernelDesc

NxForceFieldShapeDesc

NxBoxForceFieldShapeDesc

NxForceFieldShapeGroupDesc

NxActorDesc

NxCloth

NxFluid

NxScene

NxSoftBody

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson 701 – Wheel Shapes

1 Introduction
In this chapter, we learn how to construct vehicles. The basic building blocks of a
vehicle are rigid body shapes (box, sphere, capsule, convex) for its chassis, and wheel
shapes for its wheels. The wheel shape is a shape specially constructed for vehicles. It is
an entity that “rolls” (actually, “slides”) on the ground with a certain friction and
springiness. You can get it to supply forces to (“drive”) an actor it is attached to

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
depending on its contact with the ground and other objects. You can steer it and brake it
as well.

In this lesson, we will build and actor from one rigid body shape and one wheel.

2 Wheel Shapes
We recently introduced an all new "Wheel Shape", NxWheelShape, to complement the
current list of plane, box, sphere, capsule, convex, and trimesh shapes. This shape is a
special shape used for simulating a car wheel. You can still use the capsule method
described in the 2.2 “Lesson 309: Raycast Car”, but once we release 2.3.1, it is
recommended you move over to the new shape.

NxWheelShape has a radius, width, suspension travel distance, and suspension spring.
Suspension and contact of the wheel with the ground works like this. The -Y axis of the
wheel shape should be directed toward the ground. A ray is cast from the shape's origin
along the -Y axis. When the ray strikes something, and the distance is:

1. Less than wheelRadius from the shape origin: a hard contact is created
2. Between wheelRadius and (suspensionTravel + wheelRadius): a soft suspension
contact is created
3. Greater than (suspensionTravel + wheelRadius): no contact is created.

Thus at the point of greatest possible suspension compression the wheel axle will pass
through at the shape's origin. At the point greatest suspension extension the wheel axle
will be a distance of suspensionTravel from the shape's origin.

The suspension's targetValue is 0 for real cars, which means that the suspension tries to
extend all the way. Otherwise one can specify values [0,1] for suspensions which have a
spring to pull the wheel up when it is extended too far. 0.5 will then fall halfway along
suspensionTravel.

The +Z axis is the 'forward' direction of travel for the wheel. -Z is backwards. The the
wheel rolls forward when rotating around the positive direction around the X axis.

A positive wheel steering angle corresponds to a positive rotation around the shape's Y
axis. (Castor angles are not modeled.)

The coordinate frame of the shape is rigidly fixed on the car.

The wheel shape also has a notion of wheel mass, steer angle, axle speed, motor torque,
and brake torque.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
3 Adding a Wheel Shape to an Actor
In InitNx(), we create a few rigid body objects and finally a rigid body box with a wheel
attached.

// Actor globals
extern NxActor* groundPlane;
extern NxActor* box;
extern NxActor* sphere;
extern NxActor* capsule;
extern NxActor* cab;

// Wheel globals
NxWheel* wheel = NULL;
...
void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);
...
// Create the objects in the scene
groundPlane = CreateGroundPlane();

box = CreateBox(NxVec3(5,0,0), NxVec3(0.5,1,0.5), 20);


sphere = CreateSphere(NxVec3(0,0,5), 1, 10);
capsule = CreateCapsule(NxVec3(-5,0,0), 2, 0.5, 10);

cab = CreateCab(NxVec3(0,0,0));

gSelectedActor = cab;
gForceStrength = 35000;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
// Turn on all contact notifications
gScene->setActorGroupPairFlags(0, 0, NX_NOTIFY_ON_TOUCH);

// Get the current time
UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

Before we get to the wheel creation in CreateCab(), notice the two new visualization
parameters we have added.

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);

NX_VISUALIZE_CONTACT_POINT sets up contact points for visualization.


NX_VISUALIZE_CONTACT_NORMAL draws the normals to these points as well.

// Turn on all contact notifications


gScene->setActorGroupPairFlags(0, 0, NX_NOTIFY_ON_TOUCH);

This call to NxScene::setActorGroupPairFlags(), sets up all actor pairs to send their


contacts to the User Contact Report.

In CreateCab(), we build an actor from a box shape and a wheel shape.

NxActor* CreateCab(const NxVec3& pos)


{
NxActor* actor = CreateBox(pos + NxVec3(0,1,0), NxVec3(1,0.5,0.5), 10);
actor->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
actor->raiseBodyFlag(NX_BF_FROZEN_ROT_Z);
actor->setAngularDamping(0.5);

NxWheelDesc wheelDesc;
// wheelDesc.wheelAxis.set(0,0,1);
// wheelDesc.downAxis.set(0,-1,0);
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.1;
wheelDesc.wheelSuspension = 0.2;
wheelDesc.springRestitution = 7000;
wheelDesc.springDamping = 800;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
wheelDesc.frictionToFront = 0.1;
wheelDesc.frictionToSide = 0.99;
wheelDesc.position = NxVec3(0,-0.4,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT |
NX_WF_BUILD_LOWER_HALF;

AddWheelToActor(actor, &wheelDesc);

return actor;
}

We build a box shape actor first.

NxActor* actor = CreateBox(pos + NxVec3(0,1,0), NxVec3(1,0.5,0.5), 10);

The box shape actor has its rotation frozen in x and z, so the cab doesn't fall over. It can
still rotate in y with some damping.

actor->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
actor->raiseBodyFlag(NX_BF_FROZEN_ROT_Z);
actor->setAngularDamping(0.5);

We then build the wheel descriptor

NxWheelDesc wheelDesc;
// wheelDesc.wheelAxis.set(0,0,1);
// wheelDesc.downAxis.set(0,-1,0);
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.1;
wheelDesc.wheelSuspension = 0.2;
wheelDesc.springRestitution = 7000;
wheelDesc.springDamping = 800;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.frictionToFront = 0.1;
wheelDesc.frictionToSide = 0.99;
wheelDesc.position = NxVec3(0,-0.4,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT |
NX_WF_BUILD_LOWER_HALF;

AddWheelToActor(actor, &wheelDesc);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
We pass in our box actor and our wheel descriptor into AddWheelToActor().

void AddWheelToActor(NxActor* actor, NxWheelDesc* wheelDesc)


{
NxWheelShapeDesc wheelShapeDesc;

// Create a shared car wheel material to be used by all wheels


static NxMaterial* wsm = 0;
if (!wsm)
{
NxMaterialDesc m;
m.flags |= NX_MF_DISABLE_FRICTION;
wsm = gScene->createMaterial(m);
}
wheelShapeDesc.materialIndex = wsm->getMaterialIndex();

wheelShapeDesc.localPose.t = wheelDesc->position;
NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
wheelShapeDesc.localPose.M.fromQuat(q);

NxReal heightModifier = (wheelDesc->wheelSuspension + wheelDesc->wheelRadius)


/ wheelDesc->wheelSuspension;

wheelShapeDesc.suspension.spring = wheelDesc->springRestitution*heightModifier;
wheelShapeDesc.suspension.damper = 0; //wheelDesc-
>springDamping*heightModifier;
wheelShapeDesc.suspension.targetValue = wheelDesc->springBias*heightModifier;

wheelShapeDesc.radius = wheelDesc->wheelRadius;
wheelShapeDesc.suspensionTravel = wheelDesc->wheelSuspension;
wheelShapeDesc.inverseWheelMass = 0.1; //not given!? TODO

// wheelShapeDesc.lateralTireForceFunction = ; //TODO
// wheelShapeDesc.longitudalTireForceFunction = ; //TODO

NxWheelShape* wheelShape;
wheelShape = static_cast<NxWheelShape *>(actor->createShape(wheelShapeDesc));
}

We make a static variable wheel material that will be the material for all wheels added to
actors by the function.

// Create a shared car wheel material to be used by all wheels


static NxMaterial* wsm = 0;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
if (!wsm)
{
NxMaterialDesc m;
m.flags |= NX_MF_DISABLE_FRICTION;
wsm = gScene->createMaterial(m);
}

The local pose of the wheel shape is the position in the wheel descriptor (-0.4 below the
position of the actor).

wheelShapeDesc.localPose.t = wheelDesc->position;

By default the wheel is pointed down positive z, with the normal being positive x, and
“up” being positive y. We turn the wheel to face positive x.

NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
wheelShapeDesc.localPose.M.fromQuat(q);

We add a suspension spring.

NxReal heightModifier = (wheelDesc->wheelSuspension + wheelDesc->wheelRadius)


/ wheelDesc->wheelSuspension;

wheelShapeDesc.suspension.spring = wheelDesc->springRestitution*heightModifier;
wheelShapeDesc.suspension.damper = 0; //wheelDesc-
>springDamping*heightModifier;
wheelShapeDesc.suspension.targetValue = wheelDesc->springBias*heightModifier;

We add a radius to the wheel.

wheelShapeDesc.radius = wheelDesc->wheelRadius;
wheelShapeDesc.suspensionTravel = wheelDesc->wheelSuspension;
wheelShapeDesc.inverseWheelMass = 0.1; //not given!? TODO

// wheelShapeDesc.lateralTireForceFunction = ; //TODO
// wheelShapeDesc.longitudalTireForceFunction = ; //TODO

Our wheel shape descriptor is complete, and we pass it to NxActor::createShape() to add


the wheel to our cab actor.

NxWheelShape* wheelShape;
wheelShape = static_cast<NxWheelShape *>(actor->createShape(wheelShapeDesc));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
4 Conclusion and Playing Around
You have now attached a wheel shape to your box actor. Run the demo. The cab is the
selected actor. You can push your small cab around with “ijkl”, just like any of the other
actors. The only difference is the attached wheel shape.

NxWheelShape has a simple but extensible interface that permits simple saving and
loading of the entire wheel state, as well as the modification of any properties during the
simulation. It is also possible to query the contact status directly from the wheel shape.
This provides a complete set of contact properties, including the local surface material of
the shapes in contact with the wheel as well as the currently acting tire forces at that
contact.

This is a simple app containing a single wheel shape. In the coming lessons, we will
show you how to apply contact forces to your wheel shape and how to steer your wheel
shape. We will also show you how to create a graphical representation of your wheel
shape.

Once you know how to do this, we will build a four-wheeled vehicle and then create a
vehicle class with which you can build lots of different types of wheeled vehicles.

5 Related Classes, Functions, and Parameters


NxParameter
NX_VISUALIZE_CONTACT_POINT
NX_VISUALIZE_CONTACT_NORMAL

NxWheelDesc
wheelDesc
wheelAxis
downAxis
wheelApproximation
wheelRadius
wheelWidth
wheelSuspension
springRestitution
springDamping
springBias
maxBrakeForce
frictionToFront
frictionToSide
position
wheelFlags

NxWheelFlag
NX_WF_USE_WHEELSHAPE
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
NX_WF_BUILD_LOWER_HALF
NX_WF_ACCELERATED
NX_WF_AFFECTED_BY_HANDBRAKE
NX_WF_STEERABLE_INPUT
NX_WF_BUILD_LOWER_HALF

NxWheelShapeDesc
materialIndex
localPose
suspension
radius
suspensionTravel
inverseWheelMass

NxWheelShape

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
Lesson 702 – Suspension and Torque

1 Introduction
In this lesson, we create a two-wheeled vehicle. We will play around with the suspension
height and springiness on the wheels, as well as other wheel parameters. We also make
both wheels motorized. Setting the motor torques on either or both wheels will drive our
actor when the wheels are in contact with the ground plane or other objects.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Two Wheeled Actor
In InitNx(), we load a PML scene with a ramp and create a board actor that is a box shape
with two wheel shapes attached.

// Actor globals
NxActor* board = NULL;

// Wheel shape globals


NxWheelShape* wheel1 = NULL;
NxWheelShape* wheel2 = NULL;
...
// Data Directory Paths (solution, binary, install)
char fname[] = "../../../../Data/pml";
char fname1[] = "../../../Data/pml";
char fname2[] = "../../TrainingPrograms/Programs/Data/pml";
char fname3[] = "TrainingPrograms/Programs/Data/pml";
...
void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL,
&gErrorStream);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AXES, 1);

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene){
sceneDesc.simType = NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene) return;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
}

NxU32 set = 0;

#ifdef WIN32
set = SetCurrentDirectory(&fname[0]);
if (!set) set = SetCurrentDirectory(&fname1[0]);
if (!set)
{
char basePath[256];
GetModuleFileName(NULL, basePath, 256);
char* pTmp = strrchr(basePath, '\\');
basePath[pTmp-basePath+1] = 0;
SetCurrentDirectory(basePath);//for running from start menu

set = SetCurrentDirectory(&fname2[0]);
}
if (!set) set = SetCurrentDirectory(&fname3[0]);
#elif LINUX
set = chdir(&fname[0]);
if (set != 0) set = chdir(&fname2[0]);
if (set != 0) set = chdir(&fname3[0]);
#endif

// Create the default material


NxMaterialDesc defaultMaterial;
defaultMaterial.restitution = 0;
defaultMaterial.staticFriction = 0.5;
defaultMaterial.dynamicFriction = 0.5;
NxMaterial* m = gScene->getMaterialFromIndex(0);
m->loadFromDesc(defaultMaterial);

// Load the ramp scene


char buffer[512];
FindMediaFile("Ramp.pml", buffer);
nxmlLoadScene(buffer, gPhysicsSDK, gScene);

// Switch from Max Coordinate System to


// Training Program Coordinate System
NxMat34 mat;
NxMat33 orient;
orient.setColumn(0, NxVec3(-1,0,0));
orient.setColumn(1, NxVec3(0,0,1));
orient.setColumn(2, NxVec3(0,1,0));
mat.M = orient;
SwitchCoordinateSystem(gScene, mat);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
// Reset wheel material
wsm = NULL;

// Create board actor


board = CreateBoard(NxVec3(0,3,0));
board->wakeUp(1e30);

AddUserDataToActors(gScene);

gSelectedActor = board;

// Initialize HUD
InitializeHUD();
InitializeSpecialHUD();

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

We create an empty physics scene and then we load the ramp scene the PML loader from
“Lesson 502: Loading PML Files”.

// Load the ramp scene


char buffer[512];
FindMediaFile("Ramp.pml", buffer);
nxmlLoadScene(buffer, gPhysicsSDK, gScene);
This scene has two dynamic objects, a sphere and a box, and a static convex object, a
ramp. We change the orientation of the loaded objects from the 3DS Max coordinate
system to the Training Program coordinate system (x left, y up, z forward). This code
was used in “Lesson 503: Switching Coordinate Systems”.

// Switch from Max Coordinate System to


// Training Program Coordinate System
NxMat34 mat;
NxMat33 orient;
orient.setColumn(0, NxVec3(-1,0,0));
orient.setColumn(1, NxVec3(0,0,1));
orient.setColumn(2, NxVec3(0,1,0));
mat.M = orient;
NewCoordinateSystem(gScene, mat);

Finally, we construct our board actor.


Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
// Create board actor
board = CreateBoard(NxVec3(0,0,0));
board->wakeUp(1e30);
gSelectedActor = board;

Look at CreateBoard().

NxActor* CreateBoard(const NxVec3& pos)


{
NxActor* actor = CreateBox(pos + NxVec3(0,0.5,0), NxVec3(1,0.25,0.5), 10);
actor->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
actor->setAngularDamping(0.5);

// Left wheel
NxWheelDesc wheelDesc;
//wheelDesc.wheelAxis.set(0,0,1);
//wheelDesc.downAxis.set(0,-1,0);
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.1;
wheelDesc.wheelSuspension = 0.5;
wheelDesc.springRestitution = 7000;
wheelDesc.springDamping = 0;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.frictionToFront = 0.1;
wheelDesc.frictionToSide = 0.99;
wheelDesc.position = NxVec3(1.5,0.5,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;

wheel1 = AddWheelToActor(actor, &wheelDesc);

// Right wheel
NxWheelDesc wheelDesc2;
//wheelDesc2.wheelAxis.set(0,0,1);
//wheelDesc2.downAxis.set(0,-1,0);
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.1;
wheelDesc2.wheelSuspension = 0.5;
wheelDesc2.springRestitution = 7000;
wheelDesc2.springDamping = 0;
wheelDesc2.springBias = 0;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.frictionToFront = 0.1;
wheelDesc2.frictionToSide = 0.99;
wheelDesc2.position = NxVec3(-1.5,0.5,0);
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;

wheel2 = AddWheelToActor(actor, &wheelDesc2);

return actor;
}

We start out building a box shape actor as a board.

NxActor* actor = CreateBox(pos + NxVec3(0,0.5,0), NxVec3(1,0.25,0.5), 10);


actor->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
actor->setAngularDamping(0.5);

We freeze the rotation about the x-axis so the board doesn't tip over when we add the
wheels. We add the left wheel first.

// Left wheel
NxWheelDesc wheelDesc;
//wheelDesc.wheelAxis.set(0,0,1);
//wheelDesc.downAxis.set(0,-1,0);
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.1;
wheelDesc.wheelSuspension = 0.5;
wheelDesc.springRestitution = 7000;
wheelDesc.springDamping = 0;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.frictionToFront = 0.1;
wheelDesc.frictionToSide = 0.99;
wheelDesc.position = NxVec3(1.5,0.5,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;

wheel1 = AddWheelToActor(actor, &wheelDesc);

The wheel shape gets added to the board actor with the AddWheelToActor(actor,
&wheelDesc) call.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
NxWheelShape* AddWheelToActor(NxActor* actor, NxWheelDesc* wheelDesc)
{
NxWheelShapeDesc wheelShapeDesc;

// Create a shared car wheel material to be used by all wheels


static NxMaterial* wsm = 0;
if (!wsm)
{
NxMaterialDesc m;
m.flags |= NX_MF_DISABLE_FRICTION;
wsm = gScene->createMaterial(m);
}
wheelShapeDesc.materialIndex = wsm->getMaterialIndex();

wheelShapeDesc.localPose.t = wheelDesc->position;
NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
wheelShapeDesc.localPose.M.fromQuat(q);

NxReal heightModifier = (wheelDesc->wheelSuspension + wheelDesc->wheelRadius)


/ wheelDesc->wheelSuspension;

wheelShapeDesc.suspension.spring = wheelDesc->springRestitution*heightModifier;
wheelShapeDesc.suspension.damper = 0; //wheelDesc-
>springDamping*heightModifier;
wheelShapeDesc.suspension.targetValue = wheelDesc->springBias*heightModifier;

wheelShapeDesc.radius = wheelDesc->wheelRadius;
wheelShapeDesc.suspensionTravel = wheelDesc->wheelSuspension;
wheelShapeDesc.inverseWheelMass = 0.1; //not given!? TODO

// wheelShapeDesc.lateralTireForceFunction = ; //TODO
// wheelShapeDesc.longitudalTireForceFunction = ; //TODO

NxWheelShape* wheelShape = NULL;


wheelShape = static_cast<NxWheelShape *>(actor->createShape(wheelShapeDesc));
return wheelShape;
}

The wheel shape descriptor gets filled out here as in the previous lesson.
NxWheelShapeDesc is derived from NxShapeDesc. The wheel shape is a basic shape
object with position, orientation, and material. It gets fleshed out with the following
parameters, from NxWheelShapeDesc.h:

NxReal radius - distance from wheel axle to a point on the contact surface
NxReal suspensionTravel - distance of suspension along shape's -Y axis
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
NxSpringDesc suspension - spring along the suspensionTravel distance
NxReal inverseWheelMass – determines the velocity the wheel torques can achieve
NxReal motorTorque – sum of the engine torque on the wheel axle
NxReal brakeTorque – amount of torque applied for braking
NxReal steerAngle – steering angle, around Y axis
NxU32 wheelFlags

The following two parameters are currently unimplemented, but will be implemented in
the future. They descript

NxTireFuncDesc longitudalTireForceFunction – hermite spline coefficients describing


the longitudal tire force curve
NxTireFuncDesc lateralTireForceFunction – hermite spline coefficients describing the
lateral tire force curve

/*
Force
^ extremum
| _*_
| ~ \ asymptote
| / \~__*______________
|/
|/
---------------------------> Slip

NxTireFuncDesc

NxReal extremumSlip – extermum point of curve (default 2.0)


NxReal extremumValue – extermum point of curve (default 0.01)
NxReal asymptoteSlip – point on curve at which for all x > minimumX, function equals
minimumY (default 2.0)
NxReal asymptoteValue – point on curve at which for all x > minimumX, function equals
minimumY (default 0.01)
NxReal stiffnessFactor – scaling factor for tire force

First, we build a frictionless material for the wheel shape and assign the index to this
material to the wheel shape.

NxWheelShapeDesc wheelShapeDesc;

// Create a shared car wheel material to be used by all wheels


static NxMaterial* wsm = 0;
if (!wsm)
{
NxMaterialDesc m;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
m.flags |= NX_MF_DISABLE_FRICTION;
wsm = gScene->createMaterial(m);
}
wheelShapeDesc.materialIndex = wsm->getMaterialIndex();

Next we set the position of the wheel on the board actor. This will be 1.5 units to the left
and 0.5 up from the center of the board. Since the bottom of the board is at (0,0.5,0), the
top of the wheel will be at (1.5, 1, 0), 1m above the ground.

wheelShapeDesc.localPose.t = wheelDesc->position;

We have set the SDK up to visualize the global axis of the wheel shape, or where this
position is set at the top of the wheel. This is the
NX_VISUALIZE_COLLISION_SHAPES flag in InitNx().

void InitNx()
{
...
// Set the debug visualization parameters
...
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AXES, 1);
...
}

Next we rotate the wheel 90 degrees about the y-axis.

NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
wheelShapeDesc.localPose.M.fromQuat(q);

By default, wheel shapes start out rolling down the z-axis, with the y-axis being up and
the x-axis being the positive roll of the wheel, as shown below.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
We want the wheels on our board to roll along the x-axis, with y-axis up, and the
negative z-axis being positive roll (so positive roll is along the x-axis).

3 Suspension
Now we set the suspension on the wheel shape.

NxReal heightModifier = (wheelDesc->wheelSuspension + wheelDesc->wheelRadius)


/ wheelDesc->wheelSuspension;

wheelShapeDesc.suspension.spring = wheelDesc->springRestitution*heightModifier;
wheelShapeDesc.suspension.damper = wheelDesc->springDamping*heightModifier;
wheelShapeDesc.suspension.targetValue = wheelDesc->springBias*heightModifier;

heightModifier is (wheel suspension + wheel radius)/(wheel supenstion) = (0.5 + 0.5)/1.0


=1

Our spring is 7000, the damper is 0, and the target value is 0. This means the wheel has a
rather tight spring force holding its initial global position at (1.5, 1, 0).

The right wheel is done similarly to the left wheel, only placed at (-1.5, 1, 0) globally.

// Right wheel
NxWheelDesc wheelDesc2;
//wheelDesc2.wheelAxis.set(0,0,1);
//wheelDesc2.downAxis.set(0,-1,0);
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.1;
wheelDesc2.wheelSuspension = 0.5;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
10
wheelDesc2.springRestitution = 7000;
wheelDesc2.springDamping = 800;
wheelDesc2.springBias = 0;
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.frictionToFront = 0.1;
wheelDesc2.frictionToSide = 0.99;
wheelDesc2.position = NxVec3(-1.5,0.5,0);
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;

wheel2 = AddWheelToActor(actor, &wheelDesc2);

Try out the demo. Push the two-wheeled board around with “umijkl” to see how the
wheels move and collide with the ground. If you lift the board off the ground and let it
drop, you'll see how the wheels are suspended from 0.5 above the center of the wheel.

Try changing the left and right wheel suspension parameters like this:

// Left wheel
NxWheelDesc wheelDesc;
...
wheelDesc.wheelSuspension = 1.0;
wheelDesc.springRestitution = 20;
wheelDesc.springDamping = 0.5;
wheelDesc.springBias = 0;
...
wheel1 = AddWheelToActor(actor, &wheelDesc);

// Right wheel
NxWheelDesc wheelDesc2;
...
wheelDesc2.wheelSuspension = 1.0;
wheelDesc2.springRestitution = 20;
wheelDesc2.springDamping = 0.5;
wheelDesc2.springBias = 0;
...
wheel2 = AddWheelToActor(actor, &wheelDesc2);

The wheels are now suspended at 1.5 (suspension + wheel radius), but the resitution of
the suspension is low, so the suspension is very loose, as if built for an all terrain vehicle,
one where the cab is lifted gradually when the vehicle hits bumps in the road.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
11
4 Torque
We now use motor torques to drive the wheels. Put the suspension back at the original,
tighter settings.

// Left wheel
NxWheelDesc wheelDesc;
...
wheelDesc.wheelSuspension = 0.5;
wheelDesc.springRestitution = 7000;
wheelDesc.springDamping = 0;
wheelDesc.springBias = 0;
...
wheel1 = AddWheelToActor(actor, &wheelDesc);

// Right wheel
NxWheelDesc wheelDesc2;
...
wheelDesc2.wheelSuspension = 0.5;
wheelDesc2.springRestitution = 7000;
wheelDesc2.springDamping = 0;
wheelDesc2.springBias = 0;
...
wheel2 = AddWheelToActor(actor, &wheelDesc2);

Run the demo. Hit “1”, “2”, and “3” to set the torque on the left wheel.

“1”: +100 to left wheel torque


“2”: set left wheel torque to 0
“3”: -100 to left wheel torque

// HUD globals
...
int gCurrentMotorTorque1 = 0;
...
// Wheel shape globals
NxWheelShape* wheel1 = NULL;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
...
// Left wheel

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
12
case '1':
{
gCurrentMotorTorque1 += 100;
bSetCurrentMotorTorque1 = true;
break;
}

case '2':
{
gCurrentMotorTorque1 = 0;
bSetCurrentMotorTorque1 = true;
break;
}

case '3':
{
gCurrentMotorTorque1 -= 100;
bSetCurrentMotorTorque1 = true;
break;
}
...
}
}

If you think of the left side as the front of the vehicle, you are motivating the vehicle with
front-wheel drive.

Hit “8”, “9”, and “0” to set the torque on the right wheel.

“8”: +100 to right wheel torque


“9”: set right wheel torque to 0
“0”: -100 to right wheel torque

// HUD globals
...
int gCurrentMotorTorque2 = 0;
...
// Wheel shape globals
NxWheelShape* wheel2 = NULL;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
13
...
// Right wheel
case '8':
{
gCurrentMotorTorque2 += 100;
bSetCurrentMotorTorque2 = true;
break;
}

case '9':
{
gCurrentMotorTorque2 = 0;
bSetCurrentMotorTorque2 = true;
break;
}

case '0':
{
gCurrentMotorTorque2 -= 100;
bSetCurrentMotorTorque2 = true;
break;
}
...
}
}

void ProcessInputs()
{

if (bSetCurrentMotorTorque1)
{
bSetCurrentMotorTorque1 = false;
wheel1->setMotorTorque(gCurrentMotorTorque1);
// Set motor torque #1 in HUD
char ds[512];
sprintf(ds, "Left wheel torque: %d", gCurrentMotorTorque1);
hud.SetDisplayString(2, ds, 0.015f, 0.92f);
}

if (bSetCurrentMotorTorque2)
{
bSetCurrentMotorTorque2 = false;
wheel2->setMotorTorque(gCurrentMotorTorque2);
// Set motor torque #2 in HUD
char ds[512];
sprintf(ds, "Right wheel torque: %d", gCurrentMotorTorque2);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
14
hud.SetDisplayString(3, ds, 0.015f, 0.87f);
}

}

If you think of the right side as the rear of the vehicle, you are motivating the vehicle
with rear-wheel drive. Adjust both left and right wheel torques and the vehicle is running
on all-wheel drive.

5 Wheel Contacts and Contact Forces


The wheel shapes are driven by contact with the ground or other objects. The points of
contact can be visualized with NX_VISUALIZE_CONTACT_POINT.

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);

The contact forces that drive the wheel are perpendicuar to the wheel's contact normal.
The contact normals can be visualized with NX_VISUALIZE_CONTACT_NORMAL.

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);

6 Friction Model of the Wheel Shape (Additional)


With the NxWheelShape, we have an advanced tire slip based friction model, rather than
using the standard friction associated with other shape types.

Tire slip based friction is the standard ‘textbook’ approach to tire friction, there are a lot
of references available where it is explained (for example Race Car Vehicle Dynamics by
Milliken et al.). It separates the overall friction force into a longitudal component (this is
in the direction of rolling, and responsible for braking and acceleration) and a lateral
component (this is orthogonal to rolling and is responsible for keeping the car properly
oriented). In both directions it is first determined how much the tire is slipping (i.e. what
the speed difference is between the rubber and the road). Then this slip value is mapped
to a tire force by means of a lookup table filled with empirical tire data. The predominant
property of real tires is that for a certain low slip, they can exert high tire forces as the
rubber compensates for the slip by stretching, and maintains a static friction of sorts.
Later when the slip gets really high, the forces are reduced as the tire really starts to slide
/ spin, and dynamic friction starts to act.

Rather than using an explicit lookup table that would be associated with a certain storage
and performance overhead, NxWheelShape introduces NxTireFunctionDesc, which
contains the parameters of a function consisting of the coefficients of two cubic hermite
splines. This way we approximate the tire force function. The coefficients are very
intuitive because they are at the same time the extrema of the function.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
15
To compute the tire slip in the longitudal (rolling) direction, it is necessary to know the
current angular velocity of the tire. To get this velocity, the wheel shape contains an
update of the wheel speed. This is done by integrating the sum torque acting on the
wheel, which is made up of the sum motor, braking, and ground induced torques. The
ground induced torque is equivalent to the longitudal tire force that is also computed
internally. The motor and brake torques are user inputs, and are treated separately,
because a motor torque opposing the wheel’s rotation will make it eventually reverse
rotation, while a very large sustained brake torque will simply stop the wheel and lock it
in place.

As will be explained in the next lesson, a steering angle parameter can be set for the
wheel. Also, the ray direction is defined to cast along the negative Y axis, which means
that simulations that use Y as the up axis no longer need to turn the shape on its head to
properly orient the wheel.

The below diagram illustrates the wheel shape axis conventions:

7 Conclusion
This concludes the lesson on wheel suspension and wheel motor torques.

8 Related Classes, Functions, and Parameters


NxParameter
NX_VISUALIZE_CONTACT_POINT
NX_VISUALIZE_CONTACT_NORMAL

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
16
NxWheelDesc
wheelDesc
wheelAxis
downAxis
wheelApproximation
wheelRadius
wheelWidth
wheelSuspension
springRestitution
springDamping
springBias
maxBrakeForce
frictionToFront
frictionToSide
position
wheelFlags

NxWheelFlag
NX_WF_USE_WHEELSHAPE
NX_WF_BUILD_LOWER_HALF
NX_WF_ACCELERATED
NX_WF_AFFECTED_BY_HANDBRAKE
NX_WF_STEERABLE_INPUT
NX_WF_BUILD_LOWER_HALF

NxWheelShapeDesc
materialIndex
localPose
suspension
radius
suspensionTravel
inverseWheelMass

NxWheelShape

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
17
Lesson 703 – Brakes and Steering

1 Introduction
We now learn how to steer and brake with our wheel shapes. We will also show how
lowering the center of mass helps keep the car from flipping over. We end with a
discussion of the tire friction curve.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Brakes
In InitNx(), we load a PML scene with four small hills and create a trike actor that is a
box shape with three wheel shapes attached.

// Actor globals
NxActor* trike = NULL;

// Wheel shape globals


NxWheelShape* wheel1 = NULL;
NxWheelShape* wheel2 = NULL;
NxWheelShape* wheel3 = NULL;
...
// Data Directory Paths (solution, binary, install)
char fname[] = "../../../../Data/pml";
char fname1[] = "../../../Data/pml";
char fname2[] = "../../TrainingPrograms/Programs/Data/pml";
char fname3[] = "TrainingPrograms/Programs/Data/pml";...
void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL,
&gErrorStream);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
// gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AXES, 1);
// gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene){
sceneDesc.simType = NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
if(!gScene) return;
}

NxU32 set = 0;

#ifdef WIN32
set = SetCurrentDirectory(&fname[0]);
if (!set) set = SetCurrentDirectory(&fname1[0]);
if (!set)
{
char basePath[256];
GetModuleFileName(NULL, basePath, 256);
char* pTmp = strrchr(basePath, '\\');
basePath[pTmp-basePath+1] = 0;
SetCurrentDirectory(basePath);//for running from start menu

set = SetCurrentDirectory(&fname2[0]);
}
if (!set) set = SetCurrentDirectory(&fname3[0]);
#elif LINUX
set = chdir(&fname[0]);
if (set != 0) set = chdir(&fname2[0]);
if (set != 0) set = chdir(&fname3[0]);
#endif

// Create the default material


NxMaterialDesc defaultMaterial;
defaultMaterial.restitution = 0;
defaultMaterial.staticFriction = 0.5;
defaultMaterial.dynamicFriction = 0.5;
NxMaterial* m = gScene->getMaterialFromIndex(0);
m->loadFromDesc(defaultMaterial);

char buffer[512];
FindMediaFile("Course.pml", buffer);
nxmlLoadScene(buffer, gPhysicsSDK, gScene);

// Switch from Max Coordinate System to


// Training Program Coordinate System
NxMat34 mat;
NxMat33 orient;
orient.setColumn(0, NxVec3(-1,0,0));
orient.setColumn(1, NxVec3(0,0,1));
orient.setColumn(2, NxVec3(0,1,0));
mat.M = orient;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
SwitchCoordinateSystem(gScene, mat);

// Reset wheel material


wsm = NULL;

// Create the trike actor


trike = CreateTrike(NxVec3(0,3,0));
trike->wakeUp(1e30);

AddUserDataToActors(gScene);

gSelectedActor = trike;

// Initialize HUD
InitializeHUD();
InitializeSpecialHUD();

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

We create an empty physics scene and then we load the course scene the PML loader
from “Lesson 502: Loading PML Files”.

// Load the ramp scene


char buffer[512];
FindMediaFile("Course.pml", buffer);
nxmlLoadScene(buffer, gPhysicsSDK, gScene);

We change the orientation of the loaded objects from the 3DS Max coordinate system to
the Training Program coordinate system (x left, y up, z forward). This code was used in
“Lesson 503: Switching Coordinate Systems”.

// Switch from Max Coordinate System to


// Training Program Coordinate System
NxMat34 mat;
NxMat33 orient;
orient.setColumn(0, NxVec3(-1,0,0));
orient.setColumn(1, NxVec3(0,0,1));
orient.setColumn(2, NxVec3(0,1,0));
mat.M = orient;
NewCoordinateSystem(gScene, mat);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Finally, we construct our trike actor.

// Create the trike actor


trike = CreateTrike(NxVec3(0,3,0));
trike->wakeUp(1e30);
gSelectedActor = trike;

Look at CreateTrike().

NxActor* CreateTrike(const NxVec3& pos)


{
NxActor* actor = CreateBox(pos + NxVec3(0,0.5,0), NxVec3(2,0.25,0.75), 10);

NxTireFunctionDesc lngTFD;
lngTFD.extremumSlip = 1.0f;
lngTFD.extremumValue = 0.02f;
lngTFD.asymptoteSlip = 2.0f;
lngTFD.asymptoteValue = 0.01f;
lngTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc latTFD;
latTFD.extremumSlip = 1.0f;
latTFD.extremumValue = 0.02f;
latTFD.asymptoteSlip = 2.0f;
latTFD.asymptoteValue = 0.01f;
latTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc slipTFD;
slipTFD.extremumSlip = 1.0f;
slipTFD.extremumValue = 0.02f;
slipTFD.asymptoteSlip = 2.0f;
slipTFD.asymptoteValue = 0.01f;
slipTFD.stiffnessFactor = 100.0f;

// Front wheel
NxWheelDesc wheelDesc;
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.3; // 0.1
wheelDesc.wheelSuspension = 1.0;
wheelDesc.springRestitution = 100;
wheelDesc.springDamping = 0.5;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.longitudalTireForceFunction = lngTFD;
wheelDesc.lateralTireForceFunction = latTFD;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
wheelDesc.position = NxVec3(2.5,0.5,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel1 = AddWheelToActor(actor, &wheelDesc);

// Rear left wheel


NxWheelDesc wheelDesc2;
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.3; // 0.1
wheelDesc2.wheelSuspension = 1.0;
wheelDesc2.springRestitution = 100;
wheelDesc2.springDamping = 0.5;
wheelDesc2.springBias = 0;
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.longitudalTireForceFunction = lngTFD;
wheelDesc2.lateralTireForceFunction = latTFD;
// wheelDesc2.lateralTireForceFunction = slipTFD;
wheelDesc2.position = NxVec3(-1.5,0.5,-1);
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel2 = AddWheelToActor(actor, &wheelDesc2);

// Rear right wheel


NxWheelDesc wheelDesc3;
wheelDesc3.wheelApproximation = 10;
wheelDesc3.wheelRadius = 0.5;
wheelDesc3.wheelWidth = 0.3; // 0.1
wheelDesc3.wheelSuspension = 1.0;
wheelDesc3.springRestitution = 100;
wheelDesc3.springDamping = 0.5;
wheelDesc3.springBias = 0;
wheelDesc3.maxBrakeForce = 1;
wheelDesc3.longitudalTireForceFunction = lngTFD;
wheelDesc3.lateralTireForceFunction = latTFD;
// wheelDesc3.lateralTireForceFunction = slipTFD
wheelDesc3.position = NxVec3(-1.5,0.5,1);
wheelDesc3.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel3 = AddWheelToActor(actor, &wheelDesc3);

// LOWER THE CENTER OF MASS


NxVec3 massPos = actor->getCMassLocalPosition();
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
massPos.y -= 1;
actor->setCMassOffsetLocalPosition(massPos);

massPos = actor->getCMassLocalPosition();

return actor;
}

We start out building a box shape actor as the chassis of the trike.

NxActor* actor = CreateBox(pos + NxVec3(0,0.5,0), NxVec3(2,0.25,0.75), 10);

Next, we defined friciton models for the tires, the tire force functions. We'll discuss these
more in detail later.

NxTireFunctionDesc lngTFD;
lngTFD.extremumSlip = 1.0f;
lngTFD.extremumValue = 0.02f;
lngTFD.asymptoteSlip = 2.0f;
lngTFD.asymptoteValue = 0.01f;
lngTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc latTFD;
latTFD.extremumSlip = 1.0f;
latTFD.extremumValue = 0.02f;
latTFD.asymptoteSlip = 2.0f;
latTFD.asymptoteValue = 0.01f;
latTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc slipTFD;
slipTFD.extremumSlip = 1.0f;
slipTFD.extremumValue = 0.02f;
slipTFD.asymptoteSlip = 2.0f;
slipTFD.asymptoteValue = 0.01f;
slipTFD.stiffnessFactor = 100.0f;

We add the front wheel first.

// Front wheel
NxWheelDesc wheelDesc;
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.3; // 0.1
wheelDesc.wheelSuspension = 1.0;
wheelDesc.springRestitution = 100;
wheelDesc.springDamping = 0.5;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.longitudalTireForceFunction = lngTFD;
wheelDesc.lateralTireForceFunction = latTFD;
wheelDesc.position = NxVec3(2.5,0.5,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel1 = AddWheelToActor(actor, &wheelDesc);

This is the wheel we'll be using to steer the trike with. With put it on the trike with the
AddWheelToActor() function.

NxWheelShape* AddWheelToActor(NxActor* actor, NxWheelDesc* wheelDesc)


{
NxWheelShapeDesc wheelShapeDesc;

// Create a shared car wheel material to be used by all wheels


static NxMaterial* wsm = 0;
if (!wsm)
{
NxMaterialDesc m;
m.flags |= NX_MF_DISABLE_FRICTION;
wsm = gScene->createMaterial(m);
}
wheelShapeDesc.materialIndex = wsm->getMaterialIndex();

wheelShapeDesc.localPose.t = wheelDesc->position;
NxQuat q;
q.fromAngleAxis(90, NxVec3(0,1,0));
wheelShapeDesc.localPose.M.fromQuat(q);

NxReal heightModifier = (wheelDesc->wheelSuspension + wheelDesc->wheelRadius)


/ wheelDesc->wheelSuspension;

wheelShapeDesc.suspension.spring = wheelDesc->springRestitution*heightModifier;
wheelShapeDesc.suspension.damper = wheelDesc->springDamping*heightModifier;
wheelShapeDesc.suspension.targetValue = wheelDesc->springBias*heightModifier;

wheelShapeDesc.radius = wheelDesc->wheelRadius;
wheelShapeDesc.suspensionTravel = wheelDesc->wheelSuspension;
wheelShapeDesc.inverseWheelMass = 0.1; //not given!? TODO

wheelShapeDesc.longitudalTireForceFunction = wheelDesc-
>longitudalTireForceFunction;
wheelShapeDesc.lateralTireForceFunction = wheelDesc->lateralTireForceFunction;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
NxWheelShape* wheelShape = NULL;
wheelShape = static_cast<NxWheelShape *>(actor->createShape(wheelShapeDesc));
return wheelShape;
}

Notice we have added two lines to set the tire force functions on the wheels.

wheelShapeDesc.longitudalTireForceFunction = wheelDesc-
>longitudalTireForceFunction;
wheelShapeDesc.lateralTireForceFunction = wheelDesc->lateralTireForceFunction;

We then add the rear left and right tires to the trike in CreateTrike().

// Rear left wheel


NxWheelDesc wheelDesc2;
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.3; // 0.1
wheelDesc2.wheelSuspension = 1.0;
wheelDesc2.springRestitution = 100;
wheelDesc2.springDamping = 0.5;
wheelDesc2.springBias = 0;
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.longitudalTireForceFunction = lngTFD;
wheelDesc2.lateralTireForceFunction = latTFD;
// wheelDesc2.lateralTireForceFunction = slipTFD;
wheelDesc2.position = NxVec3(-1.5,0.5,-1);
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel2 = AddWheelToActor(actor, &wheelDesc2);

// Rear right wheel


NxWheelDesc wheelDesc3;
wheelDesc3.wheelApproximation = 10;
wheelDesc3.wheelRadius = 0.5;
wheelDesc3.wheelWidth = 0.3; // 0.1
wheelDesc3.wheelSuspension = 1.0;
wheelDesc3.springRestitution = 100;
wheelDesc3.springDamping = 0.5;
wheelDesc3.springBias = 0;
wheelDesc3.maxBrakeForce = 1;
wheelDesc3.longitudalTireForceFunction = lngTFD;
wheelDesc3.lateralTireForceFunction = latTFD;
// wheelDesc3.lateralTireForceFunction = slipTFD
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
9
wheelDesc3.position = NxVec3(-1.5,0.5,1);
wheelDesc3.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel3 = AddWheelToActor(actor, &wheelDesc3);

Finally, we lower the center of mass of the trike body by 1 meter.

// LOWER THE CENTER OF MASS


NxVec3 massPos = actor->getCMassLocalPosition();
massPos.y -= 1;
actor->setCMassOffsetLocalPosition(massPos);

This will be important in keep our vehicle from flipping over when we steer it.

Run the demo. Use “8” and “0” to accelerate and decelerate the vehicle. Note when you
switch from forward to reverse, the trike gradually slows down and then starts
accelerating in reverse.

We apply motor torques to the rear wheel shapes with with


NxWheelShape::setMotorTorque() in ProcessForceKeys().

// Wheel shape globals


NxWheelShape* wheel1 = NULL;
NxWheelShape* wheel2 = NULL;
NxWheelShape* wheel3 = NULL;
...
int gCurrentMotorTorque = 0;
int gCurrentBrakeTorque = 0;
...
void ProcessForceKeys()
{
// Process force keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{
...
// Rear wheels
case '8':
{
gCurrentMotorTorque += 5;
gCurrentBrakeTorque = 0;
wheel2->setMotorTorque(gCurrentMotorTorque);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
10
wheel3->setMotorTorque(gCurrentMotorTorque);
wheel2->setBrakeTorque(0);
wheel3->setBrakeTorque(0);
RefreshDisplayString();
break;
}

case '9':
{
gCurrentMotorTorque = 0;
gCurrentBrakeTorque = 500;
wheel2->setMotorTorque(gCurrentMotorTorque);
wheel3->setMotorTorque(gCurrentMotorTorque);
wheel2->setBrakeTorque(gCurrentBrakeTorque);
wheel3->setBrakeTorque(gCurrentBrakeTorque);
RefreshDisplayString();
break;
}

case '0':
{
gCurrentMotorTorque -= 5;
wheel2->setMotorTorque(gCurrentMotorTorque);
wheel3->setMotorTorque(gCurrentMotorTorque);
wheel2->setBrakeTorque(0);
wheel3->setBrakeTorque(0);
RefreshDisplayString();
break;
}
}
}
}

If you hit the “9” key you will brake the trike. NxWheelShape::setBrakeTorque() applies
a braking force to the wheel shapes.

case '9':
{
gCurrentMotorTorque = 0;
gCurrentBrakeTorque = 750;
wheel2->setMotorTorque(gCurrentMotorTorque);
wheel3->setMotorTorque(gCurrentMotorTorque);
wheel2->setBrakeTorque(gCurrentBrakeTorque);
wheel3->setBrakeTorque(gCurrentBrakeTorque);
RefreshDisplayString();
break;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
11
}

This sets the rear wheel motor torque to 0 and sets the brake torque to 1000. Try out the
brakes out on your vehicle. You'll notice the brakes bring the trike to halt quite nicely.
Try accelerating, braking, and reversing the trike.

3 Steering
Now try steering the trike. Hit “1” to turn the front wheel to the left, “2” to center it, and
“3” to turn it to the right.

// Wheel shape globals


NxWheelShape* wheel1 = NULL;
...
NxReal gCurrentSteerAngle = 0;
...
void ProcessForceKeys()
{
// Process force keys
for (int i = 0; i < MAX_KEYS; i++)
{
if (!gKeys[i]) { continue; }

switch (i)
{
...
// Front wheel
case '1':
{
gCurrentSteerAngle += NxPi*0.003;
wheel1->setSteerAngle(gCurrentSteerAngle);
RefreshDisplayString();
break;
}

case '2':
{
gCurrentSteerAngle = 0;
wheel1->setSteerAngle(gCurrentSteerAngle);
RefreshDisplayString();
break;
}

case '3':
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
12
gCurrentSteerAngle -= NxPi*0.003;
wheel1->setSteerAngle(gCurrentSteerAngle);
RefreshDisplayString();
break;
}
}
}
}

NxWheelShape::setSteerAngle() sets the angle the front wheel shape is turned about the
its y-axis to the left or the right. Positive is left (counter clockwise from the top of the
wheel shape), negative is right (clockwise from the top of the wheel shape).

4 Tire Force Functions


To each wheel shape, we apply a longitudal and lateral tire force function.

Force
^ extremum
| _*_
| ~ \ asymptote
| / \~__*______________
|/
|/
---------------------------> Slip

This function determines how much the wheel slips according to how fast it's spinning vs.
the amount of force between the tire and the ground.

NxTireFunctionDesc lngTFD;
lngTFD.extremumSlip = 1.0f;
lngTFD.extremumValue = 0.02f;
lngTFD.asymptoteSlip = 2.0f;
lngTFD.asymptoteValue = 0.01f;
lngTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc latTFD;
latTFD.extremumSlip = 1.0f;
latTFD.extremumValue = 0.02f;
latTFD.asymptoteSlip = 2.0f;
latTFD.asymptoteValue = 0.01f;
latTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc slipTFD;
slipTFD.extremumSlip = 1.0f;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
13
slipTFD.extremumValue = 0.02f;
slipTFD.asymptoteSlip = 2.0f;
slipTFD.asymptoteValue = 0.01f;
slipTFD.stiffnessFactor = 100.0f;

// Front wheel
NxWheelDesc wheelDesc;
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.3; // 0.1
wheelDesc.wheelSuspension = 1.0;
wheelDesc.springRestitution = 100;
wheelDesc.springDamping = 0.5;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.longitudalTireForceFunction = lngTFD;
wheelDesc.lateralTireForceFunction = latTFD;
wheelDesc.position = NxVec3(2.5,0.5,0);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
//
NX_WF_INPUT_LNG_SLIPVELOCITY |
NX_WF_INPUT_LAT_SLIPVELOCITY;
wheel1 = AddWheelToActor(actor, &wheelDesc);

// Rear left wheel


NxWheelDesc wheelDesc2;
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.3; // 0.1
wheelDesc2.wheelSuspension = 1.0;
wheelDesc2.springRestitution = 100;
wheelDesc2.springDamping = 0.5;
wheelDesc2.springBias = 0;
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.longitudalTireForceFunction = lngTFD;
wheelDesc2.lateralTireForceFunction = latTFD;
// wheelDesc2.lateralTireForceFunction = slipTFD;
wheelDesc2.position = NxVec3(-1.5,0.5,-1);
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
//
NX_WF_INPUT_LNG_SLIPVELOCITY |
NX_WF_INPUT_LAT_SLIPVELOCITY;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
14
wheel2 = AddWheelToActor(actor, &wheelDesc2);

// Rear right wheel


NxWheelDesc wheelDesc3;
wheelDesc3.wheelApproximation = 10;
wheelDesc3.wheelRadius = 0.5;
wheelDesc3.wheelWidth = 0.3; // 0.1
wheelDesc3.wheelSuspension = 1.0;
wheelDesc3.springRestitution = 100;
wheelDesc3.springDamping = 0.5;
wheelDesc3.springBias = 0;
wheelDesc3.maxBrakeForce = 1;
wheelDesc3.longitudalTireForceFunction = lngTFD;
wheelDesc3.lateralTireForceFunction = latTFD;
// wheelDesc3.lateralTireForceFunction = slipTFD;
wheelDesc3.position = NxVec3(-1.5,0.5,1);
wheelDesc3.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
//
NX_WF_INPUT_LNG_SLIPVELOCITY |
NX_WF_INPUT_LAT_SLIPVELOCITY;
wheel3 = AddWheelToActor(actor, &wheelDesc3);

Try giving the rear wheel shapes the slipTFD, which has a stiffness factor of 100 vs. the
default of 1000000. The stiffness factor is the base amount of “grip” the tire has in the
specified direction, longitual (the direction the tire is facing) or lateral (perpendicular to
the direction the tire is facing.

// wheelDesc2.lateralTireForceFunction = latTFD;
wheelDesc2.lateralTireForceFunction = slipTFD;
...
// wheelDesc3.lateralTireForceFunction = latTFD;
wheelDesc3.lateralTireForceFunction = slipTFD;

Run the demo with these settings. Give the trike some speed and steer to the left. The
rear wheels will slip out and the trike will start sliding in a counterclockwise direction
when viewed from above. Obviously this is an extreme example, but you can adjust the
stiffness and tire force curve to taste to tweak the conditions under which the tires start to
skid and when they regain traction with the ground.

5 Conclusion
You can now brake and steer your wheel shapes as well as apply tire force functions to
them to adjust their grip on the ground.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
15
6 Related Classes, Functions, and Parameters
NxWheelDesc
longitudalTireForceFunction
lateralTireForceFunction

NxWheelFlag
NX_WF_ACCELERATED
NX_WF_AFFECTED_BY_HANDBRAKE
NX_WF_STEERABLE_INPUT

NxWheelShapeDesc

NxWheelShape
setMotorTorque()
setBrakeTorque()
setSteerAngle()

NxTireFunctionDesc
extremumSlip
extremumValue
asymptoteSlip
asymptoteValue
stiffnessFactor
hermiteEval()

NxWheelShapeFlags
NX_WF_WHEEL_AXIS_CONTACT_NORMAL
NX_WF_INPUT_LAT_SLIPVELOCITY
NX_WF_INPUT_LNG_SLIPVELOCITY
NX_WF_UNSCALED_SPRING_BEHAVIOR
NX_WF_AXLE_SPEED_OVERRIDE

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
16
Lesson 704 – All Wheel Drive

1 Introduction
In this lesson, we build a complete 4-wheeled vehicle. The wheels are loaded as tire
models from 3D Studio Max and are properly drawn and positioned.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 All Wheel Drive
Look at InitNx().

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;
NxActor* dome = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator,
&gErrorStream);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);

gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1);

// Create the scene


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScene = gPhysicsSDK->createScene(sceneDesc);

// Create the default material


NxMaterialDesc defaultMaterial;
defaultMaterial.restitution = 0;
defaultMaterial.staticFriction = 0.5;
defaultMaterial.dynamicFriction = 0.5;
NxMaterial* m = gScene->getMaterialFromIndex(0);
m->loadFromDesc(defaultMaterial);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
// Create the objects in the scene
groundPlane = CreateGroundPlane();

// Set the ASE directory


int set = 0;

#ifdef WIN32
set = SetCurrentDirectory(&ASEfname[0]); rdir = &returnDir[0];
if (!set) { set = SetCurrentDirectory(&ASEfname1[0]); rdir = &returnDir1[0]; }
if (!set) { set = SetCurrentDirectory(&ASEfname2[0]); rdir = &returnDir2[0]; }
if (!set) { set = SetCurrentDirectory(&ASEfname3[0]); rdir = &returnDir3[0]; }
#elif LINUX
set = chdir(&ASEfname[0]); rdir = &returnDir[0];
if (set != 0) { set = chdir(&ASEfname2[0]); rdir = &returnDir2[0]; }
if (set != 0) { set = chdir(&ASEfname3[0]); rdir = &returnDir3[0]; }
#endif

NxInitCooking();
dome = CookASE("dome.ase", gScene);

// Page in the dome mesh to the hardware


NxShape*const* shapes = dome->getShapes();
for (unsigned int s = 0; s < dome->getNbShapes(); ++s )
{
NxShape* shape = (NxShape*)(shapes[s]);
if(shape->is(NX_SHAPE_MESH))
{
NxTriangleMeshShape* triMeshShape = (NxTriangleMeshShape *)shape;
NxTriangleMesh& triMesh = triMeshShape->getTriangleMesh();
int pageCnt = triMesh.getPageCount();
for ( int t = 0; t < pageCnt; ++t )
{
if (!triMeshShape->isPageInstanceMapped(t))
{
bool ok = triMeshShape->mapPageInstance(t);
assert(ok);
}
}
}
}

// Set the 3DS directory


set = 0;
#ifdef WIN32
set = SetCurrentDirectory(&TDSfname[0]);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
#elif LINUX
set = chdir(&TDSfname[0]);
#endif

box = CreateBox(NxVec3(5,0+30,0), NxVec3(0.75,0.75,0.75), 5);

// Create the buggy actor


buggy = CreateBuggy(NxVec3(0,3+30,0));
buggy->wakeUp(1e30);
gSelectedActor = buggy;

AddUserDataToActors(gScene);

// Set user data


AttachModelToActor(box, "box.3ds", 0, 0.75);
AttachModelToActor(dome, "dome.3ds", 0, 1);

for (int i = 1; i < 5; i++)


{
// left, right, left, right - need to rotate left model
if (i%2)
AttachModelToActor(buggy, "tire3.3ds", i, 0.5, 180); // 0.2
else
AttachModelToActor(buggy, "tire3.3ds", i, 0.5); // 0.2
}

// Set current directory back to home directory


SetCurrentDirectory(rdir);

gCameraPos = NxVec3(0,10,-15);

// Initialize HUD
bHardwareScene = gScene->getSimType() == NX_SIMULATION_HW;
char ds[512];
sprintf(ds, "Front Wheel Steer Angle: %d\nRear Wheel Motor Torque: %d\nReal
Wheel Brake Torque: %d",
(int)(gCurrentSteerAngle*180.0/NxPi), gCurrentMotorTorque,
gCurrentBrakeTorque);
hud.Update(bHardwareScene, bPause, ds);

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
In all, we build some static and dynamic objects in the scene, and then call
CreateBuggy() to build our four-wheeled vehicle.

We create the SDK and scene as usual with a default material and a ground plane. We
then load in the “dome.ase” from the “TrainingPrograms/Programs/Data/ase” directory.

NxInitCooking();
dome = CookASE("dome.ase", gScene);

// Page in the dome mesh to the hardware


NxShape*const* shapes = dome->getShapes();
for (unsigned int s = 0; s < dome->getNbShapes(); ++s )
{
NxShape* shape = (NxShape*)(shapes[s]);
if(shape->is(NX_SHAPE_MESH))
{
NxTriangleMeshShape* triMeshShape = (NxTriangleMeshShape *)shape;
NxTriangleMesh& triMesh = triMeshShape->getTriangleMesh();
int pageCnt = triMesh.getPageCount();
for ( int t = 0; t < pageCnt; ++t )
{
if (!triMeshShape->isPageInstanceMapped(t))
{
bool ok = triMeshShape->mapPageInstance(t);
assert(ok);
}
}
}
}

We use the CookASE() function from CookASE.cpp to create the dome mesh actor.

CookASE.cpp

NxActor* CookASE(const std::string& filename, NxScene* scene, NxVec3 offset,


NxVec3 scale)
{
_offset = offset;
FILE* f = fopen(filename.c_str(), "rb");
if (f == NULL)
{
printf("File not found: %s\n", filename.c_str());
return NULL;
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
NxArray<NxVec3> vertices;
NxArray<NxU32> faces;

char line[512];
int linenbr = 0;
while (!feof(f))
{
fgets(line, 512, f);
char* l = trimFront(line);
if (!strncmp(l, "*MESH {", 7))
{
readMesh(f, vertices, faces, scale);
}
else
{
//printf("Line %4d: %s\n", linenbr++, l);
}
}

fclose(f);

// Build physical model


NxTriangleMeshDesc meshDesc;
meshDesc.numVertices = vertices.size();
meshDesc.numTriangles = faces.size()/3;
meshDesc.pointStrideBytes = sizeof(NxVec3);
meshDesc.triangleStrideBytes = 3*sizeof(NxU32);
meshDesc.points = &vertices[0].x;
meshDesc.triangles = &faces[0];
meshDesc.flags =
NX_MF_HARDWARE_MESH;

// meshDesc.heightFieldVerticalAxis = NX_Y;
// meshDesc.heightFieldVerticalExtent = -1000.0f;

MemoryWriteBuffer buf;
bool status = NxCookTriangleMesh(meshDesc, buf);
NxTriangleMesh* mesh = scene-
>getPhysicsSDK().createTriangleMesh(MemoryReadBuffer(buf.data));
// scene->createFluidHardwareTriangleMesh(MemoryReadBuffer(buf.data));

NxTriangleMeshShapeDesc meshShapeDesc;
meshShapeDesc.meshData = mesh;

NxActorDesc actorDesc;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
actorDesc.shapes.pushBack(&meshShapeDesc);

NxActor* a = scene->createActor(actorDesc);

#if 0
assert(a != NULL);
assert(a->getNbShapes() == 1);
NxShape* s = a->getShapes()[0];
assert(s != NULL);
NxTriangleMeshShape* ts = s->isTriangleMesh();
assert(ts != NULL);
NxTriangleMeshDesc* tmd= new NxTriangleMeshDesc();
ts->getTriangleMesh().saveToDesc(*tmd);
ts->userData = tmd;
#endif // 0

return a;
}

Since the dome is a mesh actor, we page an instance of the triangle mesh onto the
hardware.

3 3D Studio Max Models as Visible Object Geometry


We then set the 3D Studio Max directory and read in the “box.3ds”, “dome.3ds”, and
“tire3.3ds” files from the “TrainingPrograms/Programs/Data/3ds” directory.

// Set the 3DS directory


set = 0;
#ifdef WIN32
set = SetCurrentDirectory(&TDSfname[0]);
#elif LINUX
set = chdir(&TDSfname[0]);
#endif

The “dome.ase” file is used for collision geometry, it the physical triangle mesh of the
dome actor. The “dome.3ds” file is used for visible geometry, it is the textured triangle
mesh that gets drawn to represent the dome actor. In all training programs thusfar, we
have made the visible geometry match the physical geometry as much as possible. If the
physical geometry was a triangle mesh, we would draw the triangle mesh. If the physical
geometry was a geometric shape, we would draw a triangle mesh that was a close
approximation to the geometic shape.

In this lesson, we render 3D Studio Max models in place of the physical geometry for
some of the objects.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
// Actor globals

NxActor* box = NULL;
NxActor* dome = NULL;
NxActor* buggy = NULL;

void InitNx()
{

box = CreateBox(NxVec3(5,0+30,0), NxVec3(0.75,0.75,0.75), 5);

// Create the buggy actor


buggy = CreateBuggy(NxVec3(0,3+30,0));
buggy->wakeUp(1e30);
gSelectedActor = buggy;

AddUserDataToActors(gScene);

// Set user data


AttachModelToActor(box, "box.3ds", 0, 0.75);
AttachModelToActor(dome, "dome.3ds", 0, 1);

for (int i = 1; i < 5; i++)


{
// left, right, left, right - need to rotate left model
if (i%2)
AttachModelToActor(buggy, "tire3.3ds", i, 0.5, 180); // 0.2
else
AttachModelToActor(buggy, "tire3.3ds", i, 0.5); // 0.2
}

// Set current directory back to home directory


SetCurrentDirectory(rdir);

}

AttachModelToActor() attaches the 3DS model to the box actor’s box shape user data,
the dome actor’s mesh shape user data, and user data of the four tire shapes of the buggy
actor.

void AttachModelToActor(NxActor* actor, char* file, NxU32 shapeNum, NxReal scale,


NxReal angle = 0)
{
NxShape*const* shapes = actor->getShapes();
ShapeUserData* sud = (ShapeUserData*)(shapes[shapeNum]->userData);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
if (sud)
{
sud->model = new Model_3DS;
Model_3DS* m = (Model_3DS*)(sud->model);
m->Load(file);
m->scale = scale;
Model_3DS::Vector v;
v.x = 0; v.y = angle; v.z = 0;
m->rot = v;
}
}

The Model_3DS object is defined in Model_3DS.cpp.

Look at the new RenderActors() function to see where the 3DS models get drawn.

void RenderCallback()
{

// Display scene
RenderActors(false);

}

void RenderActors(bool shadows)


{
// Render all the actors in the scene
NxU32 nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;
Draw3DSMeshActor(actor);
// DrawActor(actor, gSelectedActor, true);

// Handle shadows
if (shadows)
{
DrawActorShadow(actor, true);
}
}
}

RenderActors() calls Draw3DSMeshActor() on each actor.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
void Draw3DSMeshActor(NxActor *actor)
{
NxShape*const* shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();
while (nShapes--)
{
ShapeUserData* sud = (ShapeUserData*)(shapes[nShapes]->userData);
if (sud && sud->model)
Draw3DSMesh(shapes[nShapes], ((Model_3DS*)(sud->model))); //only set one
shape in 3ds model.
else
DrawShape(shapes[nShapes], true);
}

}

If the user data of a particular shape has a empty “model” field, we draw its physical
geometry as usual. If it is non-empty, we draw the 3DS model in the user data’s “model”
field with Draw3DSMesh().

Draw3DSShapes.cpp

// Draw 3ds mesh


void Draw3DSMesh(NxShape* shape, Model_3DS* model)
{
NxMat34 pose;
pose = shape->getGlobalPose();

// Get saved-away wheel shape pose to draw wheel shape at proper position
if (shape->getType() == NX_SHAPE_WHEEL)
{
NxWheelShape* ws = (NxWheelShape *)shape;
pose = ((ShapeUserData*)(ws->userData))->wheelShapePose;
}

glPushMatrix();

float glmat[16]; //4x4 column major matrix for OpenGL.


pose.M.getColumnMajorStride4(&(glmat[0]));
pose.t.get(&(glmat[12]));

//clear the elements we don't need:


glmat[3] = glmat[7] = glmat[11] = 0.0f;
glmat[15] = 1.0f;

glMultMatrixf(&(glmat[0]));
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
10
model->Draw();

glPopMatrix();
glDisable( GL_TEXTURE_2D );
}

The Draw3DSMesh() function in Draw3DSShapes.cpp is set up basically the same as the


DrawMesh() function in DrawObjects.cpp, only we call Model_3DS::Draw() to draw the
final geometry instead of DrawTriangleList().

Draw3DSShapes.cpp

// Draw 3ds mesh


void Draw3DSMesh(NxShape* shape, Model_3DS* model)
{

model->Draw()

}

Model_3DS.cpp

void Model_3DS::Draw()
{
if (visible)
{
glPushMatrix();

// Move the model


glTranslatef(pos.x, pos.y, pos.z);

// Rotate the model


glRotatef(rot.x, 1.0f, 0.0f, 0.0f);
glRotatef(rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(rot.z, 0.0f, 0.0f, 1.0f);

glScalef(scale, scale, scale);

// Loop through the objects


for (int i = 0; i < numObjects; i++)
{
// Enable texture coordiantes, normals, and vertices arrays
if (Objects[i].textured)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (lit)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
11
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

// Point them to the objects arrays


if (Objects[i].textured)
glTexCoordPointer(2, GL_FLOAT, 0, Objects[i].TexCoords);
if (lit)
glNormalPointer(GL_FLOAT, 0, Objects[i].Normals);
glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertexes);

// Loop through the faces as sorted by material and draw them


for (int j = 0; j < Objects[i].numMatFaces; j ++)
{
// Use the material's texture
Materials[Objects[i].MatFaces[j].MatIndex].tex.Use();

glPushMatrix();

// Move the model


glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);

// Rotate the model


//glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);
//glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
//glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);

glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);


glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);

// Draw the faces using an index to the vertex array


glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces,
GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);

glPopMatrix();
}

// Show the normals?


if (shownormals)
{
// Loop through the vertices and normals and draw the normal
for (int k = 0; k < Objects[i].numVerts * 3; k += 3)
{
// Disable texturing
glDisable(GL_TEXTURE_2D);
// Disbale lighting if the model is lit
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
12
if (lit)
glDisable(GL_LIGHTING);
// Draw the normals blue
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);

// Draw a line between the vertex and the end of the normal
glBegin(GL_LINES);
glVertex3f(Objects[i].Vertexes[k], Objects[i].Vertexes[k+1],
Objects[i].Vertexes[k+2]);
glVertex3f(Objects[i].Vertexes[k]+Objects[i].Normals[k],
Objects[i].Vertexes[k+1]+Objects[i].Normals[k+1],
Objects[i].Vertexes[k+2]+Objects[i].Normals[k+2]);
glEnd();

// Reset the color to white


glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// If the model is lit then renable lighting
if (lit)
glEnable(GL_LIGHTING);
}
}
}

glPopMatrix();
}
}

…as opposed to:

DrawObjects.cpp

void DrawMesh(NxShape* mesh, bool useShapeUserData)


{

DrawTriangleList(nbTriangles, triangles, points);

}

Note also, for the wheel shapes, we swap out the wheel shape’s pose with the
“wheelShapePose” field on the shape’s user data to adjust for the suspension and rotation
of the wheel.

Draw3DSShapes.cpp

// Draw 3ds mesh


void Draw3DSMesh(NxShape* shape, Model_3DS* model)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
13
{
NxMat34 pose;
pose = shape->getGlobalPose();

// Get saved-away wheel shape pose to draw wheel shape at proper position
if (shape->getType() == NX_SHAPE_WHEEL)
{
NxWheelShape* ws = (NxWheelShape *)shape;
pose = ((ShapeUserData*)(ws->userData))->wheelShapePose;
}

}

4 Creating the 4-Wheeled Vehicle


We go back to InitNx() now to create our 4-wheeled vehicle, the buggy actor.

NxActor* buggy = NULL;



void InitNx()
{

// Create the buggy actor
buggy = CreateBuggy(NxVec3(0,3+30,0));
buggy->wakeUp(1e30);
gSelectedActor = buggy;

}

Look at CreateBuggy().

NxActor* CreateBuggy(const NxVec3& pos)


{
NxActor* actor = CreateBox(pos + NxVec3(0,0.5,0), NxVec3(2,0.25,1), 10);

NxTireFunctionDesc lngTFD;
lngTFD.extremumSlip = 1.0f;
lngTFD.extremumValue = 0.02f;
lngTFD.asymptoteSlip = 2.0f;
lngTFD.asymptoteValue = 0.01f;
lngTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc latTFD;
latTFD.extremumSlip = 1.0f;
latTFD.extremumValue = 0.02f;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
14
latTFD.asymptoteSlip = 2.0f;
latTFD.asymptoteValue = 0.01f;
latTFD.stiffnessFactor = 1000000.0f;

NxTireFunctionDesc slipTFD;
slipTFD.extremumSlip = 1.0f;
slipTFD.extremumValue = 0.02f;
slipTFD.asymptoteSlip = 2.0f;
slipTFD.asymptoteValue = 0.01f;
slipTFD.stiffnessFactor = 100.0f;

NxTireFunctionDesc medTFD;
medTFD.extremumSlip = 1.0f;
medTFD.extremumValue = 0.02f;
medTFD.asymptoteSlip = 2.0f;
medTFD.asymptoteValue = 0.01f;
medTFD.stiffnessFactor = 10000.0f;

// Front left wheel


NxWheelDesc wheelDesc;
wheelDesc.wheelApproximation = 10;
wheelDesc.wheelRadius = 0.5;
wheelDesc.wheelWidth = 0.3; // 0.1
wheelDesc.wheelSuspension = 1.0;
wheelDesc.springRestitution = 100;
wheelDesc.springDamping = 0.5;
wheelDesc.springBias = 0;
wheelDesc.maxBrakeForce = 1;
wheelDesc.position = NxVec3(1.5,0.5,-1.5);
wheelDesc.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel1 = AddWheelToActor(actor, &wheelDesc);

// Front right wheel


NxWheelDesc wheelDesc2;
wheelDesc2.wheelApproximation = 10;
wheelDesc2.wheelRadius = 0.5;
wheelDesc2.wheelWidth = 0.3; // 0.1
wheelDesc2.wheelSuspension = 1.0;
wheelDesc2.springRestitution = 100;
wheelDesc2.springDamping = 0.5;
wheelDesc2.springBias = 0;
wheelDesc2.maxBrakeForce = 1;
wheelDesc2.position = NxVec3(1.5,0.5,1.5);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
15
wheelDesc2.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel2 = AddWheelToActor(actor, &wheelDesc2);

// Rear left wheel


NxWheelDesc wheelDesc3;
wheelDesc3.wheelApproximation = 10;
wheelDesc3.wheelRadius = 0.5;
wheelDesc3.wheelWidth = 0.3; // 0.1
wheelDesc3.wheelSuspension = 1.0;
wheelDesc3.springRestitution = 100;
wheelDesc3.springDamping = 0.5;
wheelDesc3.springBias = 0;
wheelDesc3.maxBrakeForce = 1;
wheelDesc3.position = NxVec3(-1.5,0.5,-1.5);
wheelDesc3.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel3 = AddWheelToActor(actor, &wheelDesc3);

// Rear right wheel


NxWheelDesc wheelDesc4;
wheelDesc4.wheelApproximation = 10;
wheelDesc4.wheelRadius = 0.5;
wheelDesc4.wheelWidth = 0.3; // 0.1
wheelDesc4.wheelSuspension = 1.0;
wheelDesc4.springRestitution = 100;
wheelDesc4.springDamping = 0.5;
wheelDesc4.springBias = 0;
wheelDesc4.maxBrakeForce = 1;
wheelDesc4.position = NxVec3(-1.5,0.5,1.5);
wheelDesc4.wheelFlags = NX_WF_USE_WHEELSHAPE |
NX_WF_BUILD_LOWER_HALF | NX_WF_ACCELERATED |
NX_WF_AFFECTED_BY_HANDBRAKE | NX_WF_STEERABLE_INPUT;
wheel4 = AddWheelToActor(actor, &wheelDesc4);

// LOWER THE CENTER OF MASS


NxVec3 massPos = actor->getCMassLocalPosition();
massPos.y -= 1;
actor->setCMassOffsetLocalPosition(massPos);

return actor;
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
16
This is similar to the CreateTrike() function in the last lesson, with the addition of the
fourth wheel.

5 Conclusion
OK! Now, A complete 4-wheeled vehicle was built, and you can drive the car in
accordance with the hints in the console window.

6 Related Classes, Functions, and Parameters


NxParameter
NX_VISUALIZE_CONTACT_POINT
NX_VISUALIZE_CONTACT_NORMAL

NxWheelDesc
wheelDesc
wheelAxis
downAxis
wheelApproximation
wheelRadius
wheelWidth
wheelSuspension
springRestitution
springDamping
springBias
maxBrakeForce
frictionToFront
frictionToSide
position
wheelFlags

NxWheelFlag
NX_WF_USE_WHEELSHAPE
NX_WF_BUILD_LOWER_HALF
NX_WF_ACCELERATED
NX_WF_AFFECTED_BY_HANDBRAKE
NX_WF_STEERABLE_INPUT
NX_WF_BUILD_LOWER_HALF

NxWheelShapeDesc
materialIndex
localPose
suspension
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
17
radius
suspensionTravel
inverseWheelMass

NxWheelShape

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
18
Lesson 801 – Hardware Scene Manager

1 Introduction
In this lesson, we will set up a simple application to run on the hardware. If your code is
set up to run completely asynchronously, the transition only requires setting a single
scene flag.

2 Running on Hardware
You first need to make sure the hardware drivers and firmware are installed. The
firmware is the microcode that runs on the card and the driver talks to the card to upload

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
and reset the firmware. Go to the NVIDIA official website and walk through the
instructions provided.

3 The Simulation Type


Take a look at InitNx().

void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
gAllocator, &gErrorStream);
if (0 == gPhysicsSDK) return;

// Check for hardware availability


NxHWVersion hwCheck = gPhysicsSDK->getHWVersion();
if (hwCheck == NX_HW_VERSION_NONE)
{
printf("Unable to find any AGEIA PhysX hardware, this sample requires
hardware physics to run. Please make sure that you have correctly installed the latest
version of the AGEIA PhysX SDK.\n");
gPhysicsSDK->release();
gPhysicsSDK = 0;
exit(1);
}

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);

CreateScenario();
}

bool CreateScenario()
{
DestroyScenario();

// Create the scene

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = bHWScene? NX_SIMULATION_HW :
NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(0 == gScene)
{
bHWScene = !bHWScene;
sceneDesc.simType = bHWScene? NX_SIMULATION_HW :
NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(0 == gScene) return false;
}

……..

// Create compartment(HSM, managed hardware scene) attached to this scene


NxCompartmentDesc desc;
desc.type = NX_SCT_RIGIDBODY;
desc.deviceCode = NxU32(NX_DC_PPU_0);
gCompartmentHW = gScene->createCompartment(desc);

desc.deviceCode = NxU32(NX_DC_CPU);
gCompartmentSW = gScene->createCompartment(desc);

// Create objects
boxHW = CreateManagedBox(NxVec3(6, 0, 0), NxVec3(0.5, 1, 0.5), 20,
gCompartmentHW);
boxSW = CreateManagedBox(NxVec3(3, 0, 0), NxVec3(0.5, 1, 0.5), 20,
gCompartmentSW);
sphereHW = CreateManagedSphere(NxVec3(-6,0,0), 1, 10, gCompartmentHW);
sphereHW = CreateManagedSphere(NxVec3(-3,0,0), 1, 10, gCompartmentSW);
capsule = CreateManagedCapsule(NxVec3(0,0,0), 2, 1, 5, 0);

gSelectedActor = boxHW;

// Initialize HUD
InitializeHUD();

// Start the first frame of the simulation


StartPhysics();
return true;
}
We create two rigid body compartments in the scene. One compartment type is
NX_DC_PPU_0.

desc.deviceCode = NxU32(NX_DC_PPU_0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
Another type is NX_DC_CPU.
desc.deviceCode = NxU32(NX_DC_CPU);

Run the simulation and watch your demo run on the hardware.

4 Conclusion
Congratulations! You can now using the hardware to simulate your scene.

5 Related Classes, Functions, and Parameters


NxCompartmentDesc
type
deviceCode

NxCompartmentType
NX_SCT_RIGIDBODY
NX_SCT_FLUID
NX_SCT_CLOTH

NxDeviceCode
NX_DC_CPU
NX_DC_PPU_0
NX_DC_PPU_AUTO_ASSIGN

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
Lesson 802 – Multiple Scenes

1 Introduction
You can run multiple scenes simultaneously, both on hardware and software. In this
lesson, we demonstrate two software scenes running simultaneously.

2 Multiple Scenes
We create two scenes in InitNx().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
// Physics SDK globals
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScenes[2] = { NULL, NULL };
NxVec3 gDefaultGravity(0,-9.8,0);
...
// Actor globals
NxActor* groundPlanes[2] = { NULL, NULL };
NxActor* box = NULL;
NxActor* sphere = NULL;
NxActor* capsule = NULL;
NxActor* pyramid = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;
NxScene* gSelectedScene = NULL;
...
void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);

if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);

// Create two scenes


for (NxU32 i = 0; i < 2; i++)
{
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;

gScenes[i] = gPhysicsSDK->createScene(sceneDesc);

// Create the default material

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
NxMaterial* defaultMaterial = gScenes[i]->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);
}

// Create the objects in the two scenes


groundPlanes[0] = CreateGroundPlane(0);
groundPlanes[1] = CreateGroundPlane(1);

box = CreateBox(0, NxVec3(5,0,0), NxVec3(0.5,1,0.5), 20);


sphere = CreateSphere(0, NxVec3(0,0,5), 1, 10);
capsule = CreateCapsule(1, NxVec3(-5,0,0), 2, 0.5, 10);
pyramid = CreatePyramid(1, NxVec3(0,0,0), NxVec3(1,0.5,1.5), 10);

AddUserDataToActors(gScenes[0]);
AddUserDataToActors(gScenes[1]);

gSelectedActor = box;

// Update the time step


UpdateTime();
NxReal deltaTime = UpdateTime();

// Run collision and dynamics for delta time since the last frame for both scenes
for (NxU32 i = 0; i < 2; i++)
{
if (gScenes[i])
{
gScenes[i]->simulate(deltaTime);
gScenes[i]->flushStream();
}
}

// Default gScene to gScenes[0]


gSelectedScene = gScenes[0];
}

We have two scenes, gScenes[0] and gScenes[1].

NxScene* gScenes[2] = { NULL, NULL };

We need to initialize and update each separately. We create a ground plane for each
scene.

// Create the objects in the two scenes


groundPlanes[0] = CreateGroundPlane(0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
groundPlanes[1] = CreateGroundPlane(1);

The actor creation functions in the lesson take an index to specify which scene the actor
is being created for.

NxActor* CreateGroundPlane(int sceneIndex)


{
// Create a plane with default descriptor
NxPlaneShapeDesc planeDesc;
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&planeDesc);
return gScenes[sceneIndex]->createActor(actorDesc);
}

We add a box and a sphere to gScene[0] and a capsule and a pyramid to gScene[1].

box = CreateBox(0, NxVec3(5,0,0), NxVec3(0.5,1,0.5), 20);


sphere = CreateSphere(0, NxVec3(0,0,5), 1, 10);
capsule = CreateCapsule(1, NxVec3(-5,0,0), 2, 0.5, 10);
pyramid = CreatePyramid(1, NxVec3(0,0,0), NxVec3(1,0.5,1.5), 10);

We kick off the simulation of both scenes.

// Run collision and dynamics for delta time since the last frame for both scenes
for (NxU32 i = 0; i < 2; i++)
{
if (gScenes[i])
{
gScenes[i]->simulate(deltaTime);
gScenes[i]->flushStream();
}
}

The initial scene objects will be selected from by the user is gScenes[0].

// Default gSelectedScene to gScenes[0]


gSelectedScene = gScenes[0];

Run the demo. You will be able to select between the box and the sphere at first to move
around. These objects are in gScenes[0]. You will be able to move these objects through
the objects in gScenes[1], the pyramid and the capsule. The two scenes run completely
independently of each other. The box and sphere rest on groundPlanes[0] while the
pyramid and the sphere rest on groundPlanes[1].

Hit “g” to switch scenes you are selecting actors from.

// Focus actor

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
NxActor* gSelectedActor = NULL;
NxScene* gSelectedScene = NULL;
...
void KeyboardUpCallback(unsigned char key, int x, int y)
{
gKeys[key] = false;

switch (key)
{
...
case 'g': {
if (gSelectedScene == gScenes[0])
{
gSelectedActor = pyramid;
gSelectedScene = gScenes[1];
}
else
{
gSelectedActor = box;
gSelectedScene = gScenes[0];
}
break;
}
...
}
}

Hitting “g” switches the focus actor and the focus scene.

3 Maintaining Two Scenes


You will see throughout the code that we need to account for the fact that we have two
scenes instead of one.

Take RenderActors() for instance:

void RenderActors(bool shadows)


{
for (NxU32 i = 0; i < 2; i++)
{
if (gScenes[i])
{
// Render all the actors in the scene
NxU32 nbActors = gScenes[i]->getNbActors();
NxActor** actors = gScenes[i]->getActors();
while (nbActors--)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
{
NxActor* actor = *actors++;
DrawActor(actor);

// Handle shadows
if (shadows)
{
DrawActorShadow(actor);
}
}
}
}
}

Here, we need to cycle through each scene and the list of actors in each scene. In other
areas of the code where we previously referenced a single scene, we now loop through
multiple scenes. You will need to do this for as many scenes as you create.

4 Conclusion
You have now learned how to build multiple scenes and simulate each of them.

5 Related Classes, Functions, and Parameters

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
Lesson 803 – PhysX FX Integration

1 Introduction
This is a demonstration of how to integrate a PhysX scene so that it runs alongside an
existing gameplay physics system. We call this a "PhysX FX" integration since it
enables you to add complicated effects physics to your game without interfering with
your gameplay physics.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
We're going to be releasing many examples that use this method, mostly large-scale
physics effects that accompany small-scale gameplay physics. For now, here's a simple
demonstration of the technique, "Lesson 803: PhysX FX Integration".

2 PhysX FX Integration
When you run the demo, you'll see a large sphere suspended above a stack of small boxes
resting on a plane. The large sphere and the plane are actors in gScene[0]. The stack of
boxes are actors in gScene[1]. You'll notice the sphere falls and smashes the stack of
boxes. We have set up two scenes running in parallel with one another and made it so the
first scene can interact with the second, though not vice versa.

Here's how this is done:

Create two scenes. One will be the gameplay scene, gScene[0]. The other will be the
effects scene, gScene[1].

// Physics SDK globals


NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScenes[2] = { NULL, NULL };
...
// Actor globals
NxActor* groundPlane = NULL;
NxActor* sphere = NULL;
NxActor** boxes = NULL;
...
void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, gAllocator);

if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1);

// Create two scenes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
for (NxU32 i = 0; i < 2; i++)
{
...
gScenes[i] = gPhysicsSDK->createScene(sceneDesc);
...
}

// Create the objects in the two scenes


groundPlane = CreateGroundPlane(0);
sphere = CreateSphere(0, NxVec3(0,6,0), 1, 50);
boxes = CreateStack(1, NxVec3(0,0,0), NxVec3(6,3,6), NxVec3(0.25,0.25,0.25), 10);

// Create the PhysX FX actors


CreatePFXActors();

UpdateTime();

}

The groundPlane and sphere actors are part of gScene[0].

groundPlane = CreateGroundPlane(0);
sphere = CreateSphere(0, NxVec3(0,6,0), 1, 50);

The actors in the stack of boxes are part of gScene[1].

boxes = CreateStack(1, NxVec3(0,0,0), NxVec3(6,3,6), NxVec3(0.25,0.25,0.25), 10);

The relevant actor creation functions have a scene index passed to them and create the
actor for the scene corresponding to the number passed into the function.

NxActor* CreateGroundPlane(int sceneIndex)


{
// Create a plane with default descriptor
NxPlaneShapeDesc planeDesc;
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&planeDesc);
return gScenes[sceneIndex]->createActor(actorDesc);
}

NxActor* CreateBox(int sceneIndex, const NxVec3& pos, const NxVec3& boxDim,


const NxReal density)
{
// Add a single-shape actor to the scene
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
// The actor has one shape, a box
NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(boxDim.x,boxDim.y,boxDim.z);
boxDesc.localPose.t = NxVec3(0,boxDim.y,0);
actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = density;
actorDesc.globalPose.t = pos;

return gScenes[sceneIndex]->createActor(actorDesc);
}

NxActor* CreateSphere(int sceneIndex, const NxVec3& pos, const NxReal radius, const
NxReal density)
{
// Add a single-shape actor to the scene
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// The actor has one shape, a sphere


NxSphereShapeDesc sphereDesc;
sphereDesc.radius = radius;
sphereDesc.localPose.t = NxVec3(0,radius,0);
actorDesc.shapes.pushBack(&sphereDesc);

actorDesc.body = &bodyDesc;
actorDesc.density = density;
actorDesc.globalPose.t = pos;
return gScenes[sceneIndex]->createActor(actorDesc);
}

NxActor** CreateStack(int sceneIndex, const NxVec3& pos, const NxVec3& stackDim,


const NxVec3& boxDim, NxReal density)
{
NxActor** stack = new NxActor*[(int)(8*stackDim.x*stackDim.y*stackDim.z)];

NxVec3 offset = NxVec3(boxDim.x,0,boxDim.z) + pos;

int count = 0;
for (int i = -stackDim.x; i < stackDim.x; i++)
{
for (int j = 0; j < stackDim.y; j++)
{
for (int k = -stackDim.z; k < stackDim.z; k++)
{
NxVec3 boxPos = NxVec3(i*boxDim.x*2,j*boxDim.y*2,k*boxDim.z*2);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
stack[count++] = CreateBox(sceneIndex, boxPos + offset, boxDim, density);
}
}
}

return stack;
}

Once the actors for both scenes are created, we build the PhysX FX actors. These are
kinematic actors in gScene[1] that match the descriptions of all the actors in gScene[0]
and shadow their positions and orientations precisely.

void InitNx()
{
...
// Create the PhysX FX actors
CreatePFXActors();
...
}

CreatePFXActors() calls AddPFXActor(), which creates an array of PFXActors, one for


each actor in gScene[0].

// PhysX FX actor class


class PFXActor
{
public:
PFXActor() {}
~PFXActor() {}

NxActor* actor[2];
};
...
// PhysX FX actor globals
PFXActor** pfxActor;
...
void CreatePFXActors()
{
NxU32 nbActors = gScenes[0]->getNbActors();
NxActor** actors = gScenes[0]->getActors();

pfxActor = new PFXActor*[nbActors];

for (NxU32 i = 0; i < nbActors; i++)


{
AddPFXActor(i, actors[i]);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
}

AddPFXActor() gets called for each actor in gScene[0]. This points the pfxActor's
actor[0] to an actor in gScene[0] and creates the pfxActor's corresponding kinematic
actor[1].

void AddPFXActor(int i, NxActor* actor)


{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

pfxActor[i] = new PFXActor;


pfxActor[i]->actor[0] = actor;
actor->saveToDesc(actorDesc);

NxU32 nbShapes = actor->getNbShapes();


NxShape** shapes = actor->getShapes();
while (nbShapes--)
{
NxShape* shape = shapes[nbShapes];
switch (shape->getType())
{
case NX_SHAPE_PLANE:
{
NxPlaneShapeDesc planeDesc;
shape->isPlane()->saveToDesc(planeDesc);
actorDesc.shapes.pushBack(&planeDesc);
}
break;
case NX_SHAPE_BOX:
{
NxBoxShapeDesc boxDesc;
shape->isBox()->saveToDesc(boxDesc);
actorDesc.shapes.pushBack(&boxDesc);
}
break;
case NX_SHAPE_SPHERE:
{
NxSphereShapeDesc sphereDesc;
shape->isSphere()->saveToDesc(sphereDesc);
actorDesc.shapes.pushBack(&sphereDesc);
}
break;
case NX_SHAPE_CAPSULE:
{
}
break;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
case NX_SHAPE_CONVEX:
{
}
break;
case NX_SHAPE_MESH:
{
}
break;
}
}

if (actor->isDynamic())
actorDesc.body = &bodyDesc;
else
actorDesc.body = NULL;

pfxActor[i]->actor[1] = gScenes[1]->createActor(actorDesc);

if (actor->isDynamic())
{
pfxActor[i]->actor[1]->raiseBodyFlag(NX_BF_KINEMATIC);
}
}

For each actor in gScene[0], the actor[0] member of the pfxActor, we save away a copy
of its shape descriptors and use these to build the kinematic actor in gScene[1], the
actor[1] member of the pfxActor. For our demo, we have just filled out the code for the
plane, sphere and box shapes. The other shape types get filled out in the same manner in
a full integration.

case NX_SHAPE_BOX:
{
NxBoxShapeDesc boxDesc;
shape->isBox()->saveToDesc(boxDesc);
actorDesc.shapes.pushBack(&boxDesc);
}
break;

If the actor[0] is static, we leave actor[1] static. Otherwise, we make actor[1] kinematic.

if (actor->isDynamic())
actorDesc.body = &bodyDesc;
else
actorDesc.body = NULL;

pfxActor[i]->actor[1] = gScenes[1]->createActor(actorDesc);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


7
if (actor->isDynamic())
{
pfxActor[i]->actor[1]->raiseBodyFlag(NX_BF_KINEMATIC);
}

In our demo, the kinematic and static "shadow actors" for gScene[1] are copies of the
large sphere and the plane created for gScene[0]. They now exist alongside the stack of
dynamic boxes that have already been created for gScene[1].

After we create these actors, we need to make sure to update them so they match the
positions and orientations of the dynamic actors they're shadowing.

void RenderCallback()
{
if (!bPause)
{
NxReal gDeltaTime = UpdateTime();

// Software and hardware scenes both run asynchronously


if (gScenes[0] && gScenes[1])
{
GetPhysicsResults(0);
GetPhysicsResults(1);
ProcessInputs();
StartPhysics(0, gDeltaTime);
StartPhysics(1, gDeltaTime);
}
}
...
}
...
void ProcessInputs()
{

// Update the hardware kinematic actors shadowing the software actors
UpdatePFXActors();

}

void UpdatePFXActors()
{
NxU32 nbActors = gScenes[0]->getNbActors();
NxActor** actors = gScenes[0]->getActors();

for (NxU32 i = 0; i < nbActors; i++)


{
if (pfxActor[i]->actor[0]->isDynamic())

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


8
{
pfxActor[i]->actor[1]->moveGlobalPose(pfxActor[i]->actor[0]-
>getGlobalPose());
}
}
}

The key code is the line where we set the global pose of actor[1] of the pfxActor to the
global pose of actor[0] of the pfxActor.

pfxActor[i]->actor[1]->moveGlobalPose(pfxActor[i]->actor[0]-
>getGlobalPose());

In our demo, the sphere on the plane is our gameplay scene and the stack of boxes is our
effects scene. Try moving the sphere through the stack of boxes. You'll notice the small
boxes fall and bounce on the gameplay plane and are pushed aside by the gameplay
sphere, but do not affect either of the gameplay objects themselves.

You can extrapolate this to something like a first-person shooter, where there are a few
gameplay physics objects, crates, barrels, walls, etc., and a much larger number of effects
physics objects, debris and dust on the crates, fog flowing through the level, blood on the
walls, etc. Using this integation, the game will play exactly the same as before, with the
additional effects objects reacting to the game objects.

Something else that becomes possible is the creation of a gameplay scene that runs at a
much higher frequency and in parallel with a less “update-critical” effects scene. Or
multiple effects scenes running at different rates in parallel with the main scene as well as
each other.

Note that we have used a PhysX scene for our gameplay scene, but this could be a scene
using any other physics SDK or a physics or collision system of your own design.

3 Conclusion

4 Related Classes, Functions, and Parameters


none

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


9
Lesson 804 – User Controlled Threads

1 Introduction
We can do threading in PhysX, either creating multiple threads to be run on a single
processor or on separate processors.

2 User Controlled Threads


There are four threading interfaces available in the PhysX SDK 2.6. These are:

1. An application supplied scheduler (custom scheduler)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


1
2. Polling, i.e., the SDK manages the work queue and the application can poll a function
to perform work.
3. SDK managed threading – at present this is likely to yield the best performance
4. No threading

Fine grained threading is available only on the XBox 360 at the moment. While these
interfaces are available on other platforms, they will not provide any performance gains.
The graphics wrapper used on the XBox is quite slow and performance comparisons
should not be made with graphics enabled. This code expects to find a core dump
named MyCoreDump.cda in “..\TrainingPrograms\Core_Dump”.

3 Unsetting Running the Simulation in a Separate Thread


By default the SDK runs the physics on a seperate thread to the user thread (i.e., the
thread which calls the API). However if this flag is disabled, then the simulation is run
in the thread which calls #simulate().

We disable the default setting, running the physics in a separate thread, by unsetting
NxSceneFlags::NX_SF_SIMULATE_SEPARATE_THREAD on the scene.

void InitNx()
{
...
// Create the scene
NxSceneDesc sceneDesc;
...
// Unset running the simulation in a separate thread...
sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD;
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
}

If we leave things like this, the simulation will run on the same thread as the main
application.

4 Custom Scheduler
To enable multithreading with a custom scheduler, use #define
THREAD_CUSTOM_SCHEDULER.

#define THREAD_CUSTOM_SCHEDULER
...
// Thread stuff
static CustomScheduler gCustomScheduler;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


2
...
void InitNx()
{
...
// Create the scene
NxSceneDesc sceneDesc;
...
#if defined(THREAD_CUSTOM_SCHEDULER)

sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD;
sceneDesc.customScheduler = &gCustomScheduler;
...
#endif
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
}

The NxSceneFlags:: NX_SF_ENABLE_MULTITHREAD flag sets up multithreading to


work with gCustomScheduler as the multithreading scheduler.

The CustomScheduler object is defined in CustomScheduler.h.

CustomScheduler.h

class CustomScheduler : public NxUserScheduler


{
public:

CustomScheduler();
~CustomScheduler();

void CreateThreads(int count);


void KillThreads();

virtual void addTask(NxTask *task);


virtual void waitTasksComplete();

private:

static DWORD __stdcall threadFuncStatic(LPVOID userParam);


DWORD threadFunc();

bool executeTask();

CRITICAL_SECTION taskQueueCS;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


3
std::deque<NxTask *> taskQueue;

int threadCount;
HANDLE *threadHandles;
HANDLE quitEvent;
HANDLE pendingEvent;

volatile long workingCount;


};

The user calls the custom scheduler functions to addjust, like we've done in InitNx().
We call CustomScheduler::CreateThreads().

void InitNx()
{
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
// Kick off the threading
#if defined(THREAD_CUSTOM_SCHEDULER)

gCustomScheduler.CreateThreads(2);
...
#endif

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

CustomScheduler.cpp

void CustomScheduler::CreateThreads(int count)


{
KillThreads();

threadCount=count;
threadHandles=new HANDLE[threadCount];

quitEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
assert(quitEvent!=NULL);

pendingEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
assert(pendingEvent!=NULL);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


4
workingCount=0;

InitializeCriticalSection(&taskQueueCS);

for (int i=0;i<threadCount;i++)


{
threadHandles[i]=CreateThread(NULL,0,threadFuncStatic,this,0,NULL);
assert(threadHandles[i]!=NULL);
}
}

This will create two threads explicitly for the simulation to run on.

5 Polling
To enable multithreading with polling, use #define THREAD_POLLING.

#define THREAD_POLLING
...
// Thread stuff
...
static PollingThreads gPollingThreads;
...
void InitNx()
{
...
// Create the scene
NxSceneDesc sceneDesc;
...
#elif defined(THREAD_POLLING)

sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD;
...
#endif
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
}

The NxSceneFlags:: NX_SF_ENABLE_MULTITHREAD flag sets up multithreading.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


5
The user calls PollingThreads::CreateThreads() and PollingThreads::ResetPollForWork()
to create the threads.

void InitNx()
{
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
// Kick off the threading
...
#elif defined(THREAD_POLLING)

gPollingThreads.CreateThreads(2, gScene);

// We must reset the polling threads so they are ready for the next run
gPollingThreads.ResetPollForWork();
...
#endif

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

The PollingThreads object is defined in PollingThreads.h.

class PollingThreads
{
public:

PollingThreads()
{
threadCount=0;
threadArray=NULL;
quitEvent=NULL;
simRunningEvent=NULL;
allWaitingEvent=NULL;
waitingThreads=0;
scene=NULL;
}

~PollingThreads()
{
KillThreads();
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


6
void CreateThreads(int count,NxScene *newScene);
void KillThreads();

void ResetPollForWork();

private:
static DWORD __stdcall threadFuncStatic(LPVOID userParam);
DWORD threadFunc();

int threadCount;
HANDLE *threadArray;

HANDLE quitEvent;
HANDLE simRunningEvent;

LONG workingCounter;
CRITICAL_SECTION shieldCS;
HANDLE workingMutexEvent;

NxScene *scene;

};

We use a couple of functions from NxScene, NxScene::pollForWork() and


NxScene::resetPollForWork().

nxscene.h

NxThreadPollResult NxScene::pollForWork(NxThreadWait waitType)

This returns NX_THREAD_SIMULATION_END if the simulation step ended,


NX_THREAD_NOWORK if no work is available, NX_THREAD_MOREWORK if
there may be work remaining to execute. The function waits for work to execute
according the the waitType argument. The function will continue to return
NX_THREAD_SIMULATION_END until the #resetSimulation() method is called.

NxThreadWait waitType specifies how long the function should wait for work.

NxThreadWait

NX_WAIT_NONE - The poll function will return immediatly if there is no work


available.
NX_WAIT_SIMULATION_END - The poll function will wait until the end of the
simulation tick for work.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


7
NxThreadPollResult return valueInforms the user about the reason for returning.

NxThreadPollResult
NX_THREAD_NOWORK - There is no work to execute at the time the function was
called.
NX_THREAD_MOREWORK - There may be more work waiting for execution.
NX_THREAD_SIMULATION_END - The function returned because the simulation
tick finished.
NX_THREAD_SCENE_DESTROYED - The function returned because the
simulation is being destroyed.

void NxScene::resetPollForWork()

This resets parallel simulation so that threads calling pollForWork do not return
NX_THREAD_SIMULATION_END, but instead wait for a new simulation step to start.

In between NxScene::fecthResults() and NxScene::simulate(), we call NxScene::

Lesson805.cpp

void ProcessInputs()
{
...
#if defined(THREAD_POLLING)
// We must reset the polling threads so they are ready for the next run
gPollingThreads.ResetPollForWork();
#endif
...
}

PollingThreads.cpp

void PollingThreads::ResetPollForWork()
{
if (threadArray==NULL)
return;

WaitForSingleObject(workingMutexEvent,INFINITE);

//release threads to start working again


scene->resetPollForWork();

SetEvent(simRunningEvent);
SetEvent(workingMutexEvent);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


8
}

6 SDK Managed
To enable SDK-managed multithreading, use #define THREAD_SDK.

void InitNx()
{
...
// Create the scene
NxSceneDesc sceneDesc;
...
#elif defined(THREAD_SDK)

sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD;
sceneDesc.internalThreadCount = 2;
...
#endif
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
}

The user doesn't need to call anything after the scene is created. The user will be using
neither the a scheduler nor polling. The SDK will manage the threads.

void InitNx()
{
...
gScene = gPhysicsSDK->createScene(sceneDesc);
...
// Kick off the threading
...
#elif defined(THREAD_SDK)
// Nothing to do, SDK manages threads
#endif

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


9
7 Conclusion
You should now know how to kick off threads for use by the PhysX SDK, either
controlled by a custom scheduler, polling, or SDK-managed.

8 Related Classes, Functions, and Parameters


NxScene
pollForWork()
resetPollForWork()

NxThreadWait
NX_WAIT_NONE
NX_WAIT_SIMULATION_END

NxThreadPollResult
NX_THREAD_NOWORK
NX_THREAD_MOREWORK
NX_THREAD_SIMULATION_END
NX_THREAD_SCENE_DESTROYED

Copyright (c) 2008 NVIDIA Corporation. All rights reserved http://www.NVIDIA.com


10
Lesson 901 – Fluid Creation

1 Introduction
Real-time fluid simulation is a brand new addition to games. Our fluid is designed to be
fully integrated with the physics environment. It will collide and interact with the
dynamic and static actors in the scene in a physically realistic manner.

With fluids, you can create cascading waterfalls, jars full of liquid that break and spill,
and the obligatory blood spattering onto a wall and dripping down after blasting an
enemy.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Fluid Creation
In InitNx(), we build a box on top of a flat surface. To the right of the box, we create
our fluid with the CreateFluid() function.

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;
NxActor* flatSurface = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

// Fluid globals
NxFluid* fluid = NULL;

// Fluid particle globals


NxVec3 gParticleBuffer[10000];
NxU32 gParticleBufferCap = 10000;
NxU32 gParticleBufferNum = 0;

...
void InitNx()
{
...
// Create the objects in the scene
groundPlane = CreateGroundPlane();
flatSurface = CreateFlatSurface(NxVec3(0,0.5,0), 6, 6, 2);
box = CreateBox(NxVec3(1,2,0),NxVec3(0.5,0.5,0.5),10);

fluid = CreateFluid(NxVec3(0,3,0), 20, 0.1, gScene)


...
}

The fluid class represents a dynamic sized set of particles that interact with each other.
Associated with a fluid is a, possibly empty, list of emitters. For now, we render our fluid
particles simply as red boxes. Take a look at CreateFluid().

// Create a set of particles


gParticleBufferNum = 0;
NxReal rad = sideNum*distance*0.5f;
for (NxU32 i=0; i<sideNum; i++)
{
for (NxU32 j=0; j<sideNum; j++)
{
for (NxU32 k=0; k<sideNum; k++)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
{
NxVec3 p = NxVec3(i*distance,j*distance,k*distance);
if (p.distance(NxVec3(rad,rad,rad)) < rad)
{
p += pos - NxVec3(rad,rad,rad);
gParticleBuffer[gParticleBufferNum++] = p;
}
}
}
}

// Set structure to pass particles, and receive them after every simulation step
NxParticleData particles;
particles.numParticlesPtr = &gParticleBufferNum;
particles.bufferPos = &gParticleBuffer[0].x;
particles.bufferPosByteStride = sizeof(NxVec3);

// Create a fluid descriptor


NxFluidDesc fluidDesc;
fluidDesc.maxParticles = gParticleBufferCap;
fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;
fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;
fluidDesc.motionLimitMultiplier = MOTION_LIMIT_MULTIPLIER;
fluidDesc.packetSizeMultiplier = PACKET_SIZE_MULTIPLIER;
fluidDesc.stiffness = 50;
fluidDesc.viscosity = 22;
fluidDesc.restDensity = 1000;
fluidDesc.damping = 0;
// There are some API changes since 280 version, Fluid collision coefficients have been //
renamed,
// E.g. NxFluidDesc::dynamicCollisionAdhesion is named
// NxFluidDesc::dynamicFrictionForDynamicShapes.
#if NX_SDK_VERSION_NUMBER < 280
fluidDesc.staticCollisionRestitution = 0.162f;
fluidDesc.staticCollisionAdhesion = 0.146f;
fluidDesc.dynamicCollisionRestitution = 0.5f;
fluidDesc.dynamicCollisionAdhesion = 0.5f;
#else
fluidDesc.restitutionForStaticShapes = 0.162f;
fluidDesc.dynamicFrictionForStaticShapes = 0.146f;
fluidDesc.restitutionForDynamicShapes = 0.5f;
fluidDesc.dynamicFrictionForDynamicShapes = 0.5f;
#endif
fluidDesc.restitutionForStaticShapes = 0.4;
fluidDesc.dynamicFrictionForStaticShapes= 0.03;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
fluidDesc.simulationMethod = NX_F_SPH;
//NX_F_NO_PARTICLE_INTERACTION;

fluidDesc.initialParticleData = particles;
fluidDesc.particlesWriteData = particles;

if(!bHardwareFluid)
fluidDesc.flags &= ~NX_FF_HARDWARE;

gFluid = gScene->createFluid(fluidDesc);
assert(gFluid != NULL);
return gFluid;

}

We create a bunch of fluid particles within a sphere of radius 1.0 meters, centered at
(0,3,0). This is done by iterating over all particles in a box of side 2.0 meters
(sideNum*distance = 20*0.1 = 2.0), with one particle every 0.1 meters, and taking only
those particles a distance half the size of the box from the center of the box.

// Create a set of particles


gParticleBufferNum = 0;
NxReal rad = sideNum*distance*0.5f;
for (NxU32 i=0; i<sideNum; i++)
{
for (NxU32 j=0; j<sideNum; j++)
{
for (NxU32 k=0; k<sideNum; k++)
{
NxVec3 p = NxVec3(i*distance,j*distance,k*distance);
if (p.distance(NxVec3(rad,rad,rad)) < rad)
{
p += pos - NxVec3(rad,rad,rad);
gParticleBuffer[gParticleBufferNum++] = p;
}
}
}
}

The particle coordinates are stored in gParticleBuffer. We attach gParticleBuffer to the


“bufferPos” field of a particle data structure, and fill out the stride of the coordinates,
attach the number of particles, and fill outthe maximum number of particles.

// Set structure to pass particles, and receive them after every simulation step
NxParticleData particles;
particles.numParticlesPtr = &gParticleBufferNum;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
particles.bufferPos = &gParticleBuffer[0].x;
particles.bufferPosByteStride = sizeof(NxVec3);

This particle data structure then gets attached to the fluid descriptor's “initialParticleData”
and “particlesWriteData” fields, indicating this is the data indicating the initial positions
of the particles as well as where their updated positions will be written.

// Create a fluid descriptor


NxFluidDesc fluidDesc;
...
fluidDesc.initialParticleData = particles;
fluidDesc.particlesWriteData = particles;

The fluid particle data will now get written to the NxParticleData structure (the displayed
data) each frame of the simulation

3 Fluid Parameters
We have our particle data, the sphere of particles, set in our fluid descriptor. Now we
flesh out the rest of the descriptor with data describing the physical properties of our
fluid.

// Create a fluid descriptor


NxFluidDesc fluidDesc;
fluidDesc.maxParticles = gParticleBufferCap;
fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;
fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;
fluidDesc.motionLimitMultiplier = MOTION_LIMIT_MULTIPLIER;
fluidDesc.packetSizeMultiplier = PACKET_SIZE_MULTIPLIER;
fluidDesc.stiffness = 50;
fluidDesc.viscosity = 22;
fluidDesc.restDensity = 1000;
fluidDesc.damping = 0;
// There are some API changes since 280 version, Fluid collision coefficients have been //
renamed,
// E.g. NxFluidDesc::dynamicCollisionAdhesion is named
// NxFluidDesc::dynamicFrictionForDynamicShapes.
#if NX_SDK_VERSION_NUMBER < 280
fluidDesc.staticCollisionRestitution = 0.162f;
fluidDesc.staticCollisionAdhesion = 0.146f;
fluidDesc.dynamicCollisionRestitution = 0.5f;
fluidDesc.dynamicCollisionAdhesion = 0.5f;
#else
fluidDesc.restitutionForStaticShapes = 0.162f;
fluidDesc.dynamicFrictionForStaticShapes = 0.146f;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
fluidDesc.restitutionForDynamicShapes = 0.5f;
fluidDesc.dynamicFrictionForDynamicShapes = 0.5f;
#endif
fluidDesc.restitutionForStaticShapes = 0.4;
fluidDesc.dynamicFrictionForStaticShapes= 0.03;
fluidDesc.simulationMethod = NX_F_SPH;
//NX_F_NO_PARTICLE_INTERACTION;

We take each of these singly.

#define REST_PARTICLES_PER_METER 10

fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;

restParticlesPerMeter is the particle resolution of the fluid. This is the number of particles
of fluid that exist per meter when the fluid is at rest. An emitter releasing a fluid will
release it at this density.

#define KERNEL_RADIUS_MULTIPLIER 1.8

fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;

kernelRadiusMultiplier gives the distance at which particles can influence each other. In
this case, we are saying a particle will influence all particles within 1.8 rest particle radii,
or 1.8 / (2*restParticlesPerMeter) = 1.8 / (2*10) = 1.8 / 20 = 0.09 meter.

fluidDesc.stiffness = 50;

The stiffness is the fluid's resistance to compression, much like a gas constant or the
physical constant for a compressible fluid.

fluidDesc.viscosity = 22;

This is the viscosity of the fluid, describing the level of attraction of the fluid particles.
Fluid with a low viscosity has a low inter-particle attraction. Fluid with a high viscosity
has a high inter-particle attraction.

fluidDesc.damping = 0;

This damps the velocity of the particles, simulation inter-particle friction. It is the
amount of energy loss within a fluid that will eventually slow its particles to a stop.

fluidDesc.staticCollisionRestitution = 0.162;
or
fluidDesc.restitutionForStaticShapes = 0.162f;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
staticCollisionRestitution (this parameter’s name has been changed into
restitutionForStaticShapes since 2.8.0 version) is the coefficient of restitution of the
fluid with static shapes. This is much the same as the coefficient of restitution that
dynamic objects possess through their associated materials.

fluidDesc.staticCollisionAdhesion = 0.146;
or
fluidDesc.dynamicFrictionForStaticShapes = 0.146;

staticCollisionAdhesion (this parameter’s name has been changed into


dynamicFrictionForStaticShapes since 2.8.0 version) defines the “friction” of the fluid
regarding the surface of a static shape. This is the energy the fluid loses as it slides over
a static surface.

There is dynamic collision restitution and adhesion as well. The restitution and adhesion
parameters of the fluid are analogous to the restitution and friction parameters of dynamic
actors via their materials.

fluidDesc.simulationMethod = NX_F_SPH;

The simulation method flags whether the particles interact with each other or not.
Particles can be treated in two ways, either they are simulated with interparticular forces
(SPH) or they are simulated independently. The NX_F_SPH flag specifies our fluid
particles will have interparticular forces influencing each other. If we do not want any
interparticle interaction, we use the NX_F_NO_PARTICLE_INTERACTION flag in
place of the NX_F_SPH flag.

Finally, we pass our fluid descriptor into NxScene::createFluid() and the SDK builds the
fluid particles with the specified properties.

gFluid = gScene->createFluid(fluidDesc);

4 Drawing the Fluid


In fluid lessons, our fluid is merely a collection of points. We represent these points with
small boxes. Look at RenderCallback().

void RenderCallback()
{
// Clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

ProcessCameraKeys();
SetupCamera();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

// Display scene
RenderActors(bShadows);

if(gFluid)
RenderFluid();

if (bForceMode)
DrawForce(gSelectedActor, gForceVec, NxVec3(1,1,0));
else
DrawForce(gSelectedActor, gForceVec, NxVec3(0,1,1));
gForceVec = NxVec3(0,0,0);

// Update and render HUD


hud.Update(bHardwareScene, bPause, "");
hud.Render();

glFlush();
glutSwapBuffers();
}

Right after RenderActors() we call RenderFluid().

void RenderFluid()
{
// Keep physics & graphics in sync
for (NxU32 p=0; p<gParticleBufferNum; p++)
{
NxVec3& particle = gParticleBuffer[p];

glPushMatrix();
glColor4f(1.0f,0.0f,0.0f, 1.0f);
glTranslatef(particle.x,particle.y,particle.z);
glutSolidCube(0.1); // glutSolidSphere(0.1,8,5);
glColor4f(1.0f,1.0f,1.0f, 1.0f);
glPopMatrix();
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
We use the “glutSolidBox()” call to render each fluid particle as a red box. You can use
other primitives to represent the fluid particles, for instance, you can use
“glutSolidSphere(0.1,8,5)” to render each fluid particle as a sphere.

5 Conclusion and Playing Around


You have created your first fluid. Congratulations! Splash the big ball of fluid around
on the flat surface with the box actor using the “umijkl” keys.

(Programmer’s Note: For some reason the hardware box actors do not collide with the
mesh surface built by CreateFlatSurface().

6 Related Classes, Functions, and Parameters


NxParticleData
numParticlesPtr
bufferPos
bufferPosByteStride

NxFluidDesc
restParticlesPerMeter
kernelRadiusMultiplier
stiffness
viscosity
restDensity
damping
staticCollisionRestitution
staticCollisionAdhesion
simulationMethod
initialParticleData
particlesWriteData

NxFluidSimulationMethod
NX_F_SPH
NX_F_NO_PARTICLE_INTERACTION

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
Lesson 902 – Fluid Emitters

1 Introduction
In the previous lesson, we created a fluid by generating its particles all at once up front.
Now we build a fluid emitter, a point source that will generate the particles for us. We set
the fluid emitter's position and direction in the world as well as various parameters to
adjust the rate at which the emitter spawns particles, the velocity they spawn with, the
lifetime of the fluid particle, the shape of the emitter, etc.

Fluid emitters are very useful as they are a very natural and intuitive means of
introducing fluid into your games.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Fluid Emitters
Take a look at the new InitNx().

// Actor globals
NxActor* groundPlane = NULL;
NxActor* box = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

// Fluid particle globals


NxVec3 gParticleBuffer[10000];
NxU32 gParticleBufferCap = 10000;
NxU32 gParticleBufferNum = 0;

// Fluid particle globals


NxU32 gNumParticles = 0;
NxVec3* gParticles = 0;

// Fluid globals
NxFluid* fluid = NULL;
NxFluidEmitter* fluidEmitter = 0;
...
void InitNx()
{
...
// Create the objects in the scene
groundPlane = CreateGroundPlane();

fluidEmitter = CreateFluidEmitter(0.25, 0.25);...


}

The fluid emitter is attached to the fluid, so when you create a fluid, you can create it
with or without an emitter. Take a look at CreateFluidEmitter().

NxFluidEmitter* CreateFluidEmitter(const NxReal dimX, const NxReal dimY)


{
fluid = CreateFluid();
assert(fluid);

NxQuat q;
q.fromAngleAxis(-90,NxVec3(1,0,0));
NxMat34 mat;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
mat.M.fromQuat(q);
mat.t = NxVec3(0,1.2,0);
// Create frame actor
NxActor* mBox = CreateBox(NxVec3(0,1,0),NxVec3(1.0,1.0,1.0),2);
gSelectedActor = mBox;
// Create emitter
NxFluidEmitterDesc emitterDesc;
emitterDesc.setToDefault();
emitterDesc.frameShape = mBox->getShapes()[0];
emitterDesc.dimensionX = dimX;
emitterDesc.dimensionY = dimY;
emitterDesc.relPose = mat;
emitterDesc.rate = 300;
emitterDesc.randomAngle = 0.001f;
emitterDesc.randomPos = NxVec3(0.0f,0.0f,0.0f);
emitterDesc.fluidVelocityMagnitude = 10.0f;
emitterDesc.repulsionCoefficient = 0.02f;
emitterDesc.maxParticles = 0;
emitterDesc.particleLifetime = 5.0f;
emitterDesc.type = NX_FE_CONSTANT_FLOW_RATE;
emitterDesc.shape = NX_FE_ELLIPSE;
return fluid->createEmitter(emitterDesc);
}

First we make a call to CreateFluid() to initialize our fluid.

NxFluid* CreateFluid()
{
// Create fluid
NxFluidDesc fluidDesc;
fluidDesc.setToDefault();

fluidDesc.kernelRadiusMultiplier = 2.0f;
fluidDesc.restParticlesPerMeter = 7.0f;
fluidDesc.motionLimitMultiplier = 3.0f;
fluidDesc.packetSizeMultiplier = 8;
fluidDesc.collisionDistanceMultiplier = 0.1;
fluidDesc.stiffness = 50.0f;
fluidDesc.viscosity = 40.0f;
fluidDesc.restDensity = 1000.0f;
fluidDesc.damping = 0.0f;

gParticles = new NxVec3[fluidDesc.maxParticles];


fluidDesc.particlesWriteData.bufferPos = &gParticles[0].x;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
fluidDesc.particlesWriteData.bufferPosByteStride = sizeof(NxVec3);
fluidDesc.particlesWriteData.bufferVel = 0;
fluidDesc.particlesWriteData.bufferVelByteStride = 0;
fluidDesc.particlesWriteData.numParticlesPtr = &gNumParticles;


}

As before, our fluid gets set with a density, stiffness, viscosity, restitution against static
objects, and adhesion to static objects. We further set a maximum number of particles
the fluid may have. Once this limit is reached, the fluid's emitter will not be able to
generate any more particles. It will only start generating particles again when other
particles of the fluid start dying off, either because their lifetime is spent or they have
been killed off by a drain.

We allocate our particle buffer. The fluid emitter fills in the buffer with data as it
launches the particles. It manages the creation and deletion of the fluid's particles.

gParticles = new NxVec3[fluidDesc.maxParticles];


fluidDesc.particlesWriteData.bufferPos = &gParticles[0].x;
fluidDesc.particlesWriteData.bufferPosByteStride = sizeof(NxVec3);
fluidDesc.particlesWriteData.bufferVel = 0;
fluidDesc.particlesWriteData.bufferVelByteStride = 0;
fluidDesc.particlesWriteData.numParticlesPtr = &gNumParticles;

3 Emitter Cone
We have created our fluid. Now we will create an emitter to attach to the fluid to control
where and how it is released.

NxFluidEmitter* CreateFluidEmitter(const NxReal dimX, const NxReal dimY)


{
fluid = CreateFluid();
assert(fluid);

NxQuat q;
q.fromAngleAxis(-90,NxVec3(1,0,0));
NxMat34 mat;
mat.M.fromQuat(q);
mat.t = NxVec3(0,1.2,0);
// Create frame actor
NxActor* mBox = CreateBox(NxVec3(0,1,0),NxVec3(1.0,1.0,1.0),2);
gSelectedActor = mBox;
// Create emitter
NxFluidEmitterDesc emitterDesc;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
emitterDesc.setToDefault();
emitterDesc.frameShape = mBox->getShapes()[0];
emitterDesc.dimensionX = dimX;
emitterDesc.dimensionY = dimY;
emitterDesc.relPose = mat;
emitterDesc.rate = 300;
emitterDesc.randomAngle = 0.001f;
emitterDesc.randomPos = NxVec3(0.0f,0.0f,0.0f);
emitterDesc.fluidVelocityMagnitude = 10.0f;
emitterDesc.repulsionCoefficient = 0.02f;
emitterDesc.maxParticles = 0;
emitterDesc.particleLifetime = 5.0f;
emitterDesc.type = NX_FE_CONSTANT_FLOW_RATE;
emitterDesc.shape = NX_FE_ELLIPSE;
return fluid->createEmitter(emitterDesc);
}

Our emitter is going to be a fountain, an ellipse facing upward which spouts particles of
the fluid it is attached to. Fluid jets out of the emitter along its z-axis. With an identity
matrix, this will be the global z-axis, hence. When we call CreateFluidEmitter(), we pass
in this matrix as well as the dimensions of the ellipse, dimX and dimY, which specify the
size of the emitter's ellipse along its x and y axes.

NxFluidEmitter* CreateFluidEmitter(const NxReal dimX, const NxReal dimY)


{
...
NxQuat q;
q.fromAngleAxis(-90,NxVec3(1,0,0));
NxMat34 mat;
mat.M.fromQuat(q);
mat.t = NxVec3(0,1.2,0);
...
emitterDesc.dimensionX = dimX;
emitterDesc.dimensionY = dimY;
emitterDesc.relPose = mat;
...
}

Hit “v” to display the emitter cone. The blue arrow shows the emitter direction while the
emitter cone is shown in purple.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
4 Attaching the Emitter to an Actor
Note that the emitter matrix is called the “relPose”. This is because we can attach it to
an actor. The default “frameShape” field is null, therefore our emitter's matrix specifies
global position and orientation.

emitterDesc.frameShape = NULL;

Try to point the “frameShape” to the box’s shape in our scene.

emitterDesc.frameShape = mBox->getShapes()[0];

The fountain is now attached to the box, you can move the actor around and the fluid will
continue to spout from above the box. When you change the emitter's matrix, it will
position and orient it in the box’s coordinate space.

5 Emitter Parameters
We specify the rate of our emitter, the number of fluid particles it ejects per second.

emitterDesc.rate = 300;

We also specify a random angle. This is the angle by which a particle may deviate from
the emitter's direction when spawned. The number is the cosine of the angle.

emitterDesc.randomAngle = 0.001;

Next is the magnitude of the fluid particles' initial velocity, in meters per second.

emitterDesc.fluidVelocityMagnitude = 10.0f;

We set the maximum number of particles the emitter can emit. The emitter will stop
creating particles altogether when this limit is reached and start again once the number of
fluid particles falls below the limit.

emitterDesc.maxParticles = MAX_PARTICLES;

We set a lifetime for the particles, in seconds. The particles will self-destruct in this
time after they are launched. The other way particles get destroyed is if they come into
contact with a drain.

emitterDesc.particleLifetime = 5.0f;

We give the emitter a constant flow rate. Particles will be expelled from the emitter at a
constant rate per unit time. We can also set this to NX_FE_CONSTANT_PRESSURE,
so the emitter exerts a constant force.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
emitterDesc.type = NX_FE_CONSTANT_FLOW_RATE;

Finally we specify the emitter's shape, an ellipse. The other shape type is
NX_FE_RECTANGULAR, so we can make it a rectangle with dimX and dimY half-
dimensions as well.

emitterDesc.shape = NX_FE_ELLIPSE;

6 Conclusion
We can spawn fluids in two ways: all at once in a specified volume or, as we have seen
here, at a specified rate and velocity from an emitter.

7 Related Classes, Functions, and Parameters


NxFluidEmitterDesc
relPose
frameShape
type
maxParticles
shape
dimensionX
dimensionY
randomPos
randomAngle
fluidVelocityMagnitude
rate
particleLifetime
flags
userData
name

NxFluidEmitter
getFluid()
setGlobalPose()
setGlobalPosition()
setGlobalOrientation()
getGlobalPose()
getGlobalPosition()
getGlobalOrientation()
setLocalPose()
setLocalPosition()
setLocalOrientation()
getLocalPose()
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
7
getLocalPosition()
getLocalOrientation()
setFrameActor()
getFrameActor()
getDimensionX()
getDimensionY()
setRandomPos()
getRandomPos()
setRandomAngle()
getRandomAngle()
setFluidVelocityMagnitude()
getFluidVelocityMagnitude()
setRate()
getRate()
setParticleLifetime()
getParticleLifetime()
setFlag()
getFlag()
getShape()
getType()
setName()
getName()
userData

NxEmitterShape
NX_FE_RECTANGULAR
NX_FE_ELLIPSE

NxEmitterType
NX_FE_CONSTANT_PRESSURE
NX_FE_CONSTANT_FLOW_RATE

NxFluidEmitterFlag
NX_FEF_VISUALIZATION
NX_FEF_BROKEN_ACTOR_REF
NX_FEF_FORCE_ON_ACTOR
NX_FEF_ADD_ACTOR_VELOCITY
NX_FEF_ENABLED

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
Lesson 903 – Fluids and Rigid Bodies

1 Introduction
In this lesson, we will load a flume ramp object
(“..\TrainingPrograms\Programs\Data\ase\coolFlow.ase”), the fluid will fall onto the
ramp and flow down to a bowl object
(“..\TrainingPrograms\Programs\Data\ase\fluidSample.ase”).

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Fluids and Rigid Bodies
Most interaction between fluids and the world will involve fluids colliding with static
mesh geometry. A character blasted by a shotgun will spew blood onto the walls and
floor of a game level. A health fountain will bubble over and fill up a solid bowl placed
in the level. Gasoline will spill from barrels that have been shot or tipped over and flow
over floors and around level objects before being set ablaze. Molten steel will cascade
from cast-iron pots into pools of lava.

This geometry needs to be loaded and cooked to prepare it for collision with the fluid.
Take a look at InitNx().

void InitNx()
{

bowl = CookASE("fluidSample.ase", gScene, NxVec3(1,0,0));
flume = CookASE("coolFlow.ase", gScene, NxVec3(1,-4,-0), NxVec3(1,0.2,1));

box = CreateBox(NxVec3(2,5-0.75,7), NxVec3(0.75,0.75,0.75), 1);


sphere = CreateSphere(NxVec3(0,4-0.4,1), 0.4, 1);
capsule = CreateCapsule(NxVec3(3,0-(1+0.5),8), 1, 1, 1);

CreateBox(NxVec3(0,-10-1,0), NxVec3(40,1,40), 0);

fluid = CreateFluid(NxVec3(0,1.5,0), 19, 0.1, gScene);


}

We call CookASE() on the two files, “fluidSample.ase” and “coolFlow.ase”.

bowl = CookASE("fluidSample.ase", gScene, NxVec3(1,0,0));


flume = CookASE("coolFlow.ase", gScene, NxVec3(1,-4,-0), NxVec3(1,0.2,1));

CookASE() is defined in CookASE.cpp.

Then we call CreateFluid() before kicking off the simulation.

void InitNx()
{
...
fluid = CreateFluid(NxVec3(0,2,0), 15, 0.1, gScene);


}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
CreateFluid() is much as it was in Lesson901.cpp. It attaches a particle buffer to the
fluid descriptor. The fluid will write out the positions of its particles to this buffer each
frame of the simulation. CreateFluid() then builds a ball of fluid with
NxScene::createFluid().

NxFluid* CreateFluid(const NxVec3& pos, NxU32 sideNum, NxReal distance,


NxScene* scene)
{
// Create a set of particles
gParticleBufferNum = 0;
float rad = sideNum*distance*0.5;
for (unsigned i=0; i<sideNum; i++)
{
for (unsigned j=0; j<sideNum; j++)
{
for (unsigned k=0; k<sideNum; k++)
{
NxVec3 p = NxVec3(i*distance,j*distance,k*distance);
if (p.distance(NxVec3(rad,rad,rad)) < rad)
{
p += pos;
gParticleBuffer[gParticleBufferNum++] = p;
}
}
}
}

// Set structure to pass particles, and receive them after every simulation step
NxParticleData particles;
particles.numParticlesPtr = &gParticleBufferNum;
particles.bufferPos = &gParticleBuffer[0].x;
particles.bufferPosByteStride = sizeof(NxVec3);

// Create a fluid descriptor


NxFluidDesc fluidDesc;
fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;
fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;
fluidDesc.motionLimitMultiplier = MOTION_LIMIT_MULTIPLIER;
fluidDesc.packetSizeMultiplier = PACKET_SIZE_MULTIPLIER;
fluidDesc.stiffness = 40; // 50
fluidDesc.viscosity = 22;
fluidDesc.restDensity = 1000;
fluidDesc.damping = 0;
fluidDesc.simulationMethod = NX_F_SPH;
//NX_F_NO_PARTICLE_INTERACTION;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
fluidDesc.initialParticleData = particles;
fluidDesc.particlesWriteData = particles;

if(!bHardwareFluid)
fluidDesc.flags &= ~NX_FF_HARDWARE;

fluid = gScene->createFluid(fluidDesc);
if(!fluid)
{
fluidDesc.flags &= ~NX_FF_HARDWARE;
bHardwareFluid = false;
}
fluid = gScene->createFluid(fluidDesc);
assert(fluid != NULL);
return fluid;
}

The mesh cooking is done for the user by the SDK and is completely opaque to the user.
Recognize, however, that while all this is done for you “under the hood”, you still need to
be careful adding geometry in the proximity of your fluid. The more you added, the more
that needs to get cooked and will be tracked by the hardware. If too much is added, this
can slow down your simulation considerable.

Our fluid is now ready to be simulated and rendered, and fluid will collide with triangle
mesh actors, you can also create some dynamic actors by pressing “b”.

3 Conclusion
We have loaded two static mesh assets saved as ASE files, placed them in the scene, and
cooked them so our fluid spills over and onto them.

4 Related Classes, Functions, and Parameters


NxCookTriangleMesh(…)

NxFluidDesc
staticCollisionRestitution
or
restitutionForStaticShapes
staticCollisionAdhesion
or
dynamicFrictionForStaticShapes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 904 – Fluid and Drain

1 Introduction
Drains are an important method in keeping the particle count and spread under control.
Place drains around the area in which a fluid is used to stop the fluid particles from
spreading too far and negatively impacting performance.
In this lesson, we will learn how to create drains.

2 Fluid and Drain


It is easy to create drains for fluids, just need to add a flag to the actor’s shape.
// Create the drains in the scene
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
box = CreateBox(NxVec3(5,0,0), NxVec3(1.5,1.5,1.5), 1);
box->getShapes()[0]->setFlag(NX_SF_FLUID_DRAIN,true);
sphere = CreateSphere(NxVec3(0,0.0,0), 1.5, 1);
sphere->getShapes()[0]->setFlag(NX_SF_FLUID_DRAIN,true);
capsule = CreateCapsule(NxVec3(-5,0,0), 1, 1, 1);
capsule->getShapes()[0]->setFlag(NX_SF_FLUID_DRAIN,true);

Below is a list of currently supported static and dynamic drain shapes:


Box - NxBoxShape
Sphere - NxSphereShape
Capsule - NxCapsuleShape
Convex - NxConvexShape (Only dynamic shapes)
Plane - NxPlaneShape

3 Conclusion
Now you have created some drains successfully, the particles will be “killed” if the fluid
touches the drain.

4 Related Classes, Functions, and Parameters


enum NxShapeFlag
NX_SF_FLUID_DRAIN

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
Lesson 905 – Fluid Events

1 Introduction
We add two events for the user for making it easier to use the Fluid functionality. The
events are raised when an emitter is empty and when a fluid is empty. The event callback
class is called NxFluidUserNotify and controlled through the following interface:
void NxScene::setFluidUserNotify(NxFluidUserNotify* callback)
NxFluidUserNotify* NxScene::getFluidUserNotify() const
NxFluidUserNotify* NxSceneDesc::fluidUserNotify
The callback interface is described in the API documentation, but currently consists of
the following two functions:
//The user needs to return whether he wants to delete the emitter, in the callback.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
bool NxFluidUserNotify::onEmitterEvent(NxFluidEmitter& emitter,
NxFluidEmitterEventType eventType)

//The user needs to return whether he wants to delete the fluid, in the callback.
bool NxFluidUserNotify::onEvent(NxFluid& fluid, NxFluidEventType eventType)

2 Fluid Events
At first, we define a class MyFluidUserNotify inherited from NxFluidUserNotify:

class MyFluidUserNotify : public NxFluidUserNotify


{
public:
virtual bool onEmitterEvent(NxFluidEmitter& emitter, NxFluidEmitterEventType
eventType);
virtual bool onEvent(NxFluid& fluid, NxFluidEventType eventType);

virtual ~MyFluidUserNotify() {}
};

Then we implement two functions onEvent(…) and onEmitterEvent(…), in this lesson,


we just print some information when the events happens.

bool MyFluidUserNotify::onEvent(NxFluid& fluid, NxFluidEventType eventType)


{
int a = (int)&fluid;
if(eventType ==NX_FET_NO_PARTICLES_LEFT)
{
for(int i =0;i< FLUID_NUMBER;i++)
{
if(a == gFluidArray[i])
printf("Fluid %d is empty now\n",i);
}
}
return false;
}

bool MyFluidUserNotify::onEmitterEvent(NxFluidEmitter& emitter,


NxFluidEmitterEventType eventType)
{
int a = (int)&emitter;
if(eventType ==NX_FEET_EMITTER_EMPTY)
{
for(int i =0;i< FLUID_NUMBER;i++)
{
if(a == gEmitterArray[i])

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
printf("Emitter %d is empty now\n",i);
}
}
return false;
}

Finally, to receive the events, we just need to do the following things after the scene is
created.

gUserNotify = new MyFluidUserNotify();


gScene->setFluidUserNotify(gUserNotify);

3 Conclusion
Now you have learned how to receive fluid events and fluid emitter events with the class
of NxFluidUserNotify.

4 Related Classes, Functions, and Parameters


NxFluidUserNotify
NxScene::setFluidUserNotify
NxScene::getFluidUserNotify()
NxSceneDesc::fluidUserNotify

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
Lesson 906 – Fluid UserData

1 Introduction
Sometimes we need to readback the fluid particles’ information for rendering or other
using, it is necessary to specify a buffer to write the appropriate particle information into
when creating the fluid. This is done using the NxFluidDesc::particlesWriteData. After
each simulation step, the SDK writes updated positions, etc., to these buffers.

When creating a fluid, you can specify the following buffers in particlesWriteData:
bufferPos - position of each individual particle.
bufferVel - velocity of each particle.
bufferLife - lifetime of each particle.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
bufferDensity - density for each particle.
bufferId - SDK-generated unique particle ID.
bufferFlag - NxParticleFlag values for each particle.

For example, the lifetime is useful for fading out particles as they reach the end of their
life. The velocity is useful for motion blur/elongated particles (e.g., when rendering
sparks, etc.).

Besides the buffers in particlesWriteData, the members particleDeletionIdWriteData and


particleCreationIdWriteData can be used to respond to the deletion or creation of
particles by the SDK, graphically or gameplay-wise. Together with IDs, these provide the
means to easily tracking individual particles over their lifetime.

In this lesson, we create three piles of fluids, and then we render them with three different
colors by using fluid UserData.

2 Fluid UserData
At first we create the first part of fluids:

// initialize the particles' data buffer


CreateParticleSphere(gNewParticleData, gNewParticlesMax, false, fluidPos,
fluidVel, gLifetime, gDistance, gSideNum);

NxFluid* fluid = gScene->createFluid(fluidDesc);

After fluid creation, we set color for this part of fluid

//Set color for first scoop --> red


//The NxScene::createFluid() function adds the NxFluidDesc::initialParticleData to
the SDK.
//It also writes to the MyFluid::mCreatedParticleIds buffer, which was passed to the
SDK
//(NxFluidDesc::particleCreationIdWriteData).
for (unsigned i=0; i< gCreatedIdsBufferNum; i++)
{
unsigned id = gCreatedIdsBuffer[i];
gParticlesColorBuffer[id].color = NxVec3(0.8f, 0.1f,0.1f);
}

Then we create the other two parts of fluids, this is done by using the function of
NxFluid::addParticles(…)

//Create another two scoops. This time with NxFluid::addParticles().


//The NxFluid::addParticles() function adds new particles to the SDK.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
//It also writes to the MyFluid::mCreatedParticleIds buffer, which was passed to the
SDK
//(NxFluidDesc::particleCreationIdWriteData).
fluidPos.set(-2,2,0);
fluidVel.set(5,0,0);
CreateParticleSphere(gNewParticleData, gNewParticlesMax, false, fluidPos,
fluidVel, gLifetime, gDistance, gSideNum);
fluid->addParticles(gNewParticleData);

//Set scoop color --> blue.


for (unsigned i=0; i<gCreatedIdsBufferNum; i++)
{
unsigned id = gCreatedIdsBuffer[i];
gParticlesColorBuffer[id].color = NxVec3(0.3f, 0.4f,1.0f);
}

fluidPos.set(2,2,0);
fluidVel.set(-5,0,0);
CreateParticleSphere(gNewParticleData, gNewParticlesMax, false, fluidPos,
fluidVel, gLifetime, gDistance, gSideNum);
fluid->addParticles(gNewParticleData);

//Set scoop color --> green.


for (unsigned i=0; i< gCreatedIdsBufferNum; i++)
{
unsigned id = gCreatedIdsBuffer[i];
gParticlesColorBuffer[id].color = NxVec3(0.3f, 1.0f,0.3f);
}

Finally, we render these three piles of fluids with different colors:

void RenderFluid()
{
// Render fluid
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
for (NxU32 p=0; p<gNewParticlesNum; p++)
{
const ParticleSDK& particle = gNewParticles[p];
const MyParticle& particlesColorBuffer = gParticlesColorBuffer[particle.id];
glColor4f(particlesColorBuffer.color.x, particlesColorBuffer.color.y,
particlesColorBuffer.color.z, 1.0f);
glPushMatrix();
glTranslatef(particle.position.x,particle.position.y,particle.position.z);
glutSolidCube(0.1); // glutSolidSphere(0.1,8,5);
glPopMatrix();
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

3 Conclusion
Using fluid UserData, fluids are rendered with different colors.

4 Related Classes, Functions, and Parameters


NxParticleData
numParticlesPtr
bufferPos
bufferPosByteStride
bufferLife
bufferLifeByteStride
bufferId
bufferIdByteStride

NxParticleIdData
numIdsPtr
bufferId
bufferIdByteStride

NxFluidDesc
initialParticleData
particlesWriteData
particleCreationIdWriteData
particleDeletionIdWriteData

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 1001 – Cloth

1 Introduction
Cloth simulation has been done to a certain degree in games, usually confined to low
density meshes representing flags, banners hanging from walls, etc. With hardware
accelerated cloth, you can add many more cloth objects and effects to your levels or even
dress your characters in robes, cloaks, and capes.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Cloth
In this lesson we build a scene containing a curtain made out of cloth. Take a look at
InitNx():
// Physics SDK globals
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxVec3 gDefaultGravity(0,-9.8,0);

// User report globals


DebugRenderer gDebugRenderer;
UserAllocator* gAllocator;


// Simulation globals
NxReal gDeltaTime = 1.0/60.0;
bool bHardwareScene = false;
bool gHardwareCloth = true;
bool bPause = false;

// Actor globals
NxActor* groundPlane = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;

// Array of cloth objects


NxArray<MyCloth*> gCloths;


void InitNx()
{
// Create a memory allocator
gAllocator = new UserAllocator;

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
gAllocator);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
2
//gPhysicsSDK->setParameter(NX_VISUALIZE_CLOTH_COLLISIONS, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_CLOTH_SLEEP, 1);

// Check available hardware. Create cloth in HW if any HW is available


NxHWVersion hwCheck = gPhysicsSDK->getHWVersion();
if (hwCheck == NX_HW_VERSION_NONE)
{
gHardwareCloth = false;
}
else
gHardwareCloth = true;

// Create the scenes


NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene){
sceneDesc.simType = NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene) return;
}

// Create the default material


NxMaterialDesc m;
m.restitution = 0.5;
m.staticFriction = 0.2;
m.dynamicFriction = 0.2;
NxMaterial* mat = gScene->getMaterialFromIndex(0);
mat->loadFromDesc(m);

SetupCurtainScene();

if (gScene->getNbActors() > 0)
gSelectedActor = *gScene->getActors();
else
gSelectedActor = NULL;

// Initialize HUD
InitializeHUD();
InitializeSpecialHUD();
}

We create a hardware rigid body scene.

// Create the scene

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.simType = NX_SIMULATION_HW;
gScene = gPhysicsSDK->createScene(sceneDesc);

The static and dynamic rigid body actors of the hardware rigid body scene can interact
with the cloth. The cloth can be attached to actors in the rigid body scene. The rigid
bodies can pull and push the cloth and the cloth can push and pull the rigid bodies if set
up to do so.

3 Curtain
The scene gets constructed in SetupCurtainScene().

void InitNx()
{

SetupCurtainScene();

}

void SetupCurtainScene()
{
sprintf(gTitleString, "Cloth and Shape Interaction Sample");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
NxActor* sphere1 = CreateSphere(NxVec3(-1,0,-0.5), 1, 10);
NxActor* box1 = CreateBox(NxVec3(1,0,-1), NxVec3(1,1,1), 10);

// Create the box that the cloth will attach to


NxActor* box2 = CreateBox(NxVec3(0,6.5,0), NxVec3(5,0.5,0.5), 10);
box2->setLinearDamping(5);

// Limit the movement of the box by a joint


NxD6JointDesc d6Desc;
d6Desc.actor[0] = NULL;
d6Desc.actor[1] = box2;
NxVec3 globalAnchor(0,7,0);
d6Desc.localAnchor[0] = globalAnchor;
box2->getGlobalPose().multiplyByInverseRT(globalAnchor,
d6Desc.localAnchor[1]);

d6Desc.localAxis[0] = NxVec3(1,0,0);
d6Desc.localNormal[0] = NxVec3(0,1,0);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
d6Desc.localAxis[1] = NxVec3(1,0,0);
d6Desc.localNormal[1] = NxVec3(0,1,0);
d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.xMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

NxJoint* d6Joint = gScene->createJoint(d6Desc);

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(4,7,0);
clothDesc.thickness = 0.2;
clothDesc.bendingStiffness = 0.5;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth *regularCloth = new MyCloth(gScene, clothDesc, 8, 7, 0.15,


"nvidia.bmp");

if (!regularCloth->getNxCloth())
{
printf("Error: Cloth creation failed.\n");
delete regularCloth;
}
else
{
gCloths.push_back(regularCloth);
// Hang up the cloth
regularCloth->getNxCloth()->attachToShape(*box2->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}
}

We build a bar, called box2 that the cloth will hang from. The bar gets jointed to the
scene so it can slide left and right, moving the cloth like a theater curtain.

void SetupCurtainScene()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
// Create the box that the cloth will attach to
NxActor* box2 = CreateBox(NxVec3(0,6.5,0), NxVec3(5,0.5,0.5), 10);
box2->setLinearDamping(5);

// Limit the movement of the box by a joint


NxD6JointDesc d6Desc;
d6Desc.actor[0] = NULL;
d6Desc.actor[1] = box2;
NxVec3 globalAnchor(0,7,0);
d6Desc.localAnchor[0] = globalAnchor;
box2->getGlobalPose().multiplyByInverseRT(globalAnchor,
d6Desc.localAnchor[1]);

d6Desc.localAxis[0] = NxVec3(1,0,0);
d6Desc.localNormal[0] = NxVec3(0,1,0);
d6Desc.localAxis[1] = NxVec3(1,0,0);
d6Desc.localNormal[1] = NxVec3(0,1,0);
d6Desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing1Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.swing2Motion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.xMotion = NX_D6JOINT_MOTION_FREE;
d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

NxJoint* d6Joint = gScene->createJoint(d6Desc);



}

The cloth is created at the bottom of the function.

void SetupCurtainScene()
{

// Create the box that the cloth will attach to
NxActor* box2 = CreateBox(NxVec3(0,6.5,0), NxVec3(5,0.5,0.5), 10);
box2->setLinearDamping(5);

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(4,7,0);
clothDesc.thickness = 0.2;
clothDesc.bendingStiffness = 0.5;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth *regularCloth = new MyCloth(gScene, clothDesc, 8, 7, 0.15,


"nvidia.bmp");

}

We fill out our cloth descriptor with parameters for the cloth.

First we set the global position of the cloth.

clothDesc.globalPose.t = NxVec3(4,7,0);

We fix the cloth 4 units to the left and 7 units up so that the top of the cloth spawns inside
the curtain bar.

Next, we set the thickness of the cloth.

clothDesc.thickness = 0.2;

We set the bending stiffness of the cloth.

clothDesc.bendingStiffness = 0.5;

The range for this is 0 to 1 and only has an effect if NX_CLF_BENDING is set. This
parameter defines how difficult it is to bend the cloth.

We set the bending flag so cloth bending is allowed.

clothDesc.flags |= NX_CLF_BENDING;

We set two-way collision so rigid body objects will push the cloth and the cloth will push
back on them as well.

clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;

We want to run the cloth in hardware if there’s hardware available, so we set the
hardware flag on the cloth.

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

Finally, we pass the cloth descriptor into our MyCloth object constructor to build the
cloth.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
MyCloth *regularCloth = new MyCloth(gScene, clothDesc, 8, 7, 0.15,
"nvidia.bmp");

The MyCloth object is in MyCloth.h and the MyCloth constructor is in MyCloth.cpp.

MyCloth.h

class MyCloth {
public:

bool mInitDone;

NxMeshData mReceiveBuffers;
NxScene *mScene;
NxCloth *mCloth;
NxClothMesh *mClothMesh;

GLfloat *mTexCoords;
NxU32 mNumTexCoords;
GLuint mTexId;
};

MyCloth.cpp

MyCloth::MyCloth(NxScene *scene, NxClothDesc &desc, NxReal w, NxReal h, NxReal


d, char *texFilename, bool tearLines)
{
mInitDone = false;
mTexId = 0;
mTexCoords = NULL;

NxClothMeshDesc meshDesc;
generateRegularMeshDesc(meshDesc, w, h, d, texFilename != NULL, tearLines);
init(scene, desc, meshDesc);
if (texFilename)
createTexture(texFilename);
}

MyCloth:: generateRegularMeshDesc() builds the visible mesh for the cloth.

void MyCloth::generateRegularMeshDesc(NxClothMeshDesc &desc, NxReal w, NxReal


h, NxReal d, bool texCoords, bool tearLines)
{
int numX = (int)(w / d) + 1;
int numY = (int)(h / d) + 1;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
desc.numVertices = (numX+1) * (numY+1);
desc.numTriangles = numX*numY*2;
desc.pointStrideBytes = sizeof(NxVec3);
desc.triangleStrideBytes = 3*sizeof(NxU32);
desc.vertexMassStrideBytes = sizeof(NxReal);
desc.vertexFlagStrideBytes = sizeof(NxU32);
desc.points = (NxVec3*)malloc(sizeof(NxVec3)*desc.numVertices);
desc.triangles = (NxU32*)malloc(sizeof(NxU32)*desc.numTriangles*3);
desc.vertexMasses = 0;
desc.vertexFlags = 0;
desc.flags = 0;

int i,j;
NxVec3 *p = (NxVec3*)desc.points;
for (i = 0; i <= numY; i++) {
for (j = 0; j <= numX; j++) {
p->set(-d*j, 0.0f, -d*i);
p++;
}
}

if (texCoords) {
mTexCoords = (GLfloat *)malloc(sizeof(GLfloat) * 2 *
TEAR_MEMORY_FACTOR * desc.numVertices);
GLfloat *f = mTexCoords;
GLfloat dx = 1.0f; if (numX > 0) dx /= numX;
GLfloat dy = 1.0f; if (numY > 0) dy /= numY;
for (i = 0; i <= numY; i++) {
for (j = 0; j <= numX; j++) {
*f++ = j*dx;
*f++ = i*dy;
}
}
mNumTexCoords = desc.numVertices;
}
else mNumTexCoords = 0;

NxU32 *id = (NxU32*)desc.triangles;


for (i = 0; i < numY; i++) {
for (j = 0; j < numX; j++) {
NxU32 i0 = i * (numX+1) + j;
NxU32 i1 = i0 + 1;
NxU32 i2 = i0 + (numX+1);
NxU32 i3 = i2 + 1;
if ((j+i)%2) {
*id++ = i0; *id++ = i2; *id++ = i1;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
*id++ = i1; *id++ = i2; *id++ = i3;
}
else {
*id++ = i0; *id++ = i2; *id++ = i3;
*id++ = i0; *id++ = i3; *id++ = i1;
}
}
}

// generate tear lines if necessary


if(tearLines)
generateTearLines(desc, numX + 1, numY + 1);
}

The visible mesh gets stored in the cloth mesh descriptor, “meshDesc”. This gets passed
into MyCloth::init() along with the cloth descriptor, “desc”.

MyCloth:: MyCloth (NxScene *scene, NxClothDesc &desc, char *objFileName, NxReal


scale)
{

init(scene, desc, meshDesc);

}

void MyCloth::init(NxScene *scene, NxClothDesc &desc, NxClothMeshDesc


&meshDesc)
{
mScene = scene;

// if we want tearing we must tell the cooker


// this way it will generate some space for particles that will be generated
//during tearing
if (desc.flags & NX_CLF_TEARABLE)
meshDesc.flags |= NX_CLOTH_MESH_TEARABLE;

NxInitCooking();
cookMesh(meshDesc);
NxCloseCooking();

releaseMeshDescBuffers(meshDesc);

allocateReceiveBuffers(meshDesc.numVertices, meshDesc.numTriangles);

desc.clothMesh = mClothMesh;
desc.meshData = mReceiveBuffers;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
10
mCloth = scene->createCloth(desc);
mInitDone = true;
}

We set some flags on the mesh descriptor. We store the mesh in mClothMesh. We finally
call NxScene::createCloth() to build our PhysX cloth object, mCloth, using the cloth
descriptor.

mCloth = scene->createCloth(desc);

The “scene” we create the cloth with is gScene from SetupCurtainScene() in


Lesson1001.cpp.

void SetupCurtainScene()
{

MyCloth *regularCloth = new MyCloth(gScene, clothDesc, 8, 7, 0.15,
"nvidia.bmp");

}

Once again, objects in the hardware scene that is used to create hardware cloth with
NxScene::createCloth() will interact with the hardware scene cloth.

4 Conclusion and Playing Around


You have created your first cloth. Congratulations! Try out playing with the cloth
descriptor parameters to see what different sorts of cloth objects you can create. Cloth
can be used for much more than clothing, flags, and banners. With cloth you can create
many different types of deformable, bendable, and breakable objects: paper, vegetation,
rope, etc.

5 Related Classes, Functions, and Parameters


NxMeshData

NxClothMesh

NxClothDesc
clothMesh
globalPose
thickness
bendingStiffness
meshData
flags

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
11
NxClothFlag
NX_CLF_VISUALIZATION
NX_CLF_GRAVITY
NX_CLF_BENDING
NX_CLF_COLLISION_TWOWAY
NX_CLF_TEARABLE
NX_CLF_HARDWARE

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
12
Lesson 1002 – External Forces

1 Introduction
In this lesson, we build a cloth flag. We will provide wind acceleration to the flag to
simulate it blowing in the wind.

2 Flag
The scene gets constructed in SetupFlagScene().

void InitNx()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
{

SetupFlagScene();

}

void SetupFlagScene()
{
sprintf(gTitleString, "External Forces Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
// Create a pole
NxActor* capsule = CreateCapsule(NxVec3(0.0,-0.2,0.0), 10, 0.2, 0);

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0,10,0);
clothDesc.globalPose.M.rotX(-NxHalfPiF32);
clothDesc.thickness = 0.2;
clothDesc.bendingStiffness = 0.5;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;
clothDesc.windAcceleration = NxVec3(-20, 12, -1); // Set wind

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 5, 0.15,


"nvidia.bmp");

if (!regularCloth->getNxCloth())
{
printf("Error: Cloth creation failed.\n");
delete regularCloth;
}
else
{
gCloths.push_back(regularCloth);
// Attach the flag to the pole
regularCloth->getNxCloth()->attachToShape(*capsule->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
We build the ground and a pole, called capsule that the flag will fly from. The cloth for
the flag has the NX_CLOTH_ATTACHMENT_TWOWAY bit set so that the flag will be
attached to any scene objects it intersects at startup.

void SetupFlagScene()
{
sprintf(gTitleString, "External Forces Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
// Create a pole
NxActor* capsule = CreateCapsule(NxVec3(0.0,-0.2,0.0), 10, 0.2, 0);

}

The cloth is created with the following codes.

void SetupFlagScene()
{

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0,10,0);
clothDesc.globalPose.M.rotX(-NxHalfPiF32);
clothDesc.thickness = 0.2;
clothDesc.bendingStiffness = 0.5;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;
clothDesc.windAcceleration = NxVec3(-20, 12, -1); // Set wind

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 5, 0.15,


"nvidia.bmp");

}

We fill out our cloth descriptor with parameters for the cloth.

First we set the global position of the cloth.

clothDesc.globalPose.t = NxVec3(0,10,0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
Next, we set the thickness of the cloth.

clothDesc.thickness = 0.2;

We set the bending stiffness of the cloth.

clothDesc.bendingStiffness = 0.5;

The range for this is 0 to 1 and only has an effect if NX_CLF_BENDING is set. This
parameter defines how difficult it is to bend the cloth.

We set the bending flag so cloth bending is allowed.

clothDesc.flags |= NX_CLF_BENDING;

We set two-way collision so rigid body objects will push the cloth and the cloth will push
back on them as well.

clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;

We want to run the cloth in hardware if there’s hardware available, so we set the
hardware flag on the cloth.

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

Finally, we pass the cloth descriptor into our MyCloth object constructor to build the
cloth.

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 5, 0.15,


"nvidia.bmp");

3 Applying Forces to the Cloth


We apply wind forces to the cloth.

void SetupFlagScene()
{

clothDesc.windAcceleration = NxVec3(-20, 12, -1); // Set wind

}
The property we use is NxClothDesc::windAcceleration. Basically, the wind acceleration
is only applied in the normal direction of each vertex. This makes it easy to simulate a
wind effect.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
4 Conclusion
You have created a cloth flag and have applied a wind force to it to make it appear to fly
in the wind.

5 Related Classes, Functions, and Parameters


NxClothDesc
windAcceleration

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson 1003 – Damping

1 Introduction
Friction and damping are resistance to velocity. Cloth damping is the internal friction
associated with the cloth. It is friction on the springs that hold the cloth mass grid
together. In this lesson, we build three pieces of cloth, each with different associated
damping properties. One cloth is undamped, one is normally damped, and one is COM
damped.

An undamped cloth moves freely with very little internal friction. The only internal
friction on the cloth is due to small errors in the solver, so the cloth will eventually come
to rest rather than perpetually waving back and forth. When the cloth is damped, you
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
apply a damping value to it that specifies a resistance to movement in the cloth fibers.
The cloth will sway slightly back and forth, but come to rest fairly quickly. When the
cloth is critically damped (COM damped), it means the cloth will not sway back and
forth. The cloth will bend when a force is applied to it, but the cloth spring strength will
not be enough to start bending it back to its original position.

2 Creating the Damped Cloth


The scene gets constructed in SetupDampingScene().

void InitNx()
{

SetupDampingScene();

}

void SetupDampingScene()
{
sprintf(gTitleString, "Damping Demo: Undamped, normally damped, COM
damped");

// Create objects in the scene


groundPlane = CreateGroundPlane();

NxActor* bar = CreateBox(NxVec3(0,6.5,0), NxVec3(12,0.5,0.5), 0);


NxActor* box1 = CreateBox(NxVec3(-8,0,-1), NxVec3(1,1,1), 10);
NxActor* box2 = CreateBox(NxVec3(0,0,-1), NxVec3(1,1,1), 10);
NxActor* box3 = CreateBox(NxVec3(8,0,-1), NxVec3(1,1,1), 10);

// First Cloth: undamped


NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(12,7,0);
clothDesc.thickness = 0.2;
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth1 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");
gCloths.push_back(regularCloth1);
regularCloth1->getNxCloth()->attachToShape(*bar->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
// Second cloth: normally damped
clothDesc.globalPose.t = NxVec3(4,7,0);
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.dampingCoefficient = 1;

MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");
gCloths.push_back(regularCloth2);
regularCloth2->getNxCloth()->attachToShape(*bar->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);

// Third cloth: COM damped


clothDesc.globalPose.t = NxVec3(-4,7,0);
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.flags |= NX_CLF_COMDAMPING;
clothDesc.dampingCoefficient = 1;

MyCloth* regularCloth3 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");
gCloths.push_back(regularCloth3);
regularCloth3->getNxCloth()->attachToShape(*bar->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}

We build a bar from which to hang our cloth objects. We build three dynamic boxes, one
under each piece of cloth to move around and interact with the cloth.

void SetupDampingScene()
{

NxActor* bar = CreateBox(NxVec3(0,6.5,0), NxVec3(12,0.5,0.5), 0);
NxActor* box1 = CreateBox(NxVec3(-8,0,-1), NxVec3(1,1,1), 10);
NxActor* box2 = CreateBox(NxVec3(0,0,-1), NxVec3(1,1,1), 10);
NxActor* box3 = CreateBox(NxVec3(8,0,-1), NxVec3(1,1,1), 10);

}

Next we create the cloth pieces. The first cloth we create normally with no damping.

void SetupDampingScene()
{

// First Cloth: undamped
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(12,7,0);
clothDesc.thickness = 0.2;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth1 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");

}

After we create the first cloth, we attach it to the bar object with
NxCloth::attacheToShape().

regularCloth1->getNxCloth()->attachToShape(*bar->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);

The second cloth we create with the NX_CLF_DAMPING flag set and damping
coefficient set to 1. Then we attach it to the bar as well.

// Second cloth: normally damped


clothDesc.globalPose.t = NxVec3(4,7,0);
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.dampingCoefficient = 1;

MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");
gCloths.push_back(regularCloth2);
regularCloth2->getNxCloth()->attachToShape(*bar->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);

The third cloth we create with the NX_CLF_DAMPING and NX_CLF_COMDAMPING


flags set and damping coefficient set to 1.

// Third cloth: COM damped


clothDesc.globalPose.t = NxVec3(-4,7,0);
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.flags |= NX_CLF_COMDAMPING;
clothDesc.dampingCoefficient = 1;

MyCloth* regularCloth3 = new MyCloth(gScene, clothDesc, 8, 7, 0.2,


"nvidia.bmp");

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
3 Interacting with the Damped Cloth
Run the demo. The undamped cloth on the left falls onto its block and swings back and
forth freely around it. The damped cloth in the middle looks slightly starched. It falls onto
its block and swings back and forth a couple of times, but quickly comes to a rest. The
third cloth on the right looks like it’s extremely heavily starched. It falls onto its block,
gets dented by the block and doesn’t swing back at all.

Select some of the blocks and use “umijkl” keys to move them into their respective
cloths. You’ll notice the cloth on the left swings in a very free, almost fluid motion, as if
it was made of silk. The middle cloth swings but is made out of a stiffer material, more
like a sheet of cotton. The cloth on the right conforms to the surface of the block and
retains its shape when the block is removed, returning to a flat surface under gravity very
slowly.

4 Conclusion
You have learned how to set damping flags on cloth objects, how to set the damping
coefficient for damped cloth, and what the different types of damping behavior mean.

5 Related Classes, Functions, and Parameters


NxClothDesc
dampingCoefficient

NxClothFlag
NX_CLF_DAMPING
NX_CLF_COMDAMPING

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson 1004 – Pipe

1 Introduction
So far, we have built rectangular pieces of cloth to build banners and flags. In this lesson,
we will build a cloth object from geometry defined in a Wavefront Object file. This
shows the potential to build any triangular mesh object of any shape into a cloth object.

PhysX cloth objects are the first hardware accelerated “deformable” objects to be seen in
games. Eventually, we hope to build all kinds of objects out of deformable material.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
2 Creating the Cloth Pipes
The scene gets constructed in SetupPipeScene().

void InitNx()
{

SetupPipeScene();

}

void SetupPipeScene()
{
sprintf(gTitleString, "Two Way Body Cloth Interaction Demo");

// Create objects in scene


groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(0,10.5,-1), NxVec3(1,1,0.1), 0);
NxActor* box2 = CreateBox(NxVec3(0,13,1.5), NxVec3(2,2,0.5), 0);
NxActor* box3 = CreateBox(NxVec3(0,15.4,-1), NxVec3(1,0.1,1), 0);
NxActor* box4 = CreateBox(NxVec3(0,19,-11), NxVec3(1,0.1,1), 0);
NxActor* box5 = CreateBox(NxVec3(0,17.5,-13), NxVec3(3,0.1,2), 0);

NxActor* box6 = CreateBox(NxVec3(0,20,-12), NxVec3(1,5,0.1), 0);


NxActor* box7 = CreateBox(NxVec3(0,18.5,-14), NxVec3(2,5,0.1), 0);
NxActor* box8 = CreateBox(NxVec3(0,10.5,1), NxVec3(1,1,0.1), 0);

for (NxU32 i = 0; i < 10; i++)


NxActor* sphere = CreateSphere(NxVec3(0,22+4*i,-13), 0.7, 1.0);

NxQuat q;
q.fromAngleAxis(20.0f,NxVec3(1,0,0));
box3->setGlobalOrientationQuat(q);

q.fromAngleAxis(20,NxVec3(1,0,0));
box4->setGlobalOrientationQuat(q);

q.fromAngleAxis(20,NxVec3(1,0,0));
box5->setGlobalOrientationQuat(q);

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0,18.2,-11);
clothDesc.globalPose.M.rotX(0.35);
clothDesc.thickness = 0.4;
clothDesc.density = 0.05;
clothDesc.dampingCoefficient = 0.1;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
2
clothDesc.friction = 0.3;
clothDesc.collisionResponseCoefficient = 0.4;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth1 = new MyCloth(gScene, clothDesc, "pipe.obj", 1);


objCloth1->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(objCloth1);

clothDesc.globalPose.t = NxVec3(0,12.2,0);
clothDesc.globalPose.M.rotX(NxHalfPiF32);

MyCloth* objCloth2 = new MyCloth(gScene, clothDesc, "pipe.obj", 1);


objCloth2->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(objCloth2);
}

We build an array of static box-shape that we arrange to create a chute through which a
column of dynamic sphere-shape objects will fall.

void SetupPipeScene()
{
// Create objects in scene
groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(0,10.5,-1), NxVec3(1,1,0.1), 0);
NxActor* box2 = CreateBox(NxVec3(0,13,1.5), NxVec3(2,2,0.5), 0);
NxActor* box3 = CreateBox(NxVec3(0,15.4,-1), NxVec3(1,0.1,1), 0);
NxActor* box4 = CreateBox(NxVec3(0,19,-11), NxVec3(1,0.1,1), 0);
NxActor* box5 = CreateBox(NxVec3(0,17.5,-13), NxVec3(3,0.1,2), 0);

NxActor* box6 = CreateBox(NxVec3(0,20,-12), NxVec3(1,5,0.1), 0);


NxActor* box7 = CreateBox(NxVec3(0,18.5,-14), NxVec3(2,5,0.1), 0);
NxActor* box8 = CreateBox(NxVec3(0,10.5,1), NxVec3(1,1,0.1), 0);

for (NxU32 i = 0; i < 10; i++)


NxActor* sphere = CreateSphere(NxVec3(0,22+4*i,-13), 0.7, 1.0);

NxQuat q;
q.fromAngleAxis(20.0f,NxVec3(1,0,0));
box3->setGlobalOrientationQuat(q);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
q.fromAngleAxis(20,NxVec3(1,0,0));
box4->setGlobalOrientationQuat(q);

q.fromAngleAxis(20,NxVec3(1,0,0));
box5->setGlobalOrientationQuat(q);

}

We then create two cloth objects which are cloth pipes connecting the static chutes that
the spheres will fall through.

void SetupPipeScene()
{

// Create cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0,18.2,-11);
clothDesc.globalPose.M.rotX(0.35);
clothDesc.thickness = 0.4;
clothDesc.density = 0.05;
clothDesc.dampingCoefficient = 0.1;
clothDesc.friction = 0.3;
clothDesc.collisionResponseCoefficient = 0.4;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_DAMPING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth1 = new MyCloth(gScene, clothDesc, "pipe.obj", 1);


objCloth1->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(objCloth1);

clothDesc.globalPose.t = NxVec3(0,12.2,0);
clothDesc.globalPose.M.rotX(NxHalfPiF32);

MyCloth* objCloth2 = new MyCloth(gScene, clothDesc, "pipe.obj", 1);


objCloth2->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(objCloth2);

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Take a look at the MyCloth object constructors in MyCloth.cpp. The constructor is
overloaded. Before this lesson, we were using the second constructor in the file which
creates a rectangular cloth given a width, height, and mesh density.

MyCloth.cpp

MyCloth::MyCloth(NxScene *scene, NxClothDesc &desc, NxReal w, NxReal h, NxReal


d, char *texFilename, bool tearLines)
{
mInitDone = false;
mTexId = 0;
mTexCoords = NULL;

NxClothMeshDesc meshDesc;
generateRegularMeshDesc(meshDesc, w, h, d, texFilename != NULL, tearLines);
init(scene, desc, meshDesc);
if (texFilename)
createTexture(texFilename);
}

We now use the first constructor, which takes an object file name as a parameter.

MyCloth.cpp

MyCloth::MyCloth(NxScene *scene, NxClothDesc &desc, char *objFileName, NxReal


scale)
{
mInitDone = false;
mTexId = 0;
mTexCoords = NULL;

NxClothMeshDesc meshDesc;
if (!generateObjMeshDesc(meshDesc, objFileName, scale))
{
printf("Error loading cloth mesh from file: %s\n", objFileName);
exit(-1);
}
init(scene, desc, meshDesc);
}

Instead of calling MyCloth::generateRegularMeshDesc() to build the cloth mesh


descriptor, we call MyCloth::generateObjMeshDesc().

bool MyCloth::generateObjMeshDesc(NxClothMeshDesc &desc, char *filename,


NxReal scale)
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
WavefrontObj wo;
wo.loadObj(filename);
if (wo.mVertexCount == 0) return false;

desc.numVertices = wo.mVertexCount;
desc.numTriangles = wo.mTriCount;
desc.pointStrideBytes = sizeof(NxVec3);
desc.triangleStrideBytes = 3*sizeof(NxU32);
desc.vertexMassStrideBytes = sizeof(NxReal);
desc.vertexFlagStrideBytes = sizeof(NxU32);
desc.points =
(NxVec3*)malloc(sizeof(NxVec3)*desc.numVertices);
desc.triangles =
(NxU32*)malloc(sizeof(NxU32)*desc.numTriangles*3);
desc.vertexMasses = 0;
desc.vertexFlags = 0;
desc.flags = 0;

// copy positions and indices


NxVec3 *vSrc = (NxVec3*)wo.mVertices;
NxVec3 *vDest = (NxVec3*)desc.points;
for (int i = 0; i < wo.mVertexCount; i++, vDest++, vSrc++)
*vDest = (*vSrc)*scale; // resize
memcpy((NxU32*)desc.triangles, wo.mIndices,
sizeof(NxU32)*desc.numTriangles*3);
return true;
}

We load in the Wavefront Object file, “pipe.obj”. The data from this file describes the
vertices of a cylinder. This data gets loaded into the cloth descriptor from which we make
our cloth objects, so we build two cylindrical pieces of cloth.

3 Two-Way Rigid Body-to-Cloth Interaction


In SetupPipeScene, we set up two-way interaction between the cloth and the rigid bodies
in the scene.

void SetupPipeScene()
{

clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;

}

Because of this, the rigid bodies will push the cloth and the cloth will, in turn, push back
on the rigid bodies.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
4 Interacting with the Cloth Pipes
Run the demo. The balls start out in a column and fall singly through the chute-pipe
structure. As they move through the cloth pipes, the balls are held up by the cloth and the
cloth stretches under their weight.

5 Conclusion
You have learned how to set up two-way interaction between rigid bodies and cloth.
You have also learned how to create a cloth in the shape of a geometric object saved in
the Wavefront Object file format. You can do the same with different types of object
files. You just need to assign the vertices and faces properly to the PhysX cloth
descriptor.

6 Related Classes, Functions, and Parameters


NxClothDesc
flags

NxClothFlag
NX_CLF_COLLISION_TWOWAY

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
Lesson 1005 – Attachment

1 Introduction
Cloth can be attached to dynamic or static objects so that it hangs from these objects. The
objects apply forces to the cloth. You can also attach dynamic objects to cloth so that
they hang from the cloth. The cloth applies forces to the objects.

2 Creating the Objects to Attach to the Cloth


The scene gets constructed in SetupAttachmentScene().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
void InitNx()
{

SetupAttachmentScene();

}

void SetupAttachmentScene()
{
sprintf(gTitleString, "Attachment Demo");

// Create objects in scene


groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(-7,12.25,0), NxVec3(2.5,1,1), 0);
NxActor* box2 = CreateBox(NxVec3(0,12.25,0), NxVec3(2.5,1,1), 0);
NxActor* box3 = CreateBox(NxVec3(7,12.25,0), NxVec3(2.5,1,1), 0);

NxActor* attachedBox = CreateBox(NxVec3(-7.2,4.5,1.6), NxVec3(1.25,1,1), 1);


NxActor* attachedSphere = CreateSphere(NxVec3(-0.25,4.0,2.0), 1.3, 1);
NxActor* attachedCapsule = CreateCapsule(NxVec3(9.0,5.5,2.0),2.0, 1, 1);

NxReal damping = 0.3;


attachedBox->setAngularDamping(damping);
attachedBox->setLinearDamping(damping);
attachedSphere->setAngularDamping(damping);
attachedSphere->setLinearDamping(damping);
attachedCapsule->setAngularDamping(damping);
attachedCapsule->setLinearDamping(damping);

NxQuat q;
q.fromAngleAxis(90,NxVec3(0,0,1));
attachedCapsule->setGlobalOrientationQuat(q);

// Cloth
NxClothDesc clothDesc;
clothDesc.globalPose.M.rotX(1.3);
clothDesc.thickness = 0.3;
clothDesc.attachmentResponseCoefficient = 1;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_BENDING_ORTHO;
clothDesc.flags |= NX_CLF_DAMPING | NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

// Cloth attaching to sphere

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
clothDesc.globalPose.t = NxVec3(0.75,5,2);
MyCloth* regularCloth1 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth1->getNxCloth()-
>attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth1);

// Cloth attaching to box


clothDesc.globalPose.t = NxVec3(-6.2,5,2);
MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth2->getNxCloth()-
>attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth2);

// Cloth attaching to capsule


clothDesc.globalPose.t = NxVec3(8.0,5,2);
clothDesc.attachmentTearFactor = 2.0;
MyCloth* regularCloth3 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth3->getNxCloth()->attachToShape(box3->getShapes()[0],
NX_CLOTH_ATTACHMENT_TEARABLE);
regularCloth3->getNxCloth()->attachToShape(attachedCapsule->getShapes()[0],
NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth3);
}

We build a static ground plane and three static box objects, box1, box2, and box3.

void SetupAttachmentScene()
{
// Create objects in scene
groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(-7,12.25,0), NxVec3(2.5,1,1), 0);
NxActor* box2 = CreateBox(NxVec3(0,12.25,0), NxVec3(2.5,1,1), 0);
NxActor* box3 = CreateBox(NxVec3(7,12.25,0), NxVec3(2.5,1,1), 0);

}

We then build three dynamic objects to hang from box1, box2, and box3, respectively.
These are attachedBox, attachedSphere, and attachedCapsule. We set the angular and
linear damping on these to 0.3.

void SetupAttachmentScene()
{

NxActor* attachedBox = CreateBox(NxVec3(-7.2,4.5,1.6), NxVec3(1.25,1,1), 1);
NxActor* attachedSphere = CreateSphere(NxVec3(-0.25,4.0,2.0), 1.3, 1);
NxActor* attachedCapsule = CreateCapsule(NxVec3(9.0,5.5,2.0),2.0, 1, 1);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
NxReal damping = 0.3;
attachedBox->setAngularDamping(damping);
attachedBox->setLinearDamping(damping);
attachedSphere->setAngularDamping(damping);
attachedSphere->setLinearDamping(damping);
attachedCapsule->setAngularDamping(damping);
attachedCapsule->setLinearDamping(damping);

}

We rotate the capsule actor 90 degrees about the z-axis so that it is parallel lengthwise to
the ground.

void SetupAttachmentScene()
{

NxQuat q;
q.fromAngleAxis(90,NxVec3(0,0,1));
attachedCapsule->setGlobalOrientationQuat(q);

}

3 Creating the Attaching Strips of Cloth


We finally create three strips of cloth. These strips will hang from the static boxes above
and hold up their respective dynamic actors below.

void SetupAttachmentScene()
{
...
// Cloth
NxClothDesc clothDesc;
clothDesc.globalPose.M.rotX(1.3);
clothDesc.thickness = 0.3;
clothDesc.attachmentResponseCoefficient = 1;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_BENDING_ORTHO;
clothDesc.flags |= NX_CLF_DAMPING | NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

// Cloth attaching to sphere


clothDesc.globalPose.t = NxVec3(0.75,5,2);
MyCloth* regularCloth1 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
regularCloth1->getNxCloth()-
>attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth1);

// Cloth attaching to box


clothDesc.globalPose.t = NxVec3(-6.2,5,2);
MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth2->getNxCloth()-
>attachToCollidingShapes(NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth2);

// Cloth attaching to capsule


clothDesc.globalPose.t = NxVec3(8.0,5,2);
clothDesc.attachmentTearFactor = 2.0;
MyCloth* regularCloth3 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth3->getNxCloth()->attachToShape(box3->getShapes()[0],
NX_CLOTH_ATTACHMENT_TEARABLE);
regularCloth3->getNxCloth()->attachToShape(attachedCapsule->getShapes()[0],
NX_CLOTH_ATTACHMENT_TWOWAY);
gCloths.push_back(regularCloth3);
}

We use the NxCloth::attachToCollidingShapes() function to attach the pieces of cloth to


whatever rigid body shapes they are colliding with when they are spawned. We pass in
the NX_CLOTH_ATTACHMENT_TWOWAY flag to this function so that the rigid
bodies pull on its attached cloth and the cloth pulls on its attached rigid bodies.

We also use the NxCloth::attachToShape() function to attach the cloth to specific rigid
body shapes. We pass in the NX_CLOTH_ATTACHMENT_TEARABLE flag to attach
the cloth to the static shape, “box3”, so that the cloth can be torn from the shape with a
sufficient force. We pass in the NX_CLOTH_ATTACHMENT_TWOWAY flag to attach
the cloth to the dynamic shape, “attachedCapsule”, so that the cloth pulls on the capsule
shape.

Try out one-way attachment between the cloth and the rigid bodies. This means the rigid
bodies pull on their attached cloth, but the cloth does not pull on its attached rigid bodies.

void SetupAttachmentScene()
{
...
clothDesc.globalPose.t = NxVec3(-6.2,5,2);
MyCloth* regularCloth2 = new MyCloth(gScene, clothDesc, 2, 8, 0.4);
regularCloth2->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(regularCloth2);

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
When you do this, you’ll notice the cloth gets pulled to the ground but the box falls to the
ground normally, as if no cloth were attached to it.

4 Conclusion
You have learned how to attach cloth to various shapes in the scene. You have also
learned the difference between two-way and one-way rigid body-to-cloth attachment.

5 Related Classes, Functions, and Parameters


NxCloth
attachToCollidingShapes()
attachToShape()

NxClothAttachmentFlag
NX_CLOTH_ATTACHMENT_TWOWAY
NX_CLOTH_ATTACHMENT_TEARABLE

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
Lesson 1006 – Tearing

1 Introduction
Cloth can be made to have a limited resistance to stretching and thus to get torn in pieces
under a large enough force. In this lesson, we will create a tearable cloth piece.

2 Creating Objects to Tear the Cloth


The scene gets constructed in SetupTearingScene().

void InitNx()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
{

SetupTearingScene();

}

void SetupTearingScene()
{
sprintf(gTitleString, "Tearing Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
NxActor* bar = CreateBox(NxVec3(0,12,0), NxVec3(3,0.5,0.5), 0);
NxActor* box = CreateBox(NxVec3(-2.3,4.0,0), NxVec3(0.5,0.5,0.5), 10);

// Cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(2.5,12,0);
clothDesc.globalPose.M.rotX(-NxHalfPiF32);
clothDesc.thickness = 0.1;
clothDesc.tearFactor = 2;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;
clothDesc.flags |= NX_CLF_TEARABLE | NX_CLF_VISUALIZATION;
// Tearable cloth

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 8, 0.1,


"rug512.bmp", gTearLines);
gCloths.push_back(regularCloth);

regularCloth->getNxCloth()->attachToShape(*bar->getShapes(), 0);
regularCloth->getNxCloth()->attachToShape(*box->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}

We build a bar to hang our cloth from and a dynamic box which will be attached to the
cloth on the right side.

void SetupTearingScene()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
NxActor* bar = CreateBox(NxVec3(0,12,0), NxVec3(3,0.5,0.5), 0);
NxActor* box = CreateBox(NxVec3(-2.3,4.0,0), NxVec3(0.5,0.5,0.5), 10);

}

3 Creating the Tearable Cloth


We then create our cloth.

void SetupTearingScene()
{

// Cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(2.5,12,0);
clothDesc.globalPose.M.rotX(-NxHalfPiF32);
clothDesc.thickness = 0.1;
clothDesc.tearFactor = 2;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;
clothDesc.flags |= NX_CLF_TEARABLE | NX_CLF_VISUALIZATION;
// Tearable cloth

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 8, 0.1,


"rug512.bmp", gTearLines);
gCloths.push_back(regularCloth);

regularCloth->getNxCloth()->attachToShape(*bar->getShapes(), 0);
regularCloth->getNxCloth()->attachToShape(*box->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);
}

We mark our cloth as tearable using the following flag.

clothDesc.flags |= NX_CLF_TEARABLE;

We attach our cloth to the actors.

MyCloth* regularCloth = new MyCloth(gScene, clothDesc, 5, 8, 0.1,


"rug512.bmp", gTearLines);
gCloths.push_back(regularCloth);

regularCloth->getNxCloth()->attachToShape(*bar->getShapes(), 0);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
regularCloth->getNxCloth()->attachToShape(*box->getShapes(),
NX_CLOTH_ATTACHMENT_TWOWAY);

The attachment is done using NxCloth::attachToShape(). This will attach the cloth to a
particular shape passed in.

Run the demo. The dynamic box attached to the cloth will fall, tearing the cloth as it falls.

The key parameter in this scene is the “tearFactor” on the cloth descriptor.

void SetupTearingScene()
{

NxClothDesc clothDesc;

clothDesc.tearFactor = 2;

clothDesc.flags |= NX_CLF_TEARABLE | NX_CLF_VISUALIZATION;

}

The lower you set the “tearFactor” value, the more easily the cloth gets torn. The higher
you set the “tearFactor” value, the more resistant the cloth gets torn.

4 Conclusion
You have learned how to create cloth that tears, attach the cloth to specified shapes, and
raise and lower the ease with which the cloth tears.

5 Related Classes, Functions, and Parameters


NxCloth
attachToShape()

NxClothDesc
tearFactor
flags

NxClothFlag
NX_CLF_TEARABLE

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 1007 – Pressure

1 Introduction
Cloth that forms an enclosed volume can be pressurized. In this way, you can create
balloon objects that you can inflate or deflate with variable pressure.

2 Creating the Object to Pressurize


The scene gets constructed in SetupPressureScene().

void InitNx()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
{

SetupPressureScene();

}

void SetupPressureScene()
{
sprintf(gTitleString, "Pressure Demo");

// Create walls
groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(0,0,5), NxVec3(10,5,0.5), 0);
NxActor* box2 = CreateBox(NxVec3(-10,0,0), NxVec3(0.5,5,5), 0);
NxActor* box3 = CreateBox(NxVec3( 10,0,0), NxVec3(0.5,5,5), 0);

// Create spheres
NxReal r = 1;
NxReal d = 1;
CreateSphere(NxVec3(0,20,0), r, d);
CreateSphere(NxVec3(-3,35, 0), r, d);
CreateSphere(NxVec3(3,50, 0), r, d);
CreateSphere(NxVec3(0,40, 0), r, d);
CreateSphere(NxVec3(-3,55, 0), r, d);
CreateSphere(NxVec3(3,60, 0), r, d);
CreateSphere(NxVec3(0,60, 0), r, d);
CreateSphere(NxVec3(-3,75, 0), r, d);
CreateSphere(NxVec3(3,80, 0), r, d);

// Create pressure cloth


NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0.3,6,0);
clothDesc.thickness = 0.1;
clothDesc.pressure = 1.5;
clothDesc.flags |= NX_CLF_PRESSURE;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth = new MyCloth(gScene, clothDesc, "cow.obj", 1);


gCloths.push_back(objCloth);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
First, we build the ground and three static boxes to create a small enclosure where we’ll
place our balloon cloth object.

void SetupPressureScene()
{

// Create walls
groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(0,0,5), NxVec3(10,5,0.5), 0);
NxActor* box2 = CreateBox(NxVec3(-10,0,0), NxVec3(0.5,5,5), 0);
NxActor* box3 = CreateBox(NxVec3( 10,0,0), NxVec3(0.5,5,5), 0);

}

Next, we create 9 spheres that we will drop on the balloon cloth object.

void SetupPressureScene()
{

// Create spheres
NxReal r = 1;
NxReal d = 1;
CreateSphere(NxVec3(0,20,0), r, d);
CreateSphere(NxVec3(-3,35, 0), r, d);
CreateSphere(NxVec3(3,50, 0), r, d);
CreateSphere(NxVec3(0,40, 0), r, d);
CreateSphere(NxVec3(-3,55, 0), r, d);
CreateSphere(NxVec3(3,60, 0), r, d);
CreateSphere(NxVec3(0,60, 0), r, d);
CreateSphere(NxVec3(-3,75, 0), r, d);
CreateSphere(NxVec3(3,80, 0), r, d);

}

3 Creating the Pressurized Cloth Object


We then create our pressurized balloon cloth object.

void SetupPressureScene()
{

// Create pressure cloth
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(0.3,6,0);
clothDesc.thickness = 0.1;
clothDesc.pressure = 1.5;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
clothDesc.flags |= NX_CLF_PRESSURE;
clothDesc.flags |= NX_CLF_BENDING;
clothDesc.flags |= NX_CLF_COLLISION_TWOWAY |
NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth = new MyCloth(gScene, clothDesc, "cow.obj", 1);


gCloths.push_back(objCloth);
}

We mark our cloth as pressurizable using the following flag.

clothDesc.flags |= NX_CLF_PRESSURE;

The pressure setting for the cloth object is here.

clothDesc.pressure = 1.5;

The higher you set the “pressure” value, the more the cow will inflate. The lower you set
the “pressure” value, the more the cow will deflate.

Finally, we build our cloth object from our cloth descriptor.

MyCloth* objCloth = new MyCloth(gScene, clothDesc, "cow.obj", 1);

We use a WaveFront cow-shaped object to make our balloon cloth object.

4 Conclusion
You have learned how to create cloth enclosures that can be pressurized, i.e., cloth
balloons.

5 Related Classes, Functions, and Parameters


NxClothDesc
pressure
flags

NxClothFlag
NX_CLF_ PRESSURE

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
Lesson 1008 – Self Collision

1 Introduction
Cloth can be enabled with self collision to prevent interpenetration. In this lesson, we
create four pieces of cloth, two of them are self collision disabled and two of them are
self collision enabled.

2 Creating Rigid Bodies and Cloth


The scene gets constructed in SetupSelfCollisionScene().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
void InitNx()
{

SetupSelfCollisionScene();

}

void SetupSelfCollisionScene()
{
sprintf(gTitleString, "Self Collision Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
NxActor* box1 = CreateBox(NxVec3(4,0.8,-4.8), NxVec3(2.5,0.2,1.5), 0);
NxActor* box2 = CreateBox(NxVec3(-4,0.8,-4.8), NxVec3(2.5,0.2,1.5), 0);

// Rotate the two boxes


NxMat33 Mz;
Mz.rotZ(15.0f/180.0f*PI);
box1->setGlobalOrientation(Mz);
box2->setGlobalOrientation(Mz);

// No self collision cloth 1


NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(5,10,-3.5);
clothDesc.globalPose.M.rotZ(90.0f/180.0f*PI);
clothDesc.thickness = 0.18;
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_BENDING_ORTHO;
clothDesc.flags |= NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth *dropCloth1 = new MyCloth(gScene, clothDesc, 5, 2.5, 0.15,


"nvidia.bmp");
gCloths.push_back(dropCloth1);

// Self collision cloth 1


clothDesc.flags |= NX_CLF_SELFCOLLISION;
clothDesc.globalPose.t = NxVec3(-3,10,-3.5);
MyCloth *dropCloth2 = new MyCloth(gScene, clothDesc, 5, 2.5, 0.15,
"nvidia.bmp");
gCloths.push_back(dropCloth2);

NxActor* stick1 = CreateBox(NxVec3(4,6,-1), NxVec3(0.15,0.2,2), 0);


NxActor* stick2 = CreateBox(NxVec3(-4,6,-1), NxVec3(0.15,0.2,2), 0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
// No self collision cloth 2
clothDesc.globalPose.t = NxVec3(7,6.2,-2.5);
clothDesc.globalPose.M.rotY(90.0f/180.0f*PI);
clothDesc.flags &= ~NX_CLF_SELFCOLLISION;
MyCloth* curtainCloth1 = new MyCloth(gScene, clothDesc, 3, 6, 0.15,
"nvidia.bmp");
curtainCloth1->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(curtainCloth1);

// Self collision cloth 2


clothDesc.globalPose.t = NxVec3(-1,6.2,-2.5);
clothDesc.flags |= NX_CLF_SELFCOLLISION;
MyCloth* curtainCloth2 = new MyCloth(gScene, clothDesc, 3, 6, 0.15,
"nvidia.bmp");
curtainCloth2->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(curtainCloth2);
}

We build two slopes that the cloth will fall on and two bars to hang the cloth from.

void SetupSelfCollisionScene()
{

NxActor* box1 = CreateBox(NxVec3(4,0.8,-4.8), NxVec3(2.5,0.2,1.5), 0);
NxActor* box2 = CreateBox(NxVec3(-4,0.8,-4.8), NxVec3(2.5,0.2,1.5), 0);

// Rotate the two boxes


NxMat33 Mz;
Mz.rotZ(15.0f/180.0f*PI);
box1->setGlobalOrientation(Mz);
box2->setGlobalOrientation(Mz);

NxActor* stick1 = CreateBox(NxVec3(4,6,-1), NxVec3(0.15,0.2,2), 0);
NxActor* stick2 = CreateBox(NxVec3(-4,6,-1), NxVec3(0.15,0.2,2), 0);

}

We build four cloth pieces. Two of them are self collision enabled and two are self
collision disabled.

void SetupSelfCollisionScene()
{

// No self collision cloth 1
NxClothDesc clothDesc;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
clothDesc.globalPose.t = NxVec3(5,10,-3.5);
clothDesc.globalPose.M.rotZ(90.0f/180.0f*PI);
clothDesc.thickness = 0.18;
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_BENDING_ORTHO;
clothDesc.flags |= NX_CLF_VISUALIZATION;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth *dropCloth1 = new MyCloth(gScene, clothDesc, 5, 2.5, 0.15,


"nvidia.bmp");
gCloths.push_back(dropCloth1);

// Self collision cloth 1


clothDesc.flags |= NX_CLF_SELFCOLLISION;
clothDesc.globalPose.t = NxVec3(-3,10,-3.5);
MyCloth *dropCloth2 = new MyCloth(gScene, clothDesc, 5, 2.5, 0.15,
"nvidia.bmp");
gCloths.push_back(dropCloth2);

// No self collision cloth 2
clothDesc.globalPose.t = NxVec3(7,6.2,-2.5);
clothDesc.globalPose.M.rotY(90.0f/180.0f*PI);
clothDesc.flags &= ~NX_CLF_SELFCOLLISION;
MyCloth* curtainCloth1 = new MyCloth(gScene, clothDesc, 3, 6, 0.15,
"nvidia.bmp");
curtainCloth1->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(curtainCloth1);

// Self collision cloth 2


clothDesc.globalPose.t = NxVec3(-1,6.2,-2.5);
clothDesc.flags |= NX_CLF_SELFCOLLISION;
MyCloth* curtainCloth2 = new MyCloth(gScene, clothDesc, 3, 6, 0.15,
"nvidia.bmp");
curtainCloth2->getNxCloth()->attachToCollidingShapes(0);
gCloths.push_back(curtainCloth2);
}

We use the following flag to enable self collision.

clothDesc.flags |= NX_CLF_SELFCOLLISION;

Running the demo, the two cloth pieces on the left have no self collision. Since there isn’t
collision detection between the vertexes, penetration is easy to occur within the cloth.
The two cloth pieces on the right are self collision enabled. Since the collision between
the vertexes is always detected, penetration can be prevented effectively.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
3 Conclusion
You have learned how to create cloth with self collision and observe the behavior of
NX_CLF_SELFCOLLISION flag.

4 Related Classes, Functions, and Parameters


NxClothDesc
flags

NxClothFlag
NX_CLF_SELFCOLLISION

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson 1009 – Interaction with Fluid

1 Introduction
By default the cloth will interact with the rigid bodies in the scene, but not including with
fluid. With some particular flag setting, the cloth can even interact with fluid.

2 Creating Objects
The scene gets constructed in SetupInteractionWithFluidScene().

void InitNx()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
{

SetupInteractionWithFluidScene();

}

void SetupInteractionWithFluidScene()
{
sprintf(gTitleString, "Interaction With Fluid Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
NxActor* devision1 = CreateBox(NxVec3(0,12,0), NxVec3(6,0.3,6), 0);
devision1->raiseActorFlag(NX_AF_FLUID_DISABLE_COLLISION);
gDevisions.push_back(devision1);
NxActor* devision2 = CreateBox(NxVec3(0,5,0), NxVec3(6,0.3,6), 0);
devision2->raiseActorFlag(NX_AF_FLUID_DISABLE_COLLISION);
gDevisions.push_back(devision2);

NxActor* devision3 = CreateBox(NxVec3(6,5,0), NxVec3(0.3,3.5,6), 0);


gDevisions.push_back(devision3);
NxActor* devision4 = CreateBox(NxVec3(-6,5,0), NxVec3(0.3,3.5,6), 0);
gDevisions.push_back(devision4);
NxActor* devision5 = CreateBox(NxVec3(0,5,6), NxVec3(6,3.5,0.3), 0);
gDevisions.push_back(devision5);
NxActor* devision6 = CreateBox(NxVec3(0,5,-6), NxVec3(6,3.5,0.3), 0);
gDevisions.push_back(devision6);

// Create fluid and fluid emitter


gFluid = CreateFluid();
gFluidEmitter = CreateFluidEmitter(NxVec3(0,15,0), 5, 5);

// Create cloth with pressure


NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(3,10,2.5);
clothDesc.thickness = 0.3;
clothDesc.density = 0.5;
clothDesc.fromFluidResponseCoefficient = 1.0;
clothDesc.toFluidResponseCoefficient = 0.4;
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_VISUALIZATION;
clothDesc.flags |= NX_CLF_PRESSURE;
clothDesc.flags |= NX_CLF_FLUID_COLLISION;
clothDesc.flags &= ~NX_CLF_GRAVITY;
clothDesc.externalAcceleration = NxVec3(0, 2.8, 0);

if (gHardwareCloth)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth1 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);


gCloths.push_back(objCloth1);

clothDesc.globalPose.t = NxVec3(-3,10,2.5);
MyCloth* objCloth2 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);
gCloths.push_back(objCloth2);

clothDesc.globalPose.t = NxVec3(0,10,-3);
MyCloth* objCloth3 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);
gCloths.push_back(objCloth3);
}

First we build a closure to limit the motion of the cloth objects.

void SetupInteractionWithFluidScene()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();
NxActor* devision1 = CreateBox(NxVec3(0,12,0), NxVec3(6,0.3,6), 0);
devision1->raiseActorFlag(NX_AF_FLUID_DISABLE_COLLISION);
gDevisions.push_back(devision1);
NxActor* devision2 = CreateBox(NxVec3(0,5,0), NxVec3(6,0.3,6), 0);
devision2->raiseActorFlag(NX_AF_FLUID_DISABLE_COLLISION);
gDevisions.push_back(devision2);

NxActor* devision3 = CreateBox(NxVec3(6,5,0), NxVec3(0.3,3.5,6), 0);


gDevisions.push_back(devision3);
NxActor* devision4 = CreateBox(NxVec3(-6,5,0), NxVec3(0.3,3.5,6), 0);
gDevisions.push_back(devision4);
NxActor* devision5 = CreateBox(NxVec3(0,5,6), NxVec3(6,3.5,0.3), 0);
gDevisions.push_back(devision5);
NxActor* devision6 = CreateBox(NxVec3(0,5,-6), NxVec3(6,3.5,0.3), 0);
gDevisions.push_back(devision6);

}

We then create the fluid that the cloth will interact with.

void SetupInteractionWithFluidScene()
{

// Create fluid and fluid emitter
gFluid = CreateFluid();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
gFluidEmitter = CreateFluidEmitter(NxVec3(0,15,0), 5, 5);

}
NxFluid* CreateFluid()
{
// Set structure to pass particles, and receive them after every simulation step
gParticles = new NxVec3[MAX_PARTICLES];

NxParticleData particles;
particles.numParticlesPtr = &gNumParticles;
particles.bufferPos = &gParticles[0].x;
particles.bufferPosByteStride = sizeof(NxVec3);

// Create a fluid descriptor


NxFluidDesc fluidDesc;
fluidDesc.simulationMethod = NX_F_SPH;
fluidDesc.kernelRadiusMultiplier = KERNEL_RADIUS_MULTIPLIER;
fluidDesc.restParticlesPerMeter = REST_PARTICLES_PER_METER;
fluidDesc.motionLimitMultiplier = MOTION_LIMIT_MULTIPLIER;
fluidDesc.packetSizeMultiplier = PACKET_SIZE_MULTIPLIER;
fluidDesc.restDensity = 1000;
fluidDesc.viscosity = 5;
fluidDesc.stiffness = 1000;
fluidDesc.damping = 1;

fluidDesc.particlesWriteData = particles;
fluidDesc.collisionMethod = NX_F_DYNAMIC | NX_F_STATIC;
fluidDesc.maxParticles = MAX_PARTICLES;

if (gScene->getSimType() == NX_SIMULATION_HW)
fluidDesc.flags |= NX_FF_HARDWARE;

NxFluid* mFluid = gScene->createFluid(fluidDesc);


assert(mFluid != NULL);
return mFluid;
}

NxFluidEmitter* CreateFluidEmitter(const NxVec3& pos, const NxReal dimX, const


NxReal dimY)
{
NxFluidEmitterDesc emitterDesc;
emitterDesc.dimensionX = dimX;
emitterDesc.dimensionY = dimY;
emitterDesc.relPose.t = pos;
emitterDesc.relPose.M.rotX(NxHalfPiF32);
emitterDesc.rate = 180;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
emitterDesc.randomAngle = 0.2;
emitterDesc.fluidVelocityMagnitude = 2*6.5;
emitterDesc.maxParticles = 0;
emitterDesc.particleLifetime = 4;
emitterDesc.type = NX_FE_CONSTANT_FLOW_RATE;
emitterDesc.shape = NX_FE_RECTANGULAR;
return gFluid->createEmitter(emitterDesc);
}

Finally we create three pressurized cloth objects which will float in the air.

void SetupInteractionWithFluidScene()
{

// Create cloth with pressure
NxClothDesc clothDesc;
clothDesc.globalPose.t = NxVec3(3,10,2.5);
clothDesc.thickness = 0.3;
clothDesc.density = 0.5;
clothDesc.fromFluidResponseCoefficient = 1.0;
clothDesc.toFluidResponseCoefficient = 0.4;
clothDesc.flags |= NX_CLF_BENDING | NX_CLF_VISUALIZATION;
clothDesc.flags |= NX_CLF_PRESSURE;
clothDesc.flags |= NX_CLF_FLUID_COLLISION;
clothDesc.flags &= ~NX_CLF_GRAVITY;
clothDesc.externalAcceleration = NxVec3(0, 2.8, 0);

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

MyCloth* objCloth1 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);


gCloths.push_back(objCloth1);

clothDesc.globalPose.t = NxVec3(-3,10,2.5);
MyCloth* objCloth2 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);
gCloths.push_back(objCloth2);

clothDesc.globalPose.t = NxVec3(0,10,-3);
MyCloth* objCloth3 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);
gCloths.push_back(objCloth3);

}

We use the following flag to enable the interaction between cloth and fluid.

clothDesc.flags |= NX_CLF_FLUID_COLLISION;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
With fromFluidResponseCoefficient and toFluidResponseCoefficient we can control the
force coefficient between cloth and fluid. Here we set fromFluidResponseCoefficient to
1.0 and toFluidResponseCoefficient to 0.4.

clothDesc.fromFluidResponseCoefficient = 1.0;
clothDesc.toFluidResponseCoefficient = 0.4;

The cloth objects are pressurized and simulate without gravity.

clothDesc.flags |= NX_CLF_PRESSURE;
clothDesc.flags &= ~NX_CLF_GRAVITY;

External forces are applied to the cloth so that the cloth will fly in the air.

clothDesc.externalAcceleration = NxVec3(0, 2.8, 0);

The cloth objects are created from the Wavefront Object file, "icoSphere.obj".

MyCloth* objCloth1 = new MyCloth(gScene, clothDesc, "icoSphere.obj", 0.09);

Running the demo, the three pressurized cloth objects will fly upward due to the external
acceleration. When they interact with fluid, the forces from fluid particles will push them
move downward and make them deform slightly. The forces from cloth will affect the
movements of the fluid particles as well.

3 Conclusion
You have learned how to enable the interaction between cloth and fluid and how to
control the force coefficient.

4 Related Classes, Functions, and Parameters


NxClothDesc
fromFluidResponseCoefficient
toFluidResponseCoefficient

NxClothFlag
NX_CLF_FLUID_COLLISION

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
Lesson 1010 – Metal

1 Introduction
Metal cloth can be used to simulate plastic deformation. For example dents in barrels, car
bodywork or metal doors can be displayed when there is a large impact on the surface. In
this lesson, we will create four metal cloth objects with different core actors.

2 Creating Objects
The scene gets constructed in SetupMetalScene().

void InitNx()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
{

SetupMetalScene();

}

void SetupMetalScene()
{
sprintf(gTitleString, "Cloth Metal Demo");

// Create the objects in the scene


groundPlane = CreateGroundPlane();
NxActor *box1 = CreateBox(NxVec3(0,0,10), NxVec3(20,10,0.5), 0);
NxActor *box2 = CreateBox(NxVec3(-20,0,0), NxVec3(0.5,10,10), 0);
NxActor *box3 = CreateBox(NxVec3( 20,0,0), NxVec3(0.5,10,10), 0);

NxClothDesc clothDesc;

if (gHardwareCloth)
clothDesc.flags |= NX_CLF_HARDWARE;

// Create metal cloth with different core actors


CreateMetalCloth(NxVec3(0.0f, 3.0f, 0.5f), 0, clothDesc, "barrel.obj");
CreateMetalCloth(NxVec3(0.5f, 20.0f, 0.0f), 1, clothDesc, "barrel.obj");
CreateMetalCloth(NxVec3(0.0f, 40.0f, 0.5f), 2, clothDesc, "barrel.obj");
CreateMetalCloth(NxVec3(0.5f, 60.0f, 0.0f), 3, clothDesc, "barrel.obj");
}

We build the ground and three static boxes to create a small enclosure where we’ll place
our metal cloth objects.

void SetupMetalScene()
{

// Create the objects in the scene
groundPlane = CreateGroundPlane();
NxActor *box1 = CreateBox(NxVec3(0,0,10), NxVec3(20,10,0.5), 0);
NxActor *box2 = CreateBox(NxVec3(-20,0,0), NxVec3(0.5,10,10), 0);
NxActor *box3 = CreateBox(NxVec3( 20,0,0), NxVec3(0.5,10,10), 0);

}

The metal cloth objects are created in CreateMetalCloth() function.

void CreateMetalCloth(const NxVec3 &position, int mode, NxClothDesc &clothDesc,


char *meshName)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
{
// Global metal data
NxReal impulseThreshold = 50.0f;
NxReal penetrationDepth = 0.5f;
NxReal maxDeformationDistance = 0.5f;

NxBodyDesc coreBodyDesc;
coreBodyDesc.linearDamping = 0.2f;
coreBodyDesc.angularDamping = 0.2f;

NxActorDesc coreActorDesc;
coreActorDesc.density = 0.1f;
coreActorDesc.body = &coreBodyDesc;

// Create the shape, no need for size info because it is automatically generated by
// the cloth
if (mode == 0) { // Sphere as core
coreActorDesc.shapes.pushBack(new NxSphereShapeDesc());
}
else if (mode == 1) { // Capsule as core
coreActorDesc.shapes.pushBack(new NxCapsuleShapeDesc());
}
else if (mode == 2) { // Box as core
coreActorDesc.shapes.pushBack(new NxBoxShapeDesc());
}
else if (mode == 3) { // Compound of spheres as core
const NxU32 numSpheres = 10;
for (NxU32 i = 0; i < numSpheres; i++)
coreActorDesc.shapes.pushBack(new NxSphereShapeDesc());
}
else return;

NxActor *coreActor = gScene->createActor(coreActorDesc);


gMetalCores.push_back(coreActor);

// Clean up allocations
for (NxU32 i = 0; i < coreActorDesc.shapes.size(); i++)
delete coreActorDesc.shapes[i];

clothDesc.globalPose.t = position;

MyCloth *objCloth = new MyCloth(gScene, clothDesc, meshName, 1.0f);


if (!objCloth->getNxCloth())
{
printf("Error: Unable to create the cloth for the current scene.\n");
delete objCloth;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
} else
{
gCloths.push_back(objCloth);
objCloth->getNxCloth()->attachToCore(coreActor, impulseThreshold,
penetrationDepth, maxDeformationDistance);
}
}

To build a metal cloth object, we need to create a core actor first. Here we create four
different core types.

void CreateMetalCloth(const NxVec3 &position, int mode, NxClothDesc &clothDesc,


char *meshName)
{

NxActorDesc coreActorDesc;
coreActorDesc.density = 0.1f;
coreActorDesc.body = &coreBodyDesc;

// Create the shape, no need for size info because it is automatically generated by
// the cloth
if (mode == 0) { // Sphere as core
coreActorDesc.shapes.pushBack(new NxSphereShapeDesc());
}
else if (mode == 1) { // Capsule as core
coreActorDesc.shapes.pushBack(new NxCapsuleShapeDesc());
}
else if (mode == 2) { // Box as core
coreActorDesc.shapes.pushBack(new NxBoxShapeDesc());
}
else if (mode == 3) { // Compound of spheres as core
const NxU32 numSpheres = 10;
for (NxU32 i = 0; i < numSpheres; i++)
coreActorDesc.shapes.pushBack(new NxSphereShapeDesc());
}
else return;

NxActor *coreActor = gScene->createActor(coreActorDesc);



}

The cloth needs to be attached to the core actor to enable the metal feature. We use
NxCloth::attachToCore() function to attach the cloth to actors.

void CreateMetalCloth(const NxVec3 &position, int mode, NxClothDesc &clothDesc,


char *meshName)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
{

// Global metal data
NxReal impulseThreshold = 50.0f;
NxReal penetrationDepth = 0.5f;
NxReal maxDeformationDistance = 0.5f;

MyCloth *objCloth = new MyCloth(gScene, clothDesc, meshName, 1.0f);


if (!objCloth->getNxCloth())
{
printf("Error: Unable to create the cloth for the current scene.\n");
delete objCloth;
} else
{
gCloths.push_back(objCloth);
objCloth->getNxCloth()->attachToCore(coreActor, impulseThreshold,
penetrationDepth, maxDeformationDistance);
}

}

The following parameters are needed when calling NxCloth::attachToCore().

• actor: The actor to attach to the cloth. Currently only sphere shapes, box shapes,
capsule shapes and compounds of spheres are supported.

• impulseThreshold: Threshold for when deformation is allowed.

• penetrationDepth: Can be used to increase the amount of deformation due to


impacts.

• maxDeformationDistance: Maximum deviation of cloth particles from initial


position.

3 Conclusion
You have learned how to create metal cloth with different core actors.

4 Related Classes, Functions, and Parameters


NxCloth
attachToCore ()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
Lesson 1101 – SoftBodyCube

1 Introduction
Soft body simulation has been done to a certain degree in games for substantial realism.
The advantage of the technique is that it makes the simulation of triangle soups possible,
i.e. every possible mesh can be simulated.

2 General steps in soft body


In this lesson we create a basic soft body cube to illustrate the procedure. Take a look at
InitNx() in which physics SDK and scene are created for simulation.

……
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

1
void InitNx()
{
…..

// Create the physics SDK


gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION,
gAllocator);
if (!gPhysicsSDK) return;
……

NxInitCooking();
……

// Create the rigid body master scenes

NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.groundPlane = true;
if(bHardwareScene)
sceneDesc.simType = NX_SIMULATION_HW;
else
sceneDesc.simType = NX_SIMULATION_SW;

gScene = gPhysicsSDK->createScene(sceneDesc);

if(!gScene)
{
sceneDesc.simType = NX_SIMULATION_SW;
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene) return;
}

…..
// create soft body.

SoftBodyCube();

……

// Get the current time


UpdateTime();

// Start the first frame of the simulation


if (gScene) StartPhysics();
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
The static and dynamic rigid body actors of the scene can interact with the soft body.
And the soft body can be attached to actors in the rigid body scene. The rigid bodies can
pull and push the soft body and the soft body can push and pull the rigid bodies if set
right flag to the soft body.

3 Create soft body cube


A simple soft body cube is created in SetupSoftBodyCube () which is called in InitNx().

void InitNx()
{

SetupSoftBodyCube ();

}
//Create soft body cube
void SetupSoftBodyCube()
{
sprintf (gTitleString, "Basic Example");

// fill out the soft body descriptor which feed back to SDK to create soft body instance.
NxSoftBodyDesc softBodyDesc;
softBodyDesc.globalPose.t = NxVec3 (0, 5, 0);
softBodyDesc.particleRadius = 0.2f;
softBodyDesc.volumeStiffness = 0.2f;
softBodyDesc.stretchingStiffness = 0.2f;
softBodyDesc.friction = 1.0f;
softBodyDesc.collisionResponseCoefficient = 0.9f;
softBodyDesc.solverIterations = 5;

// apply hardware flag to the soft body


if (gHardwareSoftBodySimulation)
softBodyDesc.flags |= NX_SBF_HARDWARE;

gScene->setGravity(NxVec3(0.0f, -9.80f, 0.0f));

// Create the objects in the scene


MySoftBody *softBody = new MySoftBody(gScene,
softBodyDesc,SOFTBODY_BOX,3,3,3,1);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
gSoftBodies.push_back(softBody);
}
//CreateSphere(NxVec3(0,13,0),1,0);

// set camera position and direction


gCameraPos.set(0.0f, 15.0f, 15.0f);
gCameraForward.set(-0.1f, -0.22f, -0.5f);
gCameraForward.normalize();
}

First we set the global position of the soft body by filling out the soft body descriptor:

softBodyDesc.globalPose.t = NxVec3(0,5,0);

We fix the soft body 5 units up so that the soft body cube can drop down to ground plane.

Next, we set the particle radius of the soft body.

softBodyDesc.particleRadius = 0.2f;

A good particle radius value is the maximal distance between two adjacent soft body
particles in their rest pose. Visual artifacts or collision problems may appear if the
particle radius is too small.

Then we set the volumeStiffness of the soft cube.

softBodyDesc.volumeStiffness = 0.2f;

The range for volumeStiffness is 0 to 1 and only has effect if


NX_SBF_VOLUME_CONSERVATION is set. This parameter defines how strongly
the soft body counteracts deviations from the rest volume.

We set the stretching stiffness of the soft body.

softBodyDesc.stretchingStiffness = 0.2f;

This defines how difficult it is to stretch the soft body.

And we also set the friction of the soft body.

softBodyDesc.friction = 1.0f;

This defines the damping of the velocities of soft body particles that are in contact.

We can control the behavior of soft body by applying different flag value to descriptor.
The soft body flag is default as NX_SBF_GRAVITY |
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

4
NX_SBF_VOLUME_CONSERVATION. In this lesson, we don’t have special value to
it.

Finally, we pass the soft body descriptor into our MySoftBody object constructor to build
the soft body. We also push our soft body on the application’s stack of soft bodies.
MySoftBody is a user-defined interface for soft body and will be introduced in next
section.

MySoftBody *softBody = new MySoftBody(gScene,


softBodyDesc,SOFTBODY_BOX,3,3,3,1);

gSoftBodies.push_back(softBody);

Ok, now the soft body has already been created in PhysX.

4 MySoftBody object
The MySoftBody object is an interface of NxSoftbody. And it is in MySoftbody.h and
MySoftBody.cpp.

MySoftBody looks like the following code:

MySoftBody.h

class MySoftBody {
public:
……
bool mInitDone;

NxMeshData mReceiveBuffers; // data buffer for rendering


NxScene *mScene; // master scene
NxSoftBody *mSoftBody; // soft body pointer
NxSoftBodyMesh *mSoftBodyMesh; // soft body mesh

ObjMesh *mSurfaceMesh; // surface obj mesh, not is part of PhysX asset.

NxArray<NxVec3> mTetraRenderVerts;
NxArray<NxVec3> mTetraRenderNormals;

NxReal mScale;
NxVec3 mOffset;
};

Code in MySoftBody.cpp:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
MySoftBody::MySoftBody(NxScene *scene, NxSoftBodyDesc &desc, SoftBodyType
type, NxReal wx, NxReal wy, NxReal wz, NxReal h, bool tearLines)
{
mInitDone = false;
NxSoftBodyMeshDesc meshDesc;
switch (type)
{
case SOFTBODY_BOX: generateBoxMeshDesc(meshDesc, wx,wy,wz,h,
tearLines); break;
case SOFTBODY_SPHERE: generateSphereMeshDesc(meshDesc, wx,wy,wz,h,
tearLines); break;
}
init(scene, desc, meshDesc, NULL);
}

There are two methods to build the tet mesh: one is simple computing box/sphere tet
mesh as above code in generateBoxMeshDesc()/generateSphereMeshDesc(), the other is
reading mesh data from tet file as following code:

bool MySoftBody::generateTetMeshDesc(NxSoftBodyMeshDesc &desc, char *filename,


NxReal scale)
{
static const NxU16 MESH_STRING_LEN = 256;
char s[MESH_STRING_LEN];
int i0,i1,i2,i3;
NxVec3 v;

FILE *f = fopen(filename, "r");


if (!f) return false;

NxArray<NxVec3> tempVertices;
NxArray<NxU32> tempIndices;
NxBounds3 bounds;
bounds.setEmpty();

……

NxReal diag = bounds.min.distance(bounds.max);


mOffset = (bounds.min + bounds.max) * 0.5f;

NxVec3 *vSrc = (NxVec3*)tempVertices.begin();


NxVec3 *vDest = (NxVec3*)desc.vertices;
for (NxU32 i = 0; i < desc.numVertices; i++, vDest++, vSrc++)
*vDest = ((*vSrc) - mOffset)*scale;
memcpy((NxU32*)desc.tetrahedra, tempIndices.begin(),
sizeof(NxU32)*desc.numTetrahedra*4);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

6
return true;
}

MySoftBody::generateTetMeshDesc () builds mesh data of softbody from tet file. The


tetrahedron mesh gets stored in the soft body mesh descriptor, “meshDesc” which is
passed into MySoftbody::init () along with the soft body descriptor, “desc”.

MySoftBody::MySoftBody (NxScene *scene, NxSoftBodyDesc &desc, SoftBodyType


type, NxReal wx, NxReal wy, NxReal wz, NxReal h, bool tearLines)
{
……
init(scene, desc, meshDesc, NULL);
}

void MySoftBody::init(NxScene *scene, NxSoftBodyDesc &desc,


NxSoftBodyMeshDesc &meshDesc, ObjMesh *surfaceMesh)
{
mScene = scene;
mScale = 1.0f;
mOffset.zero();
mSurfaceMesh = NULL;

……

releaseMeshDescBuffers(meshDesc);

allocateReceiveBuffers(meshDesc.numVertices, meshDesc.numTetrahedra);
desc.softBodyMesh = mSoftBodyMesh;
desc.meshData = mReceiveBuffers;
mSoftBody = scene->createSoftBody(desc);
mInitDone = true;
}

We store the mesh descriptor in mSoftBodyMesh. Finally NxScene::createSoftBody() is


called to build PhysX soft body object, mSoftBody, using the soft body descriptor.

mSoftBody = scene->createSoftBody(desc);

The “scene” we create the soft body with is gScene from SetupBasicSoftBody () in
Lesson1101.cpp.

void SetupBasicSoftBody ()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

7
MySoftBody *softBody = new
MySoftBody(gScene,softBodyDesc,SOFTBODY_BOX,3,3,3,1);…
}

Once again, objects in the master scene that is used to create hardware soft body with
NxScene::createSoftBody() will interact with the hardware scene soft body.

5 Conclusion
You have created your first soft body. Congratulations! Try out playing with the soft
body descriptor parameters to see what different sorts of soft body objects you can create.

6 Related Classes, Functions, and Parameters


NxMeshData

NxSoftBodyMesh

NxSoftBodyDesc
NxSoftBodyMesh *softBodyMesh;
NxMat34 globalPose;
NxReal particleRadius;
NxReal density;
NxReal volumeStiffness;
NxReal stretchingStiffness;
NxReal dampingCoefficient;
NxReal friction;
NxReal tearFactor;
NxReal collisionResponseCoefficient;
NxReal attachmentResponseCoefficient;
NxReal attachmentTearFactor;
NxReal toFluidResponseCoefficient;
NxReal fromFluidResponseCoefficient;
NxReal minAdhereVelocity;
NxU32 solverIterations;
NxVec3 externalAcceleration;
NxReal wakeUpCounter;
NxReal sleepLinearVelocity;
NxMeshData meshData;
NxSoftBodySplitPairData splitPairData;
NxCollisionGroup collisionGroup;
NxGroupsMask groupsMask;
NxU16 forceFieldMaterial;
NxBounds3 validBounds;
NxReal relativeGridSpacing;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

8
NxU32 flags;
void* userData;
const char* name;
NxCompartment * compartment;

NxSoftBodyFlag
NxSoftBodyFlag {
NX_SBF_STATIC = (1<<1),
NX_SBF_DISABLE_COLLISION = (1<<2),
NX_SBF_SELFCOLLISION = (1<<3),
NX_SBF_VISUALIZATION = (1<<4),
NX_SBF_GRAVITY = (1<<5),
NX_SBF_VOLUME_CONSERVATION = (1<<6),
NX_SBF_DAMPING = (1<<7),
NX_SBF_COLLISION_TWOWAY = (1<<8),
NX_SBF_TEARABLE = (1<<9),
NX_SBF_HARDWARE = (1<<10),
NX_SBF_COMDAMPING = (1<<11),
NX_SBF_VALIDBOUNDS = (1<<12),
NX_SBF_FLUID_COLLISION = (1<<13)

NxSoftBodyAttachmentFlag
NX_SOFTBODY_ATTACHMENT_TWOWAY = (1<<0),
NX_SOFTBODY_ATTACHMENT_TEARABLE = (1<<1)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

9
Lesson 1102 – PalmTree

1 Introduction
In this lesson, we will build a soft body tree which sways in the wind by applying
external acceleration.

2 Create Palm Tree


General steps in creating soft body are same as Lesson1101.

This lesson will create soft body tree in SetupPalmScene ().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
void SetupPalmScene()
{
sprintf(gTitleString, "PalmTree Example");

// Create the objects in the scene

NxSoftBodyDesc softBodyDesc;
softBodyDesc.globalPose.t = NxVec3(0,3,0);
softBodyDesc.particleRadius = 0.2f;
softBodyDesc.volumeStiffness = 0.5f;
softBodyDesc.stretchingStiffness = 1.0f;
softBodyDesc.friction = 1.0f;
softBodyDesc.collisionResponseCoefficient = 0.9f;
softBodyDesc.solverIterations = 5;
softBodyDesc.flags |= NX_SBF_COLLISION_TWOWAY;

if (gHardwareSoftBodySimulation)
softBodyDesc.flags |= NX_SBF_HARDWARE;

gScene->setGravity(NxVec3(0.0f, -1.0f, 0.0f));

char *fileName = "palmNormalized";

char tetFileName[256], objFileName[256], s[256];


sprintf(tetFileName, "%s.tet", fileName);
sprintf(objFileName, "%s.obj", fileName);

// mesh surface data


ObjMesh *objMesh = new ObjMesh();
objMesh->loadFromObjFile(FindMediaFile(objFileName, s));

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
gSoftBodies.push_back(softBody);
gObjMeshes.push_back(objMesh);

softBody->getNxSoftBody()->setExternalAcceleration(NxVec3(5,0,0));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
NxActor *caps0 = CreateCompoundCapsule(NxVec3(-0.46f, -0.15f, 0.68f),
1.7f, 0.21f, 2.0f, 0.18f);
caps0->userData = (void*)&gInvisible;

// create tree trunk by capsule and spherical joint


CreateRopeSphericalJoint(NULL, caps0, NxVec3(-0.46f, -0.0f, 0.68f),
NxVec3(0,0,1));

softBody->getNxSoftBody()->attachToCollidingShapes(0);
}

// set camera position and direction


gCameraPos.set(0.0f, 7.5f, 9.5f);
gCameraForward.set(-0.1f, -0.22f, -0.5f);
gCameraForward.normalize();
}

First we set the global position of the soft body.

softBodyDesc.globalPose.t = NxVec3(0,3,0);

We fix the soft body 3 units up.

Next, we set the particle radius of the soft body.

softBodyDesc.particleRadius = 0.2f;

We set the volumeStiffness of the soft cube.

softBodyDesc.volumeStiffness = 0.5f;

The range for volumeStiffness is 0 to 1 and only has effect if


NX_SBF_VOLUME_CONSERVATION is set. This parameter defines how strongly
the soft body counteracts deviations from the rest volume.

We set the stretching stiffness of the soft body.

softBodyDesc.stretchingStiffness = 1.0;

This defines how difficult it is to stretch the soft body.

Next we set the friction of the soft body.

softBodyDesc.friction = 1.0f;

This defines the damping of the velocities of soft body particles that are in contact.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
The soft body flag is default as NX_SBF_GRAVITY |
NX_SBF_VOLUME_CONSERVATION. Here we apply
NX_SBF_COLLISION_TWOWAY to the soft body to make the soft body can push the
rigid body.

We pass the soft body descriptor into our MySoftBody object constructor to build the soft
body. We also push our soft body on the application’s stack of soft bodies.

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh);

gSoftBodies.push_back (softBody);

After the soft body is created, an external acceleration is applied to it to simulate wind
effect.

softBody->getNxSoftBody()->setExternalAcceleration(NxVec3(5,0,0));

Tree trunk is created by capsules and spherical joints in

CreateRopeSphericalJoint(NULL, caps0, NxVec3(-0.46f, -0.0f, 0.68f), NxVec3(0,0,1));

3 Conclusion
You have created a soft body tree and it can sway in wind by applying external
acceleration.

4 Related Classes, Functions, and Parameters


NxSoftBodyDesc
NxVec3 externalAcceleration;

NxSoftBodyFlag
NxSoftBodyFlag {

NX_SBF_COLLISION_TWOWAY = (1<<8),

NxSoftBodyAttachmentFlag
NX_SOFTBODY_ATTACHMENT_TWOWAY = (1<<0),
NX_SOFTBODY_ATTACHMENT_TEARABLE = (1<<1)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 1103 – BunnyScene

1 Introduction
This lesson will introduce how to make soft objects to softer by adjust soft body’s
properties’ value.

2 Create bunny scene


General steps in creating soft body are same as Lesson1101.

The scene creates soft body in SetupBunnyScene ().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
void SetupBunnyScene()
{
sprintf(gTitleString, "BunnyScene Example");

NxSoftBodyDesc softBodyDesc;
softBodyDesc.globalPose.t = NxVec3(0,35,-1);
softBodyDesc.particleRadius = 0.2f;
softBodyDesc.volumeStiffness = 1.0f;
softBodyDesc.stretchingStiffness = 0.07f;
softBodyDesc.friction = 1.0f;

if (gHardwareSoftBodySimulation)
softBodyDesc.flags |= NX_SBF_HARDWARE;

gScene->setGravity(NxVec3(0.0f, -10.0f, 0.0f));

char *fileName = "bunnyNormalized";

char tetFileName[256], objFileName[256], s[256];


sprintf(tetFileName, "%s.tet", fileName);
sprintf(objFileName, "%s.obj", fileName);

// The obj mesh is for mesh surface rendering, not physX mesh.

ObjMesh *objMesh = new ObjMesh();


objMesh->loadFromObjFile(FindMediaFile(objFileName, s));

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
gSoftBodies.push_back(softBody);
gObjMeshes.push_back(objMesh);
}

NxMat33 rot;
NxActor *plate0 = CreateBox(NxVec3(-2.5,10,0), NxVec3(20.0f, 1.0f, 10.0f),
0.0f);
rot.rotZ(-1.5f);
plate0->setGlobalOrientation(rot);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
NxActor *plate1 = CreateBox(NxVec3(2.5,10,0), NxVec3(20.0f, 1.0f, 10.0f),
0.0f);
rot.rotZ(1.5f);
plate1->setGlobalOrientation(rot);

// set camera position and direction


gCameraPos.set(-5.0f, 40.0f, 35.0f);
gCameraForward.set(0.2,-1.1,-2);
gCameraForward.normalize();
}

We fill out soft body descriptor with parameters for the soft body.

First we set the global position of the soft body.

softBodyDesc.globalPose.t = NxVec3(0,35,-1);

Next, we set the particle radius of the soft body.

softBodyDesc.particleRadius = 0.2f;

We set the volumeStiffness of the soft cube.

softBodyDesc.volumeStiffness = 1.0;

The range for volumeStiffness is 0 to 1 and only has effect if


NX_SBF_VOLUME_CONSERVATION is set. This parameter defines how strongly
the soft body counteracts deviations from the rest volume.

We set the stretching stiffness of the soft body.

softBodyDesc.stretchingStiffness = 0.07f;

Stretching stiffness defines how difficult it is to stretch the soft body. Here we take low
value, so bunny is very easy to be stretched.

Next we set the friction of the soft body.

softBodyDesc.friction = 1.0f;

This defines the damping of the velocities of soft body particles that are in contact.

The soft body flag is default as NX_SBF_GRAVITY |


NX_SBF_VOLUME_CONSERVATION.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
Finally, we pass the soft body descriptor into our MySoftBody object constructor to build
the soft body. We also push our soft body on the application’s stack of soft bodies.

MySoftBody *softBody = new MySoftBody(gScene,


softBodyDesc,SOFTBODY_BOX,3,3,3,1);

gSoftBodies.push_back(softBody);

Ok, now the soft body has already been created in PhysX.

3 Conclusion
We can change the behavior of soft body by adjusting the volume stiffness and stretch
stiffness value.

4 Related Classes, Functions, and Parameters


NxMeshData

NxSoftBodyMesh

NxSoftBodyDesc

NxReal volumeStiffness;
NxReal stretchingStiffness;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 1104 – Self-Collision

1 Introduction
We will demonstrate soft body self-collision in this lesson.

2 Create Self-Collision soft body


General steps in creating soft body are same as Lesson1101.

This lesson will create two soft body belts in SetupSelfCollisionScene (). One is general
soft body, the other is enabled self-collision.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
void SetupSelfCollisionScene()
{
sprintf(gTitleString, "SelfCollision Example");

NxSoftBodyDesc softBodyDesc;
softBodyDesc.setToDefault();
softBodyDesc.globalPose.t = NxVec3(-5,10,0);
softBodyDesc.particleRadius = 0.5;
softBodyDesc.volumeStiffness = 0.0f;
softBodyDesc.stretchingStiffness = 0.2f;
softBodyDesc.friction = 1.0f;

if (gHardwareSoftBodySimulation)
softBodyDesc.flags |= NX_SBF_HARDWARE;

gScene->setGravity(NxVec3(0.0f, -9.8f, 0.0f));

char *fileName = "belt_SB";

char tetFileName[256], objFileName[256], s[256];


sprintf(tetFileName, "%s.tet", fileName);
sprintf(objFileName, "%s.obj", fileName);

// The obj mesh is for mesh surface rendering, not physX mesh.

ObjMesh *objMesh = new ObjMesh();


objMesh->loadFromObjFile(FindMediaFile(objFileName, s));

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh,0.2);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
gSoftBodies.push_back(softBody);
gObjMeshes.push_back(objMesh);
}

softBodyDesc.globalPose.t = NxVec3(5,10,0);
softBodyDesc.flags |= NX_SBF_SELFCOLLISION;

softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh,0.2);
if (!softBody->getNxSoftBody())
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
gSoftBodies.push_back(softBody);
gObjMeshes.push_back(objMesh);
}
// set camera position and direction
gCameraPos.set(-10.0f, 5.0f, 15.0f);
gCameraForward.set(1,0,-2);
gCameraForward.normalize();
}

We fill out soft body descriptor with parameters for the soft body.

First we set the global position of the soft body.

softBodyDesc.globalPose.t = NxVec3(-5,10,0);

Next, we set the particle radius of the soft body.

softBodyDesc.particleRadius = 0.5f;

We set the stretching stiffness of the soft body.

softBodyDesc.stretchingStiffness = 0.2;

This defines how difficult it is to stretch the soft body.

Next we set the friction of the soft body.

softBodyDesc.friction = 1.0f;

This defines the damping of the velocities of soft body particles that are in contact.

We create two soft body belts in this lesson, one is general soft body, the other is with
self-collision.

The soft body flag is default as NX_SBF_GRAVITY |


NX_SBF_VOLUME_CONSERVATION. Here we apply NX_SBF_SELFCOLLISION
to the soft body in order to make the soft body can collide among their particles. Self-
collisions are only handled inbetween the soft body's particles, i.e., particles do not
collide against the tetrahedra of the soft body. The user should therefore specify a large
enough particleRadius to avoid most interpenetration.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

3
Finally, we pass the soft body descriptor into our MySoftBody object constructor to build
the soft body. We also push our soft body on the application’s stack of soft bodies.

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh);

gSoftBodies.push_back (softBody);

3 Conclusion
We can compare the different behavior between general soft body and self-collision soft
body.

4 Related Classes, Functions, and Parameters


NxSoftBodyDesc
particleRadius
stretchingStiffness

NxSoftBodyFlag
NxSoftBodyFlag {
NX_SBF_SELFCOLLISION(1<<3);
}

NxSoftBodyAttachmentFlag
NX_SOFTBODY_ATTACHMENT_TWOWAY = (1<<0),
NX_SOFTBODY_ATTACHMENT_TEARABLE = (1<<1)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
Lesson 1105 – SoftWheelCar

1 Introduction
We will demonstrate how soft body car wheel attach to axis. Thus we can design some
funny scenarios by attachment of soft body.

2 Create soft wheel car


General steps in creating soft body are same as Lesson1101.

This lesson will create soft body tree in SetupSoftWheelCarScene ().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
The car wheels are created by soft body, and the chassis is composed of box and
capsules.

void SetupSoftWheelCarScene()
{
sprintf(gTitleString, "SoftWheelCar Example");

// Create the objects in the scene


//gGroundPlane = CreateGroundPlane();

NxSoftBodyDesc softBodyDesc;
softBodyDesc.particleRadius = 0.2f;
softBodyDesc.density = 1.0f;
softBodyDesc.volumeStiffness = 1.0f;
softBodyDesc.stretchingStiffness = 1.0f;
softBodyDesc.friction = 1.0f;
softBodyDesc.collisionResponseCoefficient = 0.9f;
softBodyDesc.attachmentResponseCoefficient = 0.8f;
softBodyDesc.solverIterations = 5;

if (gHardwareSoftBodySimulation)
softBodyDesc.flags |= NX_SBF_HARDWARE;

char *fileName = "wheelNormalized";

char tetFileName[256], objFileName[256], s[256];


sprintf(tetFileName, "%s.tet", fileName);
sprintf(objFileName, "%s.obj", fileName);

MySoftBody *softBody;

NxReal carHeight = 7.5f;


NxReal stiffness = 0.9f;
NxMat34 capsulePose = NxMat34(NxMat33(NX_IDENTITY_MATRIX),
NxVec3(-4,carHeight,-5.0f));
capsulePose.M.rotX(NxHalfPiF32);
NxActor *caps1 = CreateOrientedCapsule(capsulePose, 7.1f, 1.3f, 1.0f);
capsulePose.t = NxVec3(4,carHeight,-5.0f);
NxActor *caps2 = CreateOrientedCapsule(capsulePose, 7.1f, 1.3f, 1.0f);

// The obj mesh is for mesh surface rendering, not physX mesh.

ObjMesh *objMesh = new ObjMesh();


objMesh->loadFromObjFile(FindMediaFile(objFileName, s));

NxMat33 rot;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com

2
rot.rotX(NxPiF32);
softBodyDesc.globalPose.t = NxVec3(4.0f, carHeight, 3.4f);
softBodyDesc.globalPose.M = rot;
softBodyDesc.stretchingStiffness = stiffness;
softBody = new MySoftBody(gScene, softBodyDesc,
FindMediaFile(tetFileName,s), objMesh);
if (!softBody->getNxSoftBody())
{
printf("Error: Unable to create the SoftBody for the current scene.\n");
delete softBody;
} else
{
gObjMeshes.push_back(objMesh);

// attach soft body to colliding rigid body

softBody->getNxSoftBody()-
>attachToCollidingShapes(NX_SOFTBODY_ATTACHMENT_TWOWAY);

gSoftBodies.push_back(softBody);

……

// connect box and capsule to form the chassis

NxActor *box = CreateBox(NxVec3(0,carHeight,0), NxVec3(4.6f, 0.5f,


1.0f), 1.0f);
CreateRevoluteJoint(box, caps1, NxVec3(-4,carHeight,-3.5f),
NxVec3(0,0,1));
CreateRevoluteJoint(box, caps2, NxVec3( 4,carHeight,-3.5f),
NxVec3(0,0,1));
NxActor *ground = CreateBox(NxVec3(0,1,0), NxVec3(10.0f, 1.0f, 8.0f),
0.0f);
rot.rotZ(-0.1f);
ground->setGlobalOrientation(rot);
}
// set camera position and direction
gCameraPos.set(-17.0f, 20.0f, 20.0f);
gCameraForward.set(1.0f, -0.8f, -0.90f);
gCameraForward.normalize();
gCameraForward.normalize();
}

We fill out soft body descriptor with parameters for the soft body.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

3
We set the particle radius of the soft body.

softBodyDesc.particleRadius = 0.2f;

We set the stretching stiffness of the soft body.

softBodyDesc.stretchingStiffness = 1.0;

This defines how difficult it is to stretch the soft body.

Next we set the friction of the soft body.

softBodyDesc.friction = 1.0f;

This defines the damping of the velocities of soft body particles that are in contact.

We set the volumeStiffness of the soft cube.

softBodyDesc.volumeStiffness = 1.0;

The range for volumeStiffness is 0 to 1 and only has effect if


NX_SBF_VOLUME_CONSERVATION is set. This parameter defines how strongly
the soft body counteracts deviations from the rest volume.

Set the attachment response value as following code:


softBodyDesc.attachmentResponseCoefficient = 0.8f;

Attachment response coefficient defines a factor for the impulse transfer from the soft
body to attached rigid bodies. Only has an effect if the mode of the attachment is set to
NX_SOFTBODY_ATTACHMENT_TWOWAY.

Finally, we pass the soft body descriptor into our MySoftBody object constructor to build
the soft body. We also push our soft body on the application’s stack of soft bodies.

MySoftBody *softBody = new MySoftBody(gScene, softBodyDesc,


FindMediaFile(tetFileName,s), objMesh);

gSoftBodies.push_back (softBody);

After soft body is created, we can attach soft body wheel to rigid body:
softBody->getNxSoftBody()
->attachToCollidingShapes(NX_SOFTBODY_ATTACHMENT_TWOWAY);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

4
3 Conclusion
In this lesson, we learn a method that soft body is attached to colliding shapes with two-
way.

4 Related Classes, Functions, and Parameters


NxSoftBodyDesc
particleRadius
stretchingStiffness

NxSoftBodyAttachmentFlag
NX_SOFTBODY_ATTACHMENT_TWOWAY = (1<<0),
NX_SOFTBODY_ATTACHMENT_TEARABLE = (1<<1)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

5
Lesson 1106 – Tearing

1 Introduction
In this lesson, we will learn how to make the soft body to be torn.

2 Create tearing soft body


General steps of creating soft body are same as Lesson1101.

This lesson will create soft body tree in SetupTearingScene ().

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

1
void SetupTearingScene()
{
sprintf(gTitleString, "Tearing Example");

NxSoftBodyDesc softBodyDesc;
……

softBodyDesc.tearFactor = 1.3;

softBodyDesc.flags |= NX_SBF_TEARABLE;

…..
MySoftBody* softBody = new MySoftBody(gScene,
softBodyDesc,SOFTBODY_BOX,3,3,3,1);
……

CreateSphere(NxVec3(4.0f, 7, 3.4f),1,5.5);

……
}

In order to create tear-able soft body, we should apply flag NX_SBF_TEARABLE and
set its tear factor value.

The tear factor must be larger than 1. Make sure meshData.maxVertices and the
corresponding buffers in meshData are substantially larger (e.g. 2x) then the number of
original vertices since tearing will generate new vertices. When the buffer cannot hold the
new vertices anymore, tearing stops.

3 Conclusion
Now we hold a tear-able soft body. It will be torn when rigid body sphere drop down to
it.

4 Related Classes, Functions, and Parameters


NxSoftBodyDesc
tearFactor

NxSoftBodyFlag
NX_SBF_TEARABLE (1<<9)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com

2
Lesson 1201 – Character Controller

1 Introduction
PhysX has a character controller object available for you to use. Character controllers are
widely used to move player characters and A.I. characters over terrain and through game
level geometry. You will want to use a controller when you want precise control over an
object’s movement, while still letting it be affected by the game environment, i.e., sliding
over surfaces, along walls, etc.

The controller will fall onto and rest on a static surface and it will be pushed away from
static walls by default. You then use the Controller Hit Report to determine how the
controller affects dynamic objects and other controllers when it hits them.
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
Character controllers allows for precise, twitch control of your player and A.I. characters.
Since it allows such precise control over them, it also makes navigation of your A.I.
characters simpler.

2 Character Controller
We build a randomized terrain and build various obstacles for our character controller
walk on and jump on. The obstacles are built out of static actors.


void SetupScene()
{
// Create randomized terrain
gTerrain = CreateTerrain();

// Create stairs
NxU32 i;
for(i=0;i<20;i++)
{
NxActor* box = CreateBox(NxVec3(-10 - NxF32(i), -3.6 + NxF32(i)*0.5,
0), NxVec3(2,2,2), 0);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_NON_PUSHABLE);
}

// Create cube too high for autostep


NxActor* box1 = CreateBox(NxVec3(-10, -1-2, -10), NxVec3(2,2,2), 0);
SetActorCollisionGroup(box1, GROUP_COLLIDABLE_NON_PUSHABLE);

// Create rotated cube


NxActor* box2 = CreateBox(NxVec3(10, 4-2, -10), NxVec3(2,2,2), 0);
SetActorCollisionGroup(box2, GROUP_COLLIDABLE_NON_PUSHABLE);
NxMat33 M = box2->getGlobalOrientation();
M.rotX(0.5);
box2->setGlobalOrientation(M);

NxActor* box3 = CreateBox(NxVec3(10, 10-2, 10), NxVec3(2,2,2), 0);


SetActorCollisionGroup(box3, GROUP_COLLIDABLE_NON_PUSHABLE);

NxActor* box4 = CreateBox(NxVec3(-10, 5.5-2, 10), NxVec3(2,2,2), 0);


SetActorCollisionGroup(box4, GROUP_COLLIDABLE_NON_PUSHABLE);

NxActor* sphere1 = CreateSphere(NxVec3(0, 0-1, 10), 1, 0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
SetActorCollisionGroup(sphere1, GROUP_COLLIDABLE_NON_PUSHABLE);

NxActor* sphere2 = CreateSphere(NxVec3(0, 1-1, 5), 1, 0);


SetActorCollisionGroup(sphere2, GROUP_COLLIDABLE_NON_PUSHABLE);

NxActor* capsule1 = CreateCapsule(NxVec3(0, -1.5-2, -10), 3, 2, 0);


SetActorCollisionGroup(capsule1,
GROUP_COLLIDABLE_NON_PUSHABLE);

// Create walls
NxU32 numBoxes = 20;
NxReal halfBoxSize = 2;
NxReal wallLength = numBoxes*halfBoxSize*2;
for(i=0;i<numBoxes;i++)
{
NxActor* box = CreateBox(NxVec3((NxF32(i)/NxF32(numBoxes-
1))*wallLength - wallLength*0.5f, 0, wallLength*0.5f),
NxVec3(halfBoxSize,halfBoxSize,halfBoxSize), 0);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_NON_PUSHABLE);
}
for(i=0;i<numBoxes;i++)
{
NxActor* box = CreateBox(NxVec3((NxF32(i)/NxF32(numBoxes-
1))*wallLength - wallLength*0.5f, 0, -wallLength*0.5f),
NxVec3(halfBoxSize,halfBoxSize,halfBoxSize), 0);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_NON_PUSHABLE);
}
for(i=0;i<numBoxes;i++)
{
NxActor* box = CreateBox(NxVec3(-wallLength*0.5f, 0,
(NxF32(i)/NxF32(numBoxes-1))*wallLength - wallLength*0.5f),
NxVec3(halfBoxSize,halfBoxSize,halfBoxSize), 0);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_NON_PUSHABLE);
}
for(i=0;i<numBoxes;i++)
{
NxActor* box = CreateBox(NxVec3(wallLength*0.5f, 0,
(NxF32(i)/NxF32(numBoxes-1))*wallLength - wallLength*0.5f),
NxVec3(halfBoxSize,halfBoxSize,halfBoxSize), 0);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_NON_PUSHABLE);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
}

// Initialize controller manager


gManager = NxCreateControllerManager(gAllocator);
// Create character controllers
gBoxController = new MyCharacterController(gManager, gScene, NxVec3(0, 10,
0), NxVec3(0.5,1,0.5));
gCapsuleController = new MyCharacterController(gManager, gScene, NxVec3(0,
8, 0), 0.5, 1);
gSelectedController = gCapsuleController;

AddUserDataToActors(gScene);

// Set the heightfield to use light #1 for rendering


((ActorUserData*)(gTerrain->userData))->flags |=
UD_RENDER_USING_LIGHT1;
}

In MyCharacterController.h, we define various collision groups for the objects our


character controller will be colliding with:

enum CollisionGroup
{
GROUP_NON_COLLIDABLE,
GROUP_COLLIDABLE_NON_PUSHABLE,
GROUP_COLLIDABLE_PUSHABLE,
};

By default, objects are non-collidable, the character controller will pass right through
them. Some of the objects, like the static boxes and the terrain, will be marked as non-
pushable.

void SetActorCollisionGroup(NxActor* actor, NxCollisionGroup group)


{
NxShape*const* shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();
while (nShapes--)
{
shapes[nShapes]->setGroup(group);
}
}
...
// Terrain globals

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
#define TERRAIN_SIZE 33
#define TERRAIN_NB_VERTS TERRAIN_SIZE*TERRAIN_SIZE
#define TERRAIN_NB_FACES (TERRAIN_SIZE-1)*(TERRAIN_SIZE-1)*2
#define TERRAIN_OFFSET -20
#define TERRAIN_WIDTH 10
#define TERRAIN_CHAOS 150

TerrainData* gTerrainData = NULL;


NxActor* gTerrain = NULL;
...
NxActor* CreateTerrain()
{
gTerrainData = new TerrainData;
gTerrainData->init(TERRAIN_SIZE, TERRAIN_OFFSET, TERRAIN_WIDTH,
TERRAIN_CHAOS, true);

// Build physical model


NxTriangleMeshDesc terrainDesc;
terrainDesc.numVertices = TERRAIN_NB_VERTS;
terrainDesc.numTriangles = TERRAIN_NB_FACES;
terrainDesc.pointStrideBytes = sizeof(NxVec3);
terrainDesc.triangleStrideBytes = 3*sizeof(NxU32);
terrainDesc.points = gTerrainData->verts;
terrainDesc.triangles = gTerrainData->faces;
terrainDesc.flags = (gScene->getSimType() == NX_SIMULATION_HW) ?
NX_MF_HARDWARE_MESH :0;

NxTriangleMeshShapeDesc terrainShapeDesc;

NxInitCooking();
MemoryWriteBuffer writeBuffer;
bool status = NxCookTriangleMesh(terrainDesc, writeBuffer);
terrainShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(MemoryReadBuffer(writeBuffer.data));
NxCloseCooking();

terrainShapeDesc.group = GROUP_COLLIDABLE_NON_PUSHABLE;
if (gScene->getSimType() == NX_SIMULATION_HW)
terrainShapeDesc.meshPagingMode = NX_MESH_PAGING_AUTO;

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&terrainShapeDesc);
return gScene->createActor(actorDesc);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
You will be able to create various dynamic actors for your character controller to interact
with. Hit '1' to create a box, '2' to create a sphere, and '3' to create a capsule.

void KeyboardUpCallback(unsigned char key, int x, int y)


{
gKeys[key] = false;

switch(key)
{
...
case '1':
{
NxActor* box = CreateBox(NxVec3(0,20,0), NxVec3(1,1,1), 10);
box->setAngularDamping(0.5);
SetActorCollisionGroup(box,
GROUP_COLLIDABLE_PUSHABLE);
break;
}
case '2':
{
NxActor* sphere = CreateSphere(NxVec3(0,20,0), 1, 10);
sphere->setAngularDamping(0.5);
SetActorCollisionGroup(sphere,
GROUP_COLLIDABLE_PUSHABLE);
break;
}
case '3':
{
NxActor* capsule = CreateCapsule(NxVec3(0,20,0), 4, 1, 10);
capsule->setAngularDamping(0.5);
SetActorCollisionGroup(capsule,
GROUP_COLLIDABLE_PUSHABLE);
break;
}
...
}

These dynamic objects are put in the “collidable-pushable” group, so your character
controller will be able to push them around when it collides with them.

At the end of SetupScene(), we finally build two character controllers.

void SetupScene()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
6
{
...
// Initialize controller manager
gManager = NxCreateControllerManager(gAllocator);
// Create character controllers
gBoxController = new MyCharacterController(gManager, gScene, NxVec3(0, 10,
0), NxVec3(0.5,1,0.5));
gCapsuleController = new MyCharacterController(gManager, gScene, NxVec3(0,
8, 0), 0.5, 1);
...
}

MyCharacterController::MyCharacterController(NxControllerManager* manager,
NxScene *scene, NxVec3 pos, NxVec3 dim)
{
mManager = manager;

NxBoxControllerDesc desc;
desc.position.x = pos.x;
desc.position.y = pos.y;
desc.position.z = pos.z;
desc.skinWidth = 0.1f;
desc.slopeLimit = cosf(NxMath::degToRad(45.0f));
desc.stepOffset = 0.5f;
desc.upDirection = NX_Y;
desc.extents = dim;
desc.callback = &mReport;
mController = mManager->createController(scene, desc);
}

MyCharacterController::MyCharacterController(NxControllerManager* manager,
NxScene *scene, NxVec3 pos, NxReal radius, NxReal height)
{
mManager = manager;

NxCapsuleControllerDesc desc;
desc.position.x = pos.x;
desc.position.y = pos.y;
desc.position.z = pos.z;
desc.height = height;
desc.radius = radius;
desc.skinWidth = 0.1f;
desc.slopeLimit = cosf(NxMath::degToRad(45.0f));
desc.stepOffset = 0.5f;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
desc.upDirection = NX_Y;
desc.climbingMode = CLIMB_EASY;
desc.callback = &mReport;
mController = mManager->createController(scene, desc);
}

You can build either a capsule controller or a box controller. In this lesson we build one
capsule controller and one box controller. The skin width is an amount we grow the
controller by. This is a grace distance we allow objects to approach the capsule exterior
and the box exterior. The slope limit, set to 45 degrees, is the maximum uphill slope the
character controller can climb. The step offset is an amount we test above the character
for collision. It is used in a special case, when you move your character into a cave and it
can’t move any further because it is constrained from below and above in a “>” shape.
We specify their up direction to be the positive y-axis with the NX_Y flag. Finally the
Controller Hit Report is attached to the character controller descriptor.

3 NxCharacter Library
Much of the functionality of the character controller is executed in the NxCharacter
library. The library maintains a list of character controllers and builds the character
controller objects, assigning each an associated kinematic actor. It makes calls to the
Physics SDK to create the character's kinematic actor, find geometry it touches, and
places it accordingly. It uses a Controller Hit Report to define how objects the character
controller hits are affected, according to their collidable, pushable status as set on the
objects in the previous section.

4 The Controller Hit Report


Using the Controller Hit Report, we determine how our character controller interacts with
the physical objects in the scene as well as with other character controllers.

class MyControllerHitReport : public NxUserControllerHitReport


{
public:

virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit)


{
if(hit.shape)
{
NxCollisionGroup group = hit.shape->getGroup();
if(group==GROUP_COLLIDABLE_PUSHABLE)
{
NxActor& actor = hit.shape->getActor();
if(actor.isDynamic())
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
8
{
// We only allow horizontal pushes. Vertical pushes
// when we stand on dynamic objects creates
// useless stress on the solver. It would be possible
//to enable/disable vertical pushes on
// particular objects, if the gameplay requires it.
if(hit.dir.y==0.0f)
{
NxF32 coeff = actor.getMass() * hit.length *
10.0f;
actor.addForceAtLocalPos(hit.dir*coeff,
NxVec3(0,0,0), NX_IMPULSE);
}
}
}
}

return NX_ACTION_NONE;
}

virtual NxControllerAction onControllerHit(const NxControllersHit& hit)


{
return NX_ACTION_NONE;
}
};

MyCharacterController::MyCharacterController(NxControllerManager* manager,
NxScene *scene, NxVec3 pos, NxVec3 dim)
{

desc.callback = &mReport;

}

MyCharacterController::MyCharacterController(NxControllerManager* manager,
NxScene *scene, NxVec3 pos, NxReal radius, NxReal height)
{

desc.callback = &mReport;

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
With the onShapeHit() function in the Controller Hit Report, we can tell our character
controller to apply forces or otherwise influence to objects it hits. With
onControllerHit(), we can tell it to push other controllers on contact as well.

Our controller doesn't collide with objects marked as GROUP_NON_COLLIDABLE. It


only collides with objects marked as GROUP_COLLIDABLE_NON_PUSHABLE or
GROUP_COLLIDABLE_PUSHABLE. By definition, we only want it to push objects
marked as GROUP_COLLIDABLE_ PUSHABLE.

virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit)


{
if(hit.shape)
{
NxCollisionGroup group = hit.shape->getGroup();
if(group==GROUP_COLLIDABLE_PUSHABLE)
{

}

We then check to see if the hit actor is dynamic. If it is, we apply an impulse to it at its
center.

5 Updating the Character Controller


We call UpdateCharacter() in RunPhysics() to move our character controller through the
scene. In KeyboardCallback(), we get input so the user can push the character around
with the “ijkl” keys.

void KeyboardCallback(unsigned char key, int x, int y)


{
gKeys[key] = true;

switch (key)
{
// Move character control
case 'i': {gCharacterVec = NxVec3(0,0,1); bPushCharacter = true; break; }
case 'k': {gCharacterVec = NxVec3(0,0,-1); bPushCharacter = true; break; }
case 'j': {gCharacterVec = NxVec3(1,0,0); bPushCharacter = true; break; }
case 'l': {gCharacterVec = NxVec3(-1,0,0); bPushCharacter = true; break; }

default: { break; }
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
10
...
void UpdateCharacter(NxReal deltaTime)
{
NxVec3 disp = gDefaultGravity;

if (bPushCharacter)
{
NxVec3 horizontalDisp = gCharacterVec;
horizontalDisp.y = 0.0f;
horizontalDisp.normalize();
disp += horizontalDisp * gCharacterSpeed;
}

disp *= deltaTime;
NxF32 height = GetHeight(deltaTime); // compute height(Y) in jumping
if (height != 0.0f)
{
disp.y += height;
}

NxU32 collisionFlags;
gSelectedController->Move(disp, collisionFlags);
if (collisionFlags & NXCC_COLLISION_DOWN)
StopJump();

gManager->updateControllers();
}

void RunPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();

// Update the box character's position according to by testing its


// axis-aligned bounding box against a copy of the current scene.
UpdateCharacter(deltaTime);

// Run collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
gScene->fetchResults(NX_RIGID_BODY_FINISHED, true);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
11
First we figure out how the character is moving.

NxVec3 disp = gDefaultGravity;

if (bPushCharacter)
{
NxVec3 horizontalDisp = gCharacterVec;
horizontalDisp.y = 0.0f;
horizontalDisp.normalize();
disp += horizontalDisp * gCharacterSpeed;
}
disp *= deltaTime;
NxF32 height = GetHeight(deltaTime); // compute height(Y) in jumping
if (height != 0.0f)
{
disp.y += height;
}

NxU32 collisionFlags;
gSelectedController->Move(disp, collisionFlags);

If we are pushing the character, we build a target position, “disp”, which is the current
character's position plus gravity plus the character's speed times the character's movement
vector which is (0,0,1), (0,0,-1), (1,0,0), or (-1,0,0). NxController::move() does sweep
tests above the character, to the side the character is moving, and below the character,
depending on the character’s velocity.

6 Character Jumping
The character can jump via the GetHeight() function in UpdateCharacter().

NxF32 GetHeight(NxF32 elapsedTime)


{
if (!gJump) return 0.0f;
NxF32 Vt = gV0 + G*gJumpTime; // Vt = Vo + GT
gJumpTime += elapsedTime;
return Vt*elapsedTime + 1/2*G*elapsedTime*elapsedTime;
// S = VtT + 1/2GT^2
}

void UpdateCharacter(NxReal deltaTime)


{
...
NxF32 height = GetHeight(deltaTime); // compute height(Y) in jumping
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
12
...
}

The jump is executed by hitting the ‘u’ key.

void KeyboardUpCallback(unsigned char key, int x, int y)


{
gKeys[key] = false;

switch (key)
{
...
case 'u': { StartJump(15.0f); break; }
...
}
}

This calls StartJump() with an upward velocity of 15.

bool gJump = false;


NxF32 gV0;
NxF32 gJumpTime;

void StartJump(NxF32 v0)


{
if (gJump) return;
gJumpTime = 0.0f;
gV0 = v0;
gJump = true;
}

The jump finishes when the character controller returns a collision flag of
NXCC_COLLISION_DOWN, meaning the character has found geometry at the bottom
of its extents.

void StopJump()
{
gJump = false;
}

void UpdateCharacter(NxReal deltaTime)


{
...
NxU32 collisionFlags;
gSelectedController->Move(disp, collisionFlags);
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
13
if (collisionFlags & NXCC_COLLISION_DOWN)
StopJump();
...
}

7 Features of the Character Controller


We have seen some of the features of the character controller, including:

1. Standard FPS control: sliding behaviour as in Quake, etc, i.e. "1st order" control using
a displacement vector, not impulses or forces
2. Perfect stability: doesn't jitter in corners, etc.
3. No tunnelling: it should work fine with huge displacement vectors
4. Box and capsule controller: the two supported shapes for characters are box and
capsule. The box controller uses a kinematic box that contains the character as well as
a swept-box that casts downward to the floor to get the character's under-foot contact
information. The capsule controller uses a kinematic capsule that contains the
character as well as a swept- capsule that casts downward to the floor to get the
character's under-foot contact information.
5. Autostepping: the character automatically climbs small steps or stairs
6. Walkable/non-walkable parts: the motion is automatically stopped on non-walkable
parts of the level
7. Character-objects interactions: a.k.a. pushing => the character can push other dynamic
objects
8. Different hit callbacks: trigger different game effects when touching a shape or
another character
9. Runtime shape resizing

8 Conclusion
Character controllers allow a greater level of direct control over your character than
dynamic objects, while still allowing the game environment to influence them, as
opposed to kinematic objects which are completely immune to the physical environment.

We have seen how to create a character controller, move it, and make it jump.

9 Related Classes, Functions, and Parameters


NxControllerManager
createController()

NxControllerDesc
position

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
14
upDirection
slopeLimit
skinWidth
stepOffset
callback
interactionFlag
userData

NxBoxControllerDesc
extents

NxBoxController
getExtents()
setExtents()

NxCapsuleControllerDesc
radius
height

NxCapsuleController
getRadius()
setRadius()
getHeight()
setHeight()

NxController
move()
setPosition()
getPosition()

NxControllerType
NX_CONTROLLER_BOX
NX_CONTROLLER_CAPSULE

NxControllerFlag
NXCC_COLLISION_SIDES
NXCC_COLLISION_UP
NXCC_COLLISION_DOWN

NxControllerAction
NX_ACTION_NONE
NX_ACTION_PUSH

NxControllerShapeHit

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
15
controller
shape
worldPos
worldNormal
id
dir
length

NxControllersHit
controller
other

NxUserControllerHitReport
onShapeHit()
onControllerHit()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
16
Quick Start Integration Guide
Introduction
This is a Quick Start Guide to integrating PhysX into your game. It contains a basic
PhysX shell that highlights the key components to a quick-start PhysX integration. It will
help you avoid some of the common initial developer pitfalls with integration.

1. Basic PhysX Shell


2. Make your Physics Loop Asynchronous
3. Use a Fixed Timestep
4. Turn on Debug Rendering
5. Put your User Callbacks and Inputs in ProcessInputs()
6. Adjust your Physics Parameters to Scale
7. Render your Physics Objects

1 Basic PhysX Shell


The following is a basic PhysX shell with an asynchronous physics loop, fixed timestep,
debug rendering turned on, callbacks in ProcessInputs() after NxScene::fetchResults()
and before NxScene::gScene->simulate(), physics parameters adjusted to scale, and
objects drawn in sync with their physical data. We will analyze this basic loop in the
next sections to explain where each item of interest is and how it works. For reference,
this shell is built on an OpenGL implementation.

NxPhysicsSDK* gPhysicsSDK = NULL;


NxScene* gScene = NULL;

void RenderCallback()
{

// Update physics objects
if (gScene)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

// Render physics objects


SetupCamera();
RenderActors();

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
1
void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}

void ProcessInputs()
{
// Put user callbacks here

// Show debug wireframes


if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

void StartPhysics()
{
// Update the time step
NxReal deltaTime = 1.0f/60.0f;

// Start collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
}

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

// Set scale dependent parameters


NxReal scale = 1.0f; // scale is meters per PhysX units

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05*(1/scale));
gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.15*0.15*(1/scale)*(1/scale));
gPhysicsSDK->setParameter(NX_BOUNCE_THRESHOLD, -2*(1/ scale));
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 0.5*(1/ scale));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
// Initialize the physics loop
if (gScene) StartPhysics();
}

void DebugRenderer::renderData(const NxDebugRenderable& data) const


{
...
}

2 Make your Physics Loop Asynchronous


To take full advantage of multi-threading, make your physics loop asynchronous. Set up
your physics loop like this in your render callback.

void RenderCallback()
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

This ensures the physics simulation will be running in parallel with the rest of your game
code.

void ProcessInputs()
{
// Put user callbacks here

// Show debug wireframes


if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

3 Use a Fixed Time Step


When starting out, make sure to use a fixed time step for your physics loop. You can
synchronize your physics update with your graphics update later, this will get you up and
running right away.

void StartPhysics()

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
3
{
// Update the time step
NxReal deltaTime = 1.0f/60.0f;

// Start collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
}

4 Turn on Debug Rendering


Set up debug rendering immediately so you can visualize the PhysX objects you create.
You will need to create a debug renderer object:

class DebugRenderer
{
public:
void renderData(const NxDebugRenderable& data) const;
};

Attach add some simple debug visualization flags to the SDK, like collision shapes and
actor axes. You will need to set the visualization scale to something non-zero (like 1) to
turn the visualization lines on.

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Set the debug visualization parameters
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

}

Put the following lines of code in your ProcessInputs() function. This will help you to
immediately visualize your objects:

void ProcessInputs()
{
// Put user callbacks and inputs here

// Show debug wireframes


if (bDebugWireframeMode)
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
4
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

5 Put your User Callbacks and Inputs in ProcessInputs()


The debug renderer object needs to be passed to the SDK in the ProcessInputs() function
in between GetPhysicsResults() (NxScene::fetchResults()) and StartPhysics()
(NxScene::simulate()).

Any other user callbacks or user inputs to the scene you need to do here, as input to the
scene is locked out in the rest of the code.

void RenderCallback()
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

void ProcessInputs()
{
// Put user callbacks and inputs here

// Show debug wireframes


if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

6 Adjust your Physics Parameters to Scale


One of the most common problems encountered by developers is the issue of scale.
There are five parameters PhysX uses that have absolute scale: min separation for
penalty, sleep linear velocity, bounce threshold, and visualization scale.

You will want to adjust these so they make sense within the scale of your game world. If
your object scale is scale meters / PhysX unit, you will want to set these like this

NxReal scale = 1.0f; // scale is meters per PhysX units

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05*(1/ scale));

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
5
gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.15*0.15*(1/ scale)*(1/ scale));
gPhysicsSDK->setParameter(NX_BOUNCE_THRESHOLD, -2*(1/ scale));
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 0.5*(1/ scale));

7 Render your Physics Objects


You should now be able to see your objects via debug rendering. Now you want to use
your own rendering routines to draw them. The following SetupCamera() and
RenderActors() functions cycles through PhysX actors and renders their shapes using
OpenGL. This example only renders sphere shapes.

void RenderCallback()
{

// Render your physics objects
SetupCamera();
RenderActors();
}

float gCameraAspectRatio = 1.0f;


NxVec3 gCameraPos(0,5,-15);
NxVec3 gCameraForward(0,0,1);

void SetupCamera()
{
// Setup camera
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, gCameraAspectRatio, 1.0f, 10000.0f);
gluLookAt(gCameraPos.x,gCameraPos.y,gCameraPos.z,gCameraPos.x +
gCameraForward.x, gCameraPos.y + gCameraForward.y, gCameraPos.z +
gCameraForward.z, 0.0f, 1.0f, 0.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void RenderActors()
{
// Render all the actors in the scene
int nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while (nbActors--)
{
NxActor* actor = *actors++;
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
DrawActor(actor);
}
}

void DrawActor(NxActor* actor)


{
NxShape** shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();
nShapes = actor->getNbShapes();
while (nShapes--)
{
DrawShape(shapes[nShapes]);
}
}

void DrawShape(NxShape* shape)


{
switch(shape->getType())
{

case NX_SHAPE_SPHERE:
DrawSphere(shape);
break;

}
}

int dispListSphere;

void InitShapeDisplayLists()
{
// SPHERE DISPLAY LIST
dispListSphere = glGenLists(1);
glNewList(dispListSphere, GL_COMPILE);
GLUquadricObj * quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GLU_FILL);
gluQuadricNormals (quadObj, GLU_SMOOTH);
gluQuadricOrientation(quadObj,GLU_OUTSIDE);
gluSphere (quadObj, 1.0f, 9, 7); //unit sphere
glEndList();
gluDeleteQuadric(quadObj);
}

void DrawSphere(NxShape *sphere)


{
NxMat34 pose = sphere->getGlobalPose();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
glPushMatrix();
SetupGLMatrix(pose.t, pose.M);
NxReal r = sphere->isSphere()->getRadius();
glScaled(r,r,r);
glCallList(dispListSphere);
glPopMatrix();
}

void SetupGLMatrix(const NxVec3& pos, const NxMat33& orient)


{
float glmat[16]; //4x4 column major matrix for OpenGL.
orient.getColumnMajorStride4(&(glmat[0]));
pos.get(&(glmat[12]));

//clear the elements we don't need:


glmat[3] = glmat[7] = glmat[11] = 0.0f;
glmat[15] = 1.0f;

glMultMatrixf(&(glmat[0]));
}

The key code is the setupGLMatrix() call in DrawSphere where we build an OpenGL
transformation matrix from a PhysX shape transformation matrix. This turns a PhysX
shape positioning matrix into a graphical display matrix for your object. Check this
against the debug rendering of your objects to make sure your object rendering is correct.

8 Conclusion
This has been a Quick-Start guide to PhysX.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
PhysX SDK Integration Guide
Introduction
This is the PhysX integration guide. It is designed to walk you through integrating the
PhysX SDK into your game. Common issues that arise during integration are addressed
at each step.

1. Familiarize Yourself with the SDK


2. Basic SDK Components
3. Level Geometry
4. Terrain Geometry
5. World Objects
6. Drawing your World Objects
7. Physics Parameters and Scale
8. Object Materials
9. Joints
10. Springs
11. Asynchronous API
12. Debug Rendering
13. Moving your Objects
14. Character Controller
15. Vehicle Controller
16. Triggers
17. Raycasts
18. Contacts
19. Particles and Special Effects
20. Exporters, Importers, and PML files

1 Familiarize Yourself with the SDK


Read the first lesson in the PhysX Training Programs, “Lesson 101: Box on a Plane”.
From it, you will learn all the necessary code to build a complete PhysX application.
Familiarize yourself with the code used to do the following:

1. Initializing the SDK and Creating the Scene


2. Setting Parameters for the SDK
3. Adding Materials to the SDK
4. Creating the Scene
5. Creating the Actors in the Scene
6. The Physics Loop
7. Drawing the Actors
8. The Debug Renderer
9. Resetting the Scene
10. Shutting Down the SDK
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
1
Peruse this lesson until you have gotten a feel for how a PhysX application is structured.
Think about where in your game you would need to put the SDK initialization, main
loop, and shut-down. Also think about how and when you want to construct your actors
and joints within the context of your game.

Read over the “PhysX Math Primer” document to learn how basic 3D math is done in
PhysX. Also take a look at the “User Defined Classes” document to learn about the user
callback objects that PhysX employs.

2 Basic SDK Components


All physics SDKs are built upon the few fundamental objects: the physics SDK itself, a
physics environment or scene, parameters for the scene, rigid bodies, materials that
define the surface properties of the rigid bodies, collision shapes the rigid bodies are built
from, triangle meshes that certain collision shapes are constructed from, constraints that
connect rigid bodies at a point, and springs that connect rigid bodies via forces over a
distance. The final component of the SDK is a set callback objects the user can employ
to query the scene for information. This includes debug wireframes, contact information
between designated contact pairs, joint breakage, and the results of raycasts through the
scene.

If you have never used a physics in your game before, this will brand new to you. If you
have implemented your own physics or collision systems, look at how these objects
correspond to objects you have implemented. If you have used a physics SDK before or
are currently using one for your game, make note of what PhysX objects and methods
correspond to the objects and methods of your current SDK. For each major component
of the SDK, look for its PhysX equivalent. Examine each object and note the similarities
and differences in their implementation and use.

1. Physics SDK
class NxPhysicsSDK Physics SDK object
NxPhysicsSDK::NxCreatePhysicsSDK() Creates the physics SDK

2. Scenes
class NxScene Physics environment
NxPhysicsSDK:createScene() Creates the physics environment

3. Scene Parameters
enum NxParameter SDK global parameter
NxPhysicsSDK::setParameter Sets a parameter to a non-default value

4. Rigid Bodies
class NxActor Rigid body
NxScene::createActor() Creates a rigid body

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
2
5. Materials
class NxMaterial Rigid body surface and interior material
NxPhysicsSDK::setMaterialAtIndex() Registers a material with the SDK

6. Collision Shapes
class NxShape Collision shape in a rigid body
NxActorDesc::shapes List of shapes that compose the rigid body

7. Triangle Meshes
class NxTriangleMesh Triangle mesh used to create a triangle mesh
collision shape
NxPhysicsSDK::createTriangleMesh() Create a triangle mesh from a point cloud or
a user-supplied convex hull

8. Constraints
class NxJoint Constraint connecting two actors at a point
that allows different levels of rotational and
translational freedom between the actors
NxScene::createJoint() Create a joint given a joint descriptor and
two actors
9. Spring and Damper Effectors
class NxSpringAndDamperEffector Spring connecting two actors,
attached to points on each actor
NxScene::createSpringAndDamperEffector() Create a spring given a spring
descriptor and two actors

10. User Defined Classes


These are user callback classes the user fleshes out the methods for and attaches to the
SDK to provide user-written functionality called from within the solver. They are
outlined and explained in the “User Defined Classes” that accompanies this guide.

These are the major objects of the SDK. In the following sections we will show you how
to use these objects to build the physical components of your game.

3 Level Geometry
For BSP levels, you will want to build your level geometry into a static actor or group of
static actors. Sample code to do this is in “Lesson 603: Game Level”.

// Physics SDK globals


NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;

// Actor globals
NxActor* gameLevel = NULL;
...
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
3
NxActor* CreateLevelMesh()
{
gLevelMesh.convertLevel();

// Build physical model


NxTriangleMeshDesc levelDesc;
levelDesc.numVertices = g_Level.m_numOfVerts;
levelDesc.numTriangles = gLevelMesh.nbTriangles;
levelDesc.pointStrideBytes = sizeof(tBSPVertex);
levelDesc.triangleStrideBytes = 3*sizeof(NxU32);
levelDesc.points = (const
NxPoint*)&(g_Level.m_pVerts[0].vPosition);
levelDesc.triangles = gLevelMesh.triangles;
levelDesc.flags = 0;

NxTriangleMeshShapeDesc levelShapeDesc;
NxInitCooking();
if (0)
{
// Cooking from file
bool status = NxCookTriangleMesh(levelDesc, UserStream("c:\\tmp.bin", false));
levelShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(UserStream("c:\\tmp.bin", true));
}
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookTriangleMesh(levelDesc, buf);
levelShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(MemoryReadBuffer(buf.data));
}

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&levelShapeDesc);
NxActor* actor = gScene->createActor(actorDesc);
actor->userData = (void*)1;

return actor;
}

void InitNx()
{
...
// Initialize BSP Level Actor
LoadBSP();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
4
gameLevel = CreateLevelMesh();
...
}

In this example we load a game level BSP. We create a triangle mesh descriptor and
attach the BSP mesh to it. We then create a triangle mesh shape descriptor and pass our
triangle mesh descriptor into NxPhysicsSDK::createTriangleMesh() to build the mesh
data for the shape. This mesh shape gets pushed onto our actor. Since we want the actor
to be static, we don’t give the actor a rigid body. This means the actor will be baked into
the scene and will not move.

4 Terrain Geometry
As with a BSP, you want to build your terrain geometry into a static actor or group of
static actors. Sample code to do this is in “Lesson 601: Heightfields”.

// Actor globals
...
NxActor* heightfield = NULL;

NxVec3* gHeightfieldVerts = NULL;


NxVec3* gHeightfieldNormals = NULL;
NxU32* gHeightfieldFaces = NULL;

NxActor* CreateHeightfield()
{

// Build physical model
NxTriangleMeshDesc heightfieldDesc;
heightfieldDesc.numVertices = HEIGHTFIELD_NB_VERTS;
heightfieldDesc.numTriangles = HEIGHTFIELD_NB_FACES;
heightfieldDesc.pointStrideBytes = sizeof(NxVec3);
heightfieldDesc.triangleStrideBytes = 3*sizeof(NxU32);
heightfieldDesc.points = gHeightfieldVerts;
heightfieldDesc.triangles = gHeightfieldFaces;

heightfieldDesc.flags = 0;

heightfieldDesc.heightFieldVerticalAxis = NX_Y;
heightfieldDesc.heightFieldVerticalExtent = -1000;

NxTriangleMeshShapeDesc heightfieldShapeDesc;

NxInitCooking();
if (0)
{
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
5
// Cooking from file
bool status = NxCookTriangleMesh(heightfieldDesc, UserStream("c:\\tmp.bin",
false));
heightfieldShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(UserStream("c:\\tmp.bin", true));
}
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookTriangleMesh(heightfieldDesc, buf);
heightfieldShapeDesc.meshData = gPhysicsSDK-
>createTriangleMesh(MemoryReadBuffer(buf.data));
}

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&heightfieldShapeDesc);
NxActor* actor = gScene->createActor(actorDesc);

actor->userData = (void*)-1;

return actor;
}

void InitNx()
{

heightfield = CreateHeightfield();
}

So CreateHeightfield() is much the same as CreateLevel(), only we attach a triangle mesh


that we created from a heightmap rather than the triangle mesh from the game level BSP
file. The other difference is we need to set an “up” axis for the terrain.

heightfieldDesc.heightFieldVerticalAxis = NX_Y;
heightfieldDesc.heightFieldVerticalExtent = -1000;

For terrain, you need to give your triangle shape descriptor a vertical axis (NX_Y, from
above, is the y-axis) and a vertical extent (-1000, this is the depth of the heightfield).
This defines the shape of the heightfield as well as makes sure objects collide with the
heightfield realistically.

5 World Objects
Now that you have your level geometry in place, you will want to populate your game
world with objects that aren’t part of the main static scene. We call all physical objects in
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
6
PhysX “actors” and use the words “actor” and “object” interchangeably. These actors
can be crates, doors, trees, rocks, any physical object that resides in your game world.

Most actors will be dynamic. They are part of the physical scene and will move and
collide with the world and other objects realistically. In general, you want to keep your
dynamic object densities within a dynamic range of 10, that is, if your lowest density
object has a density of 5, your highest density object should have a density of around 50.

Actors that you want total control over, you will want to make kinematic. These actors
will not react to the world and other actors. They will move precisely where you want
them to go, pushing all dynamic objects aside as if they had infinite mass. Kinematic
actors should be used for objects you consider to be of such high density, they will push
all other objects aside. They are good for moving objects that are effectively immune to
the physical scene, like heavy moving platforms or large moving blast doors or gates.
Kinematic objects can be turned into dynamic objects and vice-versa. If the objects are
stationary and will never move, you want to make them static. Static objects cannot be
made dynamic or kinematic.

Most of your objects will consist of a single shape. There are five shape types: plane,
box, sphere, capsule, and triangle mesh. The plane shape type is primarily used to
construct a ground plane at some level in your game but is otherwise rarely needed. The
plane, box, sphere, and capsule shape are geometic shapes that use their geometry to
determine points of contact and collision normals. The triangle mesh shape uses the
triangles it is constructed from to determine points of contact and collision normals.
Some objects will be built from two or more shapes. Something like a table needs to be
built from five shapes: the tabletop and the four legs. With the tools we provide, you will
be able to build the physical representation of your object in Max or Maya, then save it
out to a PML file that your game can read in.

As an example, here’s how to make a box-shape actor:

NxActor* box = NULL;



NxActor* CreateBox(const NxVec3& pos, const NxVec3& boxDim, const NxReal
density)
{
// Add a single-shape actor to the scene
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// The actor has one shape, a box


NxBoxShapeDesc boxDesc;
boxDesc.dimensions.set(boxDim.x,boxDim.y,boxDim.z);
boxDesc.localPose.t = NxVec3(0,boxDim.y,0);
actorDesc.shapes.pushBack(&boxDesc);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
7
if (density)
{
actorDesc.body = &bodyDesc;
actorDesc.density = density;
}
else
{
actorDesc.body = NULL;
}
actorDesc.globalPose.t = pos;
return gScene->createActor(actorDesc);
}

void InitNx()
{

box = CreateBox(NxVec3(5,0,5), NxVec3(0.5,1,0.5), 20);
}

Note we put the box shape at a local position of (0,boxDim.y,0) within the actor. That
way, when we give the actor its global position, the global position will be located at the
bottom of the box. It is a good idea in general to place the global position of the actor
where the actor rests on the ground naturally so you can easily place them in your levels.

By giving the object a positive density, we indicate we want the object to be dynamic and
we give the actor a body descriptor. Once the actor is created, its center of mass and
inertia tensor are calculated from the collision shapes and density that define it. Instead
of a density, you can supply a total mass for your actor as well, or build your own inertia
tensor if you wish to create an object with non-uniform density. If we set the actor’s
body descriptor to NULL with a zero or negative denstity, the box will be static. Once
the dynamic box has been created, we can make it kinematic by calling the following on
the box actor:

box->raiseBodyFlag(NX_BF_KINEMATIC);

Set it back to dynamic by clearing this flag.

box->clearBodyFlag(NX_BF_KINEMATIC);

When choosing shapes for your objects, the general rule is to build your objects from the
simplest shapes possible. Whenever you can, use the basic geometric shapes: box,
sphere, and capsule. If an object is more complicated than one shape alone, add more
shapes to better define it. If your object doesn’t conform to one of these shapes or a
moderate combination of the shapes, use a triangle mesh. With triangle meshes,
whenever possible you want to use convex meshes or a union of convex meshes. The
easiest way to create a convex mesh is to build a point cloud and let PhysX build a

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
8
convex hull around it for you. The following is from “Lesson 104: Convex Shapes and
Anisotropic Friction”.

NxActor* CreateConvexObjectComputeHull(const NxVec3& pos, const NxVec3&


boxDim, const NxReal density)
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// Compute hull
NxVec3 verts[8] =
{
NxVec3(-boxDim.x,-boxDim.y,-boxDim.z),
NxVec3(boxDim.x,-boxDim.y,-boxDim.z),
NxVec3(-boxDim.x,boxDim.y,-boxDim.z),
NxVec3(boxDim.x,boxDim.y,-boxDim.z),
NxVec3(-boxDim.x,-boxDim.y,boxDim.z),
NxVec3(boxDim.x,-boxDim.y,boxDim.z),
NxVec3(-boxDim.x,boxDim.y,boxDim.z),
NxVec3(boxDim.x,boxDim.y,boxDim.z)
};

// Create descriptor for convex mesh


NxConvexMeshDesc convexDesc;
convexDesc.numVertices = 8;
convexDesc.pointStrideBytes = sizeof(NxVec3);
convexDesc.points = verts;
convexDesc.flags = NX_CF_COMPUTE_CONVEX;

NxConvexShapeDesc convexShapeDesc;
convexShapeDesc.localPose.t = NxVec3(0,boxDim.y,0);

NxInitCooking();
if (0)
{
// Cooking from file
#ifndef LINUX
bool status = NxCookConvexMesh(convexDesc, UserStream("c:\\tmp.bin", false));
convexShapeDesc.meshData = gPhysicsSDK-
>createConvexMesh(UserStream("c:\\tmp.bin", true));
#else
printf("Linux does not behave well with UserStreams, use MemorBuffers
instead\n");
exit(1);
#endif
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
9
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookConvexMesh(convexDesc, buf);
convexShapeDesc.meshData = gPhysicsSDK-
>createConvexMesh(MemoryReadBuffer(buf.data));
}

if (convexShapeDesc.meshData)
{
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&convexShapeDesc);
if (density)
{
actorDesc.body = &bodyDesc;
actorDesc.density = density;
}
else
{
actorDesc.body = NULL;
}
actorDesc.globalPose.t = pos;
return gScene->createActor(actorDesc);
// gPhysicsSDK->releaseTriangleMesh(*convexShapeDesc.meshData);
}

return NULL;
}

We create our frustum-shaped pyramid by giving the triangle mesh descriptor eight
points, four for the larger base of the pyramid and four for the top. We then pass our
descriptor into NxPhysicsSDK::createConvexMesh(), flagging it with
NX_CF_COMPUTE_CONVEX to let the the mesh creation function know we want it to
compute a convex hull for us.

You can also provide your own convex hull. The following code is from “Lesson 213:
Convex Object Creation”.

NxActor* CreateConvexObjectSupplyHull(const NxVec3& pos, const NxVec3&


boxDim, const NxReal density)
{
NxActorDesc actorDesc;
NxBodyDesc bodyDesc;

// Supply hull

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
10
NxVec3 verts[8] =
{
NxVec3(-boxDim.x,-boxDim.y,-boxDim.z),
NxVec3(boxDim.x,-boxDim.y,-boxDim.z),
NxVec3(-boxDim.x,boxDim.y,-boxDim.z),
NxVec3(boxDim.x,boxDim.y,-boxDim.z),
NxVec3(-boxDim.x,-boxDim.y,boxDim.z),
NxVec3(boxDim.x,-boxDim.y,boxDim.z),
NxVec3(-boxDim.x,boxDim.y,boxDim.z),
NxVec3(boxDim.x,boxDim.y,boxDim.z)
};

NxU32 indices[12*3] =
{
1,2,3,
0,2,1,
5,7,6,
4,5,6,
5,4,1,
1,4,0,
1,3,5,
3,7,5,
3,2,7,
2,6,7,
2,0,6,
4,6,0
};

// Create descriptor for triangle mesh


NxConvexMeshDesc convexDesc;
convexDesc.numVertices = 8;
convexDesc.pointStrideBytes = sizeof(NxVec3);
convexDesc.points = verts;
convexDesc.numTriangles = 12;
convexDesc.triangles = indices;
convexDesc.triangleStrideBytes = 3 * sizeof(NxU32);
convexDesc.flags = 0;

NxConvexShapeDesc convexShapeDesc;
convexShapeDesc.localPose.t = NxVec3(0,boxDim.y,0);

NxInitCooking();
if (0)
{
// Cooking from file
#ifndef LINUX

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
11
bool status = NxCookConvexMesh(convexDesc, UserStream("c:\\tmp.bin", false));
convexShapeDesc.meshData = gPhysicsSDK-
>createConvexMesh(UserStream("c:\\tmp.bin", true));
#else
printf("Linux does not behave well with UserStreams, use MemorBuffers
instead\n");
exit(1);
#endif
}
else
{
// Cooking from memory
MemoryWriteBuffer buf;
bool status = NxCookConvexMesh(convexDesc, buf);
convexShapeDesc.meshData = gPhysicsSDK-
>createConvexMesh(MemoryReadBuffer(buf.data));
}

if (convexShapeDesc.meshData)
{
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&convexShapeDesc);
if (density)
{
actorDesc.body = &bodyDesc;
actorDesc.density = density;
}
else
{
actorDesc.body = NULL;
}
actorDesc.globalPose.t = pos;
return gScene->createActor(actorDesc);
// gPhysicsSDK->releaseTriangleMesh(*convexShapeDesc.meshData);
}

return NULL;
}

Providing your own convex hull means providing the triangle mesh descriptor with the
vertices of your object as well as the indices of these vertices to show how your triangles
are constructed. Ordering of the indices is important as PhysX uses the winding to
determine the triangle’s facing. Indices of (v0,v1,v2) mean the triangle normal is

(v1 – v0) × (v2 – v0)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
12
This normal should face the exterior of your object. If you store your triangles with an
opposite winding, you can set the following flag on your triangle mesh descriptor.

convexDesc.flags |= NX_MF_FLIPNORMALS;

You can build static objects from convex or concave triangle meshes. If you want to
build a dynamic object from a concave triangle mesh, you will want to decompose the
concave triangle mesh into a group of convex triangle meshes. You can then add each
convex triangle mesh shape to the actor to build your concave triangle mesh dynamic
actor.

It is usually a good idea to call isValid() on your triangle mesh descriptor, especially if
you are supplying your own hull.

if (!convexDesc.isValid()) return NULL; // or error message

Some notes about meshes:

• As mentioned above, be sure that you define face normals as facing in the
direction you intend. Collision detection will only work correctly between shapes
approaching the mesh from the “outside”, i.e., from the direction in which the
face normals point.
• Do not duplicate identical vertices. If you have two triangles sharing a vertex,
this vertex should only occur once in the vertex list, and both triangles should
index it in the list. If you create two copies of the vertex, the collision detection
code won’t know that it is actually the same vertex, which leads to decreased
performance and unreliable results.
• Also avoid t-joints and non-manifold edges for the same reason. A t-joint is a
vertex of one triangle that is placed right on top of an edge of another triangle, but
this second triangle is not split into two triangles at the vertex, as it should. A non-
manifold edge is an edge (a pair of vertices) that is referenced by more than two
triangles.
• To precompute the inertia tensor, mass, and center of mass of a mesh, you can use
the NxComputeVolumeIntegrals() function in the Foundation SDK. This is the
same function that also gets used internally if you do not provide an inertia tensor.
Note that the inertia tensor computation uses triangles’ winding to tell which side
of a triangle is ‘solid’. For this reason, improper winding may lead to a negative
mass.
• It is possible to assign a different material to each triangle in a mesh. Object
materials are explained in Section 8 below.

6 Drawing your World Objects


You will need to make sure you’re drawing your actors in their proper position and
orientation. To draw an actor, cycle through its shapes and have a routine to draw each

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
13
shape. For transformation matrices, PhysX is column major, so is OpenGL. To convert a
PhysX shape global transformation to an OpenGL transform, use the following code.
This is in and around the setupGLMatrix() code in the tutorials:

// Get an OpenGL transform (float[16]) from a PhysX shape’s global pose


// (NxMat34)
NxShape* shape = NULL;

NxMat34 pose; // 3x4 column major PhysX matrix
shape->getGlobalPose(pose);

NxMat33 orient = pose.M;


NxVec3 pos = pose.t;

float glmat[16]; // 4x4 column major OpenGL matrix


orient.getColumnMajorStride4(&(glmat[0]));
pos.get(&(glmat[12]));

// clear the elements we don't need:


glmat[3] = glmat[7] = glmat[11] = 0.0f;
glmat[15] = 1.0f;

Direct3D is binary compatible with OpenGL so you can use the same method to get a
D3D transformation matrix.

Look at DrawShapes.cpp in the tutorials to get an example of how to draw your shapes.
You can use these methods to draw a shape that closely matches the physical
representation of the shape, or you may want to draw a different representation of your
shape at the orientation and position of the PhysX shape if the graphical representation of
the shape differs substantially from the physical representation of the shape, e.g., the
graphical representation is more detailed than the physical representation.

Use the debug renderer to check the positions and orientations of the collision shapes
within your actor against your graphical representations of them.

void InitNx()
{

// Set the debug visualization parameters
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);

}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
14
7 Physics Parameters and Scale
Once you have your level geometry and world objects in place, you may want to adjust
any scale-dependent SDK parameters. PhysX parameters are set up by default assuming
1 unit = 1 meter. If your units are substantially larger or smaller than 1 meter, you will
want to adjust these parameters to scale. The main scale-dependent parameters are
gravity, min separation for penalty, sleep linear velocity, bounce threshold, and
visualization scale. These parameters are set in InitNx() in the tutorials, right after the
SDK and before the scene is initialized.

// Physics SDK globals


NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxVec3 gDefaultGravity(0,-9.8,0);

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Set the physics parameters


gPhysicsSDK->setParameter(NX_MIN_SEPARATION_FOR_PENALTY, -0.05);
gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.15*0.15);
gPhysicsSDK->setParameter(NX_BOUNCE_TRESHOLD, -2);

// Set the debug visualization parameters


gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 0.5);

// Create the scene
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;
gScene = gPhysicsSDK->createScene(sceneDesc);

}

1. Gravity
sceneDesc.gravity = gDefaultGravity;

So by default, gravity is -9.8 m/sec^2. If your scale is s meters/unit, you should divide
this value by s, so

gDefaultGravity = (1/s)*NxVec3(0,-9.8,0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
15
2. Skin Width
PhysX is an iterative solver, so it allows object surfaces to close to within a certain
distance of each other before applying penalty forces. This is the NX_SKIN_WIDTH
parameter you set when initializing PhysX.

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05);

The default skin width is 0.05, so about 5 cm. It means objects are allowed to penetrate
each other by 5 cm.

Again, depending on scale s meters/unit you want to adjust this parameter to something
like this:

gPhysicsSDK->setParameter(NX_SKIN_WIDTH, -0.05*(1/s));

3. Sleep Linear Velocity


PhysX rigid bodies are put to sleep when they fall below a certain linear velocity
threshold, i.e., PhysX considers them non-moving for all intents and purposes so stops
their motion and pays no attention to them until they are awakened by a sudden force,
impact, etc.

gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.15*0.15);

The default setting is velocity squared, so if your scale is s meters/unit, set your default
sleep parameter to something like this:

gPhysicsSDK->setParameter(NX_DEFAULT_SLEEP_LIN_VEL_SQUARED,
0.15*0.15*(1/s)*(1/s));

Note: There is no need to adjust the sleep angular velocity to scale as this velocity is in
radians and hence scale independent.

4. Bounce Threshold
PhysX rigid bodies will not bounce if moving under a certain speed. For all intents and
purposes the objects are considered to be closing with each other so slowly, the SDK
considers they will get no energy from the collision and stops them both.

gPhysicsSDK->setParameter(NX_BOUNCE_TRESHOLD, -2);

-2 is a closing speed, so with s meters/unit, make this parameter:

gPhysicsSDK->setParameter(NX_BOUNCE_TRESHOLD, -2*(1/s));

5. Visualization Scale

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
16
Not important to the simulation itself, but important for debugging purposes. The
visualization scale is the length of the debug wireframe normals (and other vectors)
displayed by the debug renderer.

gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 0.5);

If you want wireframe normals at 0.5 units where the scale is s meters/unit.

gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 0.5*(1/s));

These are the five main scale-dependent parameters you need to consider when using a
scale that varies considerably from 1 meter per unit. Remember to scale these when you
make adjustments to these parameters on a per-object basis as well (this is possible with
sleep linear velocity and bounce threshold).

8 Object Materials
Once you have your level geometry and world objects in place, you will want to adjust
the materials on your objects to make sure they slide off each other and collide with each
other in the manner you want. Actor materials are discussed in “Lesson 108: Materials”.

void InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK) return;

// Create the default material
NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);
defaultMaterial->setStaticFriction(0.5);
defaultMaterial->setDynamicFriction(0.5);

}

Materials have three properties: restitution, static friction, and dynamic friction.
Restitution defines the “bounciness” of the object, how much energy the object retains
after a collision. A restitution of 1 indicates perfect elasticity, the object will lose no
energy in a collision. A restitution of 0 indicates perfect inelasticity, the object will lose
all energy in a collision. Here we have set the default restitution of all objects to 0.5,
somewhere in between the two extremes.

Static and dynamic friction are the two surface properties defined by the material. Static
friction determines how difficult it is to start an object sliding from a stop. Dynamic
friction determines how difficult it is to keep an object sliding. We have set both static

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
17
and dynamic frictions to 0.5, so all objects in the scene will have average resistance to
sliding.

We have only defined the default, universal material here, set at material index 0. You
can add more materials to your scene as needed. For instance, you can add a completely
frictionless material to index 1.

// Create a frictionless material


NxMaterial* frictionlessMaterial = gScene->getMaterialFromIndex(1);
frictionlessMaterial->setRestitution(0.5);
frictionlessMaterial->setStaticFriction(0);
frictionlessMaterial->setDynamicFriction(0);

Materials are defined on a per-shape basis. To apply the frictionless material to an actor,
do the following.

NxActor* actor;

NxShape** shapes = actor->getShapes();
NxU32 nShapes = actor->getNbShapes();
while (nShapes--)
{
shapes[nShapes]->setMaterial(1);
}

For triangle mesh shapes, you can apply materials to the entire shape or on a per-triangle
basis.

As a general rule, think about the actual materials your objects are made out of when
determining what material parameters to apply to them. If the object is a wooden crate, it
most likely has average restitution and friction. If the object is a metal ball, like a pinball,
it will have a high restitution and low friction. If the object is a wet cloth, it will have a
low restitution and high friction.

Certain objects will have anisotropic friction, that is, they will slide better along one axis
than another. The restitution and friction of object materials can be combined in different
ways as two objects bounce off and slide along each other. By default, restitutions and
frictions are averaged, though you may wish to switch to a different combine mode to
achieve a desired result. Both anisotropic friction and material combine modes are
explained in detail in “Lesson 105: Materials”.

9 Joints
Many of your objects may be jointed together. A door swinging on a hinge will be
connected to a wall by a revolute joint. A rope hanging from the ceiling will be a series
of rigid bodies connected by spherical joints end to end, with one end connected to an
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
18
absolute position in the level by a spherical joint or other type of joint. Some objects you
will want to joint together with a fixed joint to create new rigid bodies, either because
you wish them to break apart into the two jointed pieces at some point or because it’s
convenient just to weld the two objects together rather than create a completely new one
built from the same shapes.

Joints are discussed in detail in “Lesson 201: Joints”. The majority of the intermediate
lessons cover all the different types of joints and how to use them.

Joints are relatively new to games and their use has been infrequent. Possibly the most
prevalent use of joints in games has been to contruct ragdolls. Basic ragdoll construction
is discussed in “Lesson 404: Ragdolls”. This lesson builds a ragdoll in code.

One of the simplest joints is the revolute or “hinge” joint detailed in the first lesson on
joints.

// Actor globals
NxActor* box1 = NULL;
NxActor* box2 = NULL;

// Joint globals
NxRevoluteJoint* revJoint = NULL;

NxRevoluteJoint* CreateRevoluteJoint(NxActor* a0, NxActor* a1, NxVec3


globalAnchor, NxVec3 globalAxis)
{
NxRevoluteJointDesc revDesc;

revDesc.actor[0] = a0;
revDesc.actor[1] = a1;
revDesc.setGlobalAnchor(globalAnchor);
revDesc.setGlobalAxis(globalAxis);

revDesc.jointFlags |= NX_JF_COLLISION_ENABLED;

return (NxRevoluteJoint*)gScene->createJoint(revDesc);
}

void InitNx()
{

box1 = CreateBox(NxVec3(0,5,0), NxVec3(0.5,2,1), 10);
box1->raiseBodyFlag(NX_BF_KINEMATIC);
box2 = CreateBox(NxVec3(0,1,0), NxVec3(0.5,2,1), 10);

NxVec3 globalAnchor = NxVec3(0.5,5,0);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
19
NxVec3 globalAxis = NxVec3(0,0,1);

revJoint = CreateRevoluteJoint(box1, box2, globalAnchor, globalAxis);


}

This connects two box shape actors together with a revolute joint located at (0.5,5,0) in
world space. The axis the lower box swings about the upper box is (0,0,1).

This joint connects the dynamic actor box2 to the kinematic actor box1. If you want to
connect box2 to a position in world space, pass in NULL instead of box1.

revJoint = CreateRevoluteJoint(NULL, box2, globalAnchor, globalAxis);

With this, box2 will be jointed to the world instead of box1. This is useful for jointing
objects to fixed positions in your game world.

There are many different types of joints: revolute, spherical, prismatic, cylindrical, point-
on-line, point-in-plane, etc. Each type corresponds to certain axes being set free for
rotation or translation. You can define limits, springs, motors, and breaking forces on
joints to further refine their behaviour. There is a Six Degree of Freedom joint which
will eventually replace all the different types as a single all-encompassing joint. Details
on this joint are available in “Lesson 215: Six Degree of Freedom Joints”.

10 Spring and Damper Effectors


Objects can be connected by spring and damper effectors as well as joints. This is a
“looser” connection between actors as joints are generally hard constraints that
completely stop rotational or translation motion of the actors along certain axes between
them, while springs generate forces that act to pull them together or push them apart.

Spring and damper effectors are covered in “Lesson 214: Spring and Damper Effectors”.
In this lesson, two spheres are connected at their centers by a spring.

// Actor globals
NxActor* sphere1 = NULL;
NxActor* sphere2 = NULL;

// Spring and Damper Effector globals


NxSpringAndDamperEffector* spring = NULL;

NxSpringAndDamperEffector* CreateSpring()
{
NxSpringAndDamperEffectorDesc sadeDesc;
return gScene->createSpringAndDamperEffector(sadeDesc);
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
20
void InitNx()
{

sphere1 = CreateSphere(NxVec3(3,0,0), 1, 25);
sphere1->raiseBodyFlag(NX_BF_FROZEN_ROT);
sphere1->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
sphere1->setLinearDamping(0.25);

sphere2 = CreateSphere(NxVec3(-3,0,0), 1, 25);


sphere2->raiseBodyFlag(NX_BF_FROZEN_ROT);
sphere2->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
sphere2->setLinearDamping(0.25);

spring = CreateSpring();

NxVec3 pos1 = sphere1->getCMassGlobalPosition();


NxVec3 pos2 = sphere2->getCMassGlobalPosition();
NxVec3 springVec = pos2 - pos1;

spring->setBodies(sphere1, pos1, sphere2, pos2);

NxReal distSpringRelaxed = springVec.magnitude();


NxReal maxSpringCompressForce = 2000;
NxReal distSpringCompressSaturate = 0.25*distSpringRelaxed;
NxReal maxSpringStretchForce = 2000;
NxReal distSpringStretchSaturate = 2.0*distSpringRelaxed;

spring->setLinearSpring(distSpringCompressSaturate, distSpringRelaxed,
distSpringStretchSaturate,maxSpringCompressForce, maxSpringStretchForce);

NxReal maxDamperCompressForce = 100;


NxReal velDamperCompressSaturate = 200;
NxReal maxDamperStretchForce = 100;
NxReal velDamperStretchSaturate = 200;

spring->setLinearDamper(velDamperCompressSaturate, velDamperStretchSaturate,
maxDamperCompressForce, maxDamperStretchForce);

}

The spring damper effector applies a force to each sphere along a line linking their
centers of mass. When the distance between the spheres is less than the relaxed distance,
a repulsive force is applied, increasing to a maximum force as the spheres approach each
other. When it is greater than the relaxed distance, an attractive force is applied,
increasing to a maximum as the spheres move away from each other. Damping forces

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
21
works to conteract the relative velocities between the spheres as they exceed a certain
“compress velocity” or “stretch velocity”.

11 Asynchronous API, the Main Physics Loop, and


ProcessInputs()
You should now have a complete game level filled with actors built from various shapes
and materials, some jointed to each other or to the world, some connected by springs. No
new data or input is introduced into the world. You only need to call
NxScene::simulate(), ::flushSteram(), and ::fetchResults() each frame with a time delta
for the frame and the simulation runs on its own. This is shown in the following basic
physics loop.

void RenderCallback()
{

if (gScene && !bPause) RunPhysics();

}

void RunPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();

// Run collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
gScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
}

This physics loop is inefficient as it stalls the game while it waits for the physics
simulation to update. This is fine for applications with few objects where the speed hit is
negligible, but for large game applications with potentially thousands of objects, we want
to take advantage of our ability to run the physics simulation concurrently with the rest of
the code. For a full discussion of concurrency and the asynchronous API, see “Lesson
801: Running on Hardware”.

In order to take advantage of the asynchronous API, we will reverse the ordering of the
main physics loop so that we call fetchResults() before simulate() as shown in the
following updated physics loop.

void RenderCallback()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
22
if (gScene && !bPause)
{
GetPhysicsResults();
ProcessInputs();
StartPhysics();
}

}

void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}

void ProcessInputs()
{
// Gather results of previous inputs and issue new inputs
ProcessKeys();

// Show debug wireframes


if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

void StartPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();

// Start collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
}

For this order-reversal to work, we make an initial call to StartPhysics() in InitNx() to get
the ball rolling.

void InitNx()
{
...
// Initialize the physics loop
UpdateTime();
if (gScene && !bPause) StartPhysics();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
23
}

In these next sections, we will be introducing inputs to the scene. In between


fetchResults() and simulate() we will put a ProcessInputs() function which gathers results
from previous inputs and issues new inputs to the scene.

12 Debug Rendering
Note the contents of our example ProcessInputs() function.

void ProcessInputs()
{

// Show debug wireframes
// Show debug wireframes
if (bDebugWireframeMode)
{
if (gScene) gDebugRenderer.renderData(*gScene->getDebugRenderable());
}
}

If you haven’t already, familiarize yourself with the Debug Renderer in the “User
Defined Classes” document. We put the SDK and scene visualize() calls here and pass in
our Debug Renderer report. The functions in the Debug Renderer report are called by the
solver within the NxScene::simulate(), ::fetchResults() loop to display debug wireframe
information.

Adjustments or inputs to the user defined reports must be done in ProcessInputs() after
data for the reports from the previous scene is retrieved by NxScene::fetchResults() and
before the next scene is kicked off with the NxScene::simulate() function.

13 Moving your Objects


The other function within our sample ProcessInputs() is ProcessKeys():

void ProcessPhysics()
{
ProcessKeys();

}

void ProcessKeys()
{
// Process keys
for (int i = 0; i < MAX_KEYS; i++)

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
24
{
if (!gKeys[i]) { continue; }

switch (i)
{
// Force controls
case 'i': {gForceVec = ApplyForceToActor(gSelectedActor,NxVec3(0,0,1),
gForceStrength,bForceMode); break; }

}
}
}

NxVec3 ApplyForceToActor(NxActor* actor, const NxVec3& forceDir, const NxReal


forceStrength, bool forceMode)
{
NxVec3 forceVec = forceStrength*forceDir;

if (forceMode)
actor->addForce(forceVec);
else
actor->addTorque(forceVec);

return forceVec;
}

Given a particular keypress, a force or torque will be applied to gSelectedActor using


NxActor::addForce() or ::addTorque().

In your game code, you will need to determine what forces or torques you want to apply
to your actors, batch them up, and then fire them off in your ProcessInputs() function for
them to influence the next iteration of the simulation.

If you wish to move an actor using moveGlobalPos*() or setGlobalPos*(), you will want
to do this in ProcessInputs() as well. Note that, in general, you should use
moveGlobalPos*() or setGlobalPos*() only on kinematic actors. If you need to use either
on a dynamic actor, the safest way is to set the actor to kinematic, move it with
moveGlobalPos*() or setGlobalPos*(), then set it back to dynamic. It is possible to use
either moveGlobalPos*() or setGlobalPos*() on dynamic objects, but realize these objects
are being influenced by physical forces from the scene as you move them, and the results
can be unpredicatable.

14 Character Controller
You now have a game level populated with objects that are, with the right materials,
behaving in a manner consistent with your game world. Appropriate objects are
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
25
connected by joints and/or springs. You can apply forces to dynamic objects or move
kinematic objects by adding inputs to the scene in a ProcessInputs() or similar function
that resides between the last NxScene::fetchResults() and the next NxScene::simulate().

Now you want to add a controller for your player. For complete information on the
character controller look at “Lesson 1201: Character Controller”.

The character controller consists of a Controller Hit Report to define how the character
affects dynamic objects it collides with.

class ControllerHitReport : public NxUserControllerHitReport


{
public:
virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit)
{
//RenderTerrainTriangle(hit.faceID);

if (1 && hit.shape)
{
NxCollisionGroup group = hit.shape->getGroup();
if (group!=GROUP_COLLIDABLE_NON_PUSHABLE)
{
NxActor& actor = hit.shape->getActor();
if (actor.isDynamic())
{
if (gPts[gNbPts]!=hit.worldPos)
{
gPts[gNbPts++] = hit.worldPos;
if (gNbPts==MAX_NB_PTS) gNbPts=0;
}

// We only allow horizontal pushes. Vertical pushes when we stand on dynamic


objects creates
// useless stress on the solver. It would be possible to enable/disable vertical
pushes on
// particular objects, if the gameplay requires it.
if (hit.dir.y==0.0f)
{
NxF32 coeff = actor.getMass() * hit.length * 10.0f;
actor.addForceAtLocalPos(hit.dir*coeff, NxVec3(0,0,0), NX_IMPULSE);
...
}
}
}
}

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
26
return NX_ACTION_NONE;
}

virtual NxControllerAction onControllerHit(const NxControllersHit& hit)


{
return NX_ACTION_NONE;
}

} gControllerHitReport;

void InitCharacterControllers(NxU32 nbCharacters, NxScene& scene)


{
...
// Create all characters
for(NxU32 i=0;i<nbCharacters;i++)
{
...
desc.callback = &gControllerHitReport;
...
}
}

Set the character controller parameters to define the character’s vertical axis and other
properties.

void InitNx()
{
...
// Create the character controllers
InitCharacterControllers(gNbCharacters, *gScene);
...
}
...
//#define USE_SPHERE_CONTROLLER
#define SKINWIDTH 0.1f
...
static NxVec3 gStartPos(0,2,0);
static NxVec3 gInitialExtents(0.5,1,0.5);
#ifdef USE_SPHERE_CONTROLLER
static NxF32 gInitialRadius = 1;
#endif
...
static NxU32 gNbCharacters = 0;
#ifdef USE_SPHERE_CONTROLLER
static NxSphereController** gControllers = NULL;
#else

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
27
static NxBoxController** gControllers = NULL;
#endif

#include "ControllerManager.h"
static ControllerManager gCM;
...
void InitCharacterControllers(NxU32 nbCharacters, NxScene& scene)
{
...
gControllers =
(NxBoxController**)NX_ALLOC(sizeof(NxBoxController*)*nbCharacters);
...
gNbCharacters = nbCharacters;

// Create all characters


for(NxU32 i=0;i<nbCharacters;i++)
{
...
gControllers[i] = (NxBoxController*)gCM.createController(&scene, desc);
...
}
}

The character controller performs intersection tests to determine how the character
controller object should be placed in the scene each update. From the character's origin
you sweep out an AABB (or sphere) in a certain direction and detect what geometry the
shape hits first. Using the geometry data and how the shape intersects it, we get the
character to slide along the level geometry.

BoxController::BoxController(const NxControllerDesc& desc, NxScene* s) :


Controller(desc, s)
{
Controller* ctrl = this;
appData = ctrl;

const NxBoxControllerDesc& bc = (const NxBoxControllerDesc&)desc;

extents = bc.extents;

// Create kinematic actor under the hood


if(1)
{
NxBodyDesc bodyDesc;
bodyDesc.flags |= NX_BF_KINEMATIC;

NxBoxShapeDesc boxDesc;

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
28
...
boxDesc.dimensions = extents*0.8f;
...
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&boxDesc);
actorDesc.body = &bodyDesc;
actorDesc.density = 10000.0f; // ### expose this ?
actorDesc.density = 10.0f; // ### expose this ?
actorDesc.globalPose.t = position;

kineActor = scene->createActor(actorDesc);
}
}

15 Vehicle Controller
The vehicle controller makes use of swept shapes as well. A swept sphere shape is used
to simulate the tires on the vehicle. See “Lesson 701: Wheel Shapes” for a full
explanation.

class ContactReport : public NxUserContactReport


{
public:
virtual void onContactNotify(NxContactPair& pair, NxU32 events)
{
// Iterate through contact points
NxContactStreamIterator i(pair.stream);
//user can call getNumPairs() here
while(i.goNextPair())
{
//user can also call getShape() and getNumPatches() here
NxShape * s = i.getShape(carIndex);
while(i.goNextPatch())
{
//user can also call getPatchNormal() and getNumPoints() here
const NxVec3& contactNormal = i.getPatchNormal();
while(i.goNextPoint())
{
//user can also call getPoint() and getSeparation() here
const NxVec3& contactPoint = i.getPoint();

//add forces:

//assuming front wheel drive we need to apply a force at the wheels.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
29
if (s->is(NX_SHAPE_CAPSULE)) //assuming only the wheels of
the car are capsules, otherwise we need more checks.
//this branch can't be pulled out of loops because we have to do a full
iteration through the stream
{
CarWheelContact cwc;
cwc.car = pair.actors[carIndex];
cwc.wheel = s;
cwc.contactPoint = contactPoint;
wheelContactPoints.pushBack(cwc);
}
}
}
}
}
} carContactReportObj;

NxUserContactReport* carContactReport = &carContactReportObj;

void tickCar()
{
NxReal steeringAngle = gSteeringValue * gMaxSteeringAngle;

NxArray<CarWheelContact>::iterator i = wheelContactPoints.begin();
while(i != wheelContactPoints.end())
{
CarWheelContact & cwc = *i;

WheelShapeUserData * wheelData = (WheelShapeUserData *)(cwc.wheel-


>userData);

//apply to powered wheels only.


if (wheelData->frontWheel)
{
//steering:
NxMat33 wheelOrientation = cwc.wheel->getLocalOrientation();
wheelOrientation.setColumn(0, NxVec3(NxMath::cos(steeringAngle), 0,
NxMath::sin(steeringAngle) ));
wheelOrientation.setColumn(2, NxVec3(NxMath::sin(steeringAngle), 0, -
NxMath::cos(steeringAngle) ));
cwc.wheel->setLocalOrientation(wheelOrientation);

if (frontWheelIsPowered)
{
//get the world space orientation:
wheelOrientation = cwc.wheel->getGlobalOrientation();

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
30
NxVec3 steeringDirection;
wheelOrientation.getColumn(0, steeringDirection);

//the power direction of the front wheel is the wheel's axis as it is steered.
if (gMotorForce)
cwc.car->addForceAtPos(steeringDirection * gMotorForce,
cwc.contactPoint);
}
}
if (!wheelData->frontWheel && rearWheelIsPowered)
{
//get the orientation of this car:
NxMat33 m = cwc.car->getGlobalOrientation();
NxVec3 carForwardAxis;
m.getColumn(0, carForwardAxis);
//the power direction of the rear wheel is always the car's length axis.
cwc.car->addForceAtPos(carForwardAxis * gMotorForce,cwc.contactPoint);
}
i++;
}

wheelContactPoints.clear();

Essentially, the car we have created has four tires, represented by swept spheres that cast
to the ground.

16 Triggers
You now have a fully walkable and/or drivable level replete with physical objects and a
controllable camera.

At this point, you want to populate your level with trigger shapes. To read more on
trigger shapes, go to “Lesson 304: Trigger Report”.

A trigger is a shape with the NX_TRIGGER_ENABLE flag set. Each trigger calls
onTrigger(), from the user’s Trigger Report, for every shape that intersects the trigger
area.

static NxI32 gNbTouchedBodies = 0;

class TriggerReport : public NxUserTriggerReport


{
public:

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
31
virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape,
NxTriggerFlag status)
{
if (status & NX_TRIGGER_ON_ENTER)
{
// A body just entered the trigger area
gNbTouchedBodies++;
}
if (status & NX_TRIGGER_ON_LEAVE)
{
// A body just left the trigger area
gNbTouchedBodies--;
}
NX_ASSERT(gNbTouchedBodies>=0);
}
} gTriggerReport;

NxActor* triggerBox = NULL;



// Create a static trigger
NxActor* CreateTriggerBox(const NxVec3& pos, const NxVec3& boxDim)
{
NxActorDesc actorDesc;

NxBoxShapeDesc boxDesc;
boxDesc.dimensions = boxDim;
boxDesc.shapeFlags |= NX_TRIGGER_ENABLE;

actorDesc.shapes.pushBack(&boxDesc);
actorDesc.globalPose.t = pos + NxVec3(0, boxDim.y, 0);

return gScene->createActor(actorDesc);
}

void InitNx()
{

triggerBox = CreateTriggerBox(NxVec3(0,0,0), NxVec3(2,2,2));
triggerBox->userData = (void*)-1;
gScene->setUserTriggerReport(&gTriggerReport);
}

Triggers are used to detect when the player or other game objects enter or leave certain
areas of the game world.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
32
17 Raycasts
Your game now has trigger shapes in place to determine when the character arrives at
different points in the level. Now you want the ability to perform raycasts to determine
things like where actor shadows fall onto other actors or line-of-sight from A.I. characters
to the player.

Raycasting is discussed in detail in “Lesson 305: Raycast Report”.

To cast rays through your scene, you will need a Raycast Report.

class RaycastReport : public NxUserRaycastReport


{
public:
virtual bool onHit(const NxRaycastHit& hit)
{
int userData = (int)hit.shape->getActor().userData;
userData |= 1; // Mark as hit
hit.shape->getActor().userData = (void*)userData;

const NxVec3& worldImpact = hit.worldImpact;

// Light up the hit polygon on a triangle mesh shape


NxTriangleMeshShape *tmShape = hit.shape->isTriangleMesh();
if (tmShape)
{

}

return true;
}

} gRaycastReport;

You will want to do your raycasts in ProcessInputs().

NxActor* emitter = NULL;



void ProcessInputs()
{

// Cast a ray out of the emitter along its negative x-axis
if (bRaycastClosestShape)
RaycastClosestShapeFromActor(emitter, groupFlagA | groupFlagB);
else
RaycastAllShapesFromActor(emitter, groupFlagA | groupFlagB);

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
33
}

void RaycastClosestShapeFromActor(NxActor* actor, NxU32 groupFlag)


{
// Get ray origin
NxVec3 orig = actor->getCMassGlobalPosition();

// Get ray direction


NxVec3 dir;
NxMat33 m;
actor->getGlobalOrientation(m);
m.getColumn(0, dir);
dir = -dir;

NxRay ray(orig, dir);


NxRaycastHit hit;
NxReal dist;

// Get the closest shape


NxShape* closestShape = gScene->raycastClosestShape(ray, NX_ALL_SHAPES, hit,
groupFlag);
if (closestShape)
{

}
}

void RaycastAllShapesFromActor(NxActor* actor, NxU32 groupFlag)


{
// Get ray origin
NxVec3 orig = actor->getCMassGlobalPosition();

// Get ray direction


NxVec3 dir;
NxMat33 m;
actor->getGlobalOrientation(m);
m.getColumn(0, dir);
dir = -dir;

NxRay ray(orig, dir);


NxReal dist = 10000;

RaycastLine rl(ray.orig, ray.orig + dist*dir, NxVec3(0,0,1));


rlArray.pushBack(rl);

// Get all shapes

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
34
NxU32 nbShapes = gScene->raycastAllShapes(ray, gRaycastReport,
NX_ALL_SHAPES, groupFlag);
}

The raycast report will contain the results of the raycasts you send out at the end of the
next NxScene::fetchResults().

18 Contacts
You now have a complete game level with physical objects, character controller,
vehicles, chase camera, trigger areas, and the ability to cast rays through the scene.

You now want to be able to find points of contact between the objects in your scene.
This is covered in “Lesson 303: Contact Report”. You will need a Contact Report object.

class ContactReport : public NxUserContactReport


{
public:
virtual void onContactNotify(NxContactPair& pair, NxU32 events)
{
// Iterate through contact points
NxContactStreamIterator i(pair.stream);
//user can call getNumPairs() here
while(i.goNextPair())
{
//user can also call getShape() and getNumPatches() here
while(i.goNextPatch())
{
//user can also call getPatchNormal() and getNumPoints() here
const NxVec3& contactNormal = i.getPatchNormal();
while(i.goNextPoint())
{
//user can also call getShape() and getNumPatches() here
const NxVec3& contactPoint = i.getPoint();

// Get the normal force vector


normalForceVec = pair.sumNormalForce;

// Get the friction force vector


frictionForceVec = pair.sumFrictionForce;

// Get the penetration vector


penetrationVec = - contactNormal * i.getSeparation();
}
}
}
Copyright (c) 2008 NVIDIA Corporation. All rights reserved.
http://www.NVIDIA.com
35
}
} gContactReport;

The contact report is attached to the scene like so:

void InitNx()
{

// Create the scene
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;
sceneDesc.userContactReport = &gContactReport;
gScene = gPhysicsSDK->createScene(sceneDesc);

}

Every actor pair you add as a contact pair will be passed to onContactNotify(), in the
User Contact Report, if the pair is touching. To ensure a contact pair is enabled for the
next iteration of the simulation, add the pair to the contact list before or in
ProcessInputs(). The contact results available to you in your game code will be the ones
from the current state of the simulation, i.e., the physics “back buffer”, renewed with
each NxScene::fetchResults().

void InitNx()
{

box1 = CreateBox(NxVec3(-3,5,0), NxVec3(0.75,0.75,0.75), 5);
box2 = CreateBox(NxVec3(3,0,0), NxVec3(1,1,1), 5);

GameObject* Object1 = new GameObject;


Object1->id = 1;
Object1->events = 0;
box1->userData = Object1;

GameObject* Object2 = new GameObject;


Object2->id = 2;
Object2->events = 0;
box2->userData = Object2;

}

void ProcessInputs()
{

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
36
gScene->setActorPairFlags(*box1, *box2, NX_NOTIFY_ON_START_TOUCH |
NX_NOTIFY_ON_TOUCH | NX_NOTIFY_ON_END_TOUCH);

}

19 Particle Systems and Special Effects


Look at the “PhysX FX” document for more information on how to implement particle
systems and other special effects within an application using a generic physics SDK.

The SDK has a specialized fluid and smart particle implementation. “Lesson 410:
Particle Systems” details how to implement your own particle system using rigid bodies
as particles.

20 Exporters, Importers, and PML Files


Once you have a basic level populated with objects, character controllers, and vehicles,
you will want to write an importer to be able to load PML files into your game and an
exporter to write them out from your game object editor.

Details on importing and exporting PML files can be found in “Lesson 502: Loading
PML Files”

21 Conclusion
This integration guide is meant to be a step-by-step instruction manual on how to
integrate the PhysX SDK into a game application.

Copyright (c) 2008 NVIDIA Corporation. All rights reserved.


http://www.NVIDIA.com
37

Vous aimerez peut-être aussi