// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and related documentation and // any modifications thereto. Any use, reproduction, disclosure, or // distribution of this software and related documentation without an express // license agreement from NVIDIA Corporation is strictly prohibited. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2013-2017 NVIDIA Corporation. All rights reserved. #include "../core/types.h" #include "../core/maths.h" #include "../core/platform.h" #include "../core/mesh.h" #include "../core/voxelize.h" #include "../core/sdf.h" #include "../core/pfm.h" #include "../core/tga.h" #include "../core/perlin.h" #include "../core/convex.h" #include "../core/cloth.h" #include "../external/SDL2-2.0.4/include/SDL.h" #include "../include/NvFlex.h" #include "../include/NvFlexExt.h" #include "../include/NvFlexDevice.h" #include #include #include "shaders.h" #include "imgui.h" #include "shadersDemoContext.h" #if FLEX_DX #include "d3d\appGraphCtx.h" #endif #if ENABLE_AFTERMATH_SUPPORT #include #endif SDL_Window* g_window; // window handle unsigned int g_windowId; // window id #define SDL_CONTROLLER_BUTTON_LEFT_TRIGGER (SDL_CONTROLLER_BUTTON_MAX + 1) #define SDL_CONTROLLER_BUTTON_RIGHT_TRIGGER (SDL_CONTROLLER_BUTTON_MAX + 2) int GetKeyFromGameControllerButton(SDL_GameControllerButton button) { switch (button) { case SDL_CONTROLLER_BUTTON_DPAD_UP: { return SDLK_q; } // -- camera translate up case SDL_CONTROLLER_BUTTON_DPAD_DOWN: { return SDLK_z; } // -- camera translate down case SDL_CONTROLLER_BUTTON_DPAD_LEFT: { return SDLK_h; } // -- hide GUI case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: { return -1; } // -- unassigned case SDL_CONTROLLER_BUTTON_START: { return SDLK_RETURN; } // -- start selected scene case SDL_CONTROLLER_BUTTON_BACK: { return SDLK_ESCAPE; } // -- quit case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: { return SDLK_UP; } // -- select prev scene case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: { return SDLK_DOWN; } // -- select next scene case SDL_CONTROLLER_BUTTON_A: { return SDLK_g; } // -- toggle gravity case SDL_CONTROLLER_BUTTON_B: { return SDLK_p; } // -- pause case SDL_CONTROLLER_BUTTON_X: { return SDLK_r; } // -- reset case SDL_CONTROLLER_BUTTON_Y: { return SDLK_o; } // -- step sim case SDL_CONTROLLER_BUTTON_RIGHT_TRIGGER: { return SDLK_SPACE; } // -- emit particles default: { return -1; } // -- nop }; }; // // Gamepad thresholds taken from XINPUT API // #define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849 #define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689 #define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30 int deadzones[3] = { XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_TRIGGER_THRESHOLD }; inline float joyAxisFilter(int value, int stick) { //clamp values in deadzone to zero, and remap rest of range so that it linearly rises in value from edge of deadzone toward max value. if (value < -deadzones[stick]) return (value + deadzones[stick]) / (32768.0f - deadzones[stick]); else if (value > deadzones[stick]) return (value - deadzones[stick]) / (32768.0f - deadzones[stick]); else return 0.0f; } SDL_GameController* g_gamecontroller = NULL; using namespace std; int g_screenWidth = 1280; int g_screenHeight = 720; int g_msaaSamples = 8; int g_numSubsteps; // a setting of -1 means Flex will use the device specified in the NVIDIA control panel int g_device = -1; char g_deviceName[256]; bool g_vsync = true; bool g_benchmark = false; bool g_extensions = true; bool g_teamCity = false; bool g_interop = true; bool g_d3d12 = false; bool g_useAsyncCompute = true; bool g_increaseGfxLoadForAsyncComputeTesting = false; int g_graphics = 0; // 0=ogl, 1=DX11, 2=DX12 FluidRenderer* g_fluidRenderer; FluidRenderBuffers* g_fluidRenderBuffers; DiffuseRenderBuffers* g_diffuseRenderBuffers; NvFlexSolver* g_solver; NvFlexSolverDesc g_solverDesc; NvFlexLibrary* g_flexLib; NvFlexParams g_params; NvFlexTimers g_timers; int g_numDetailTimers; NvFlexDetailTimer * g_detailTimers; int g_maxDiffuseParticles; int g_maxNeighborsPerParticle; int g_numExtraParticles; int g_numExtraMultiplier = 1; int g_maxContactsPerParticle; // mesh used for deformable object rendering Mesh* g_mesh; vector g_meshSkinIndices; vector g_meshSkinWeights; vector g_meshRestPositions; const int g_numSkinWeights = 4; // mapping of collision mesh to render mesh std::map g_convexes; std::map g_meshes; std::map g_fields; // flag to request collision shapes be updated bool g_shapesChanged = false; /* Note that this array of colors is altered by demo code, and is also read from global by graphics API impls */ Colour g_colors[] = { Colour(0.0f, 0.5f, 1.0f), Colour(0.797f, 0.354f, 0.000f), Colour(0.092f, 0.465f, 0.820f), Colour(0.000f, 0.349f, 0.173f), Colour(0.875f, 0.782f, 0.051f), Colour(0.000f, 0.170f, 0.453f), Colour(0.673f, 0.111f, 0.000f), Colour(0.612f, 0.194f, 0.394f) }; struct SimBuffers { NvFlexVector positions; NvFlexVector restPositions; NvFlexVector velocities; NvFlexVector phases; NvFlexVector densities; NvFlexVector anisotropy1; NvFlexVector anisotropy2; NvFlexVector anisotropy3; NvFlexVector normals; NvFlexVector smoothPositions; NvFlexVector diffusePositions; NvFlexVector diffuseVelocities; NvFlexVector diffuseCount; NvFlexVector activeIndices; // convexes NvFlexVector shapeGeometry; NvFlexVector shapePositions; NvFlexVector shapeRotations; NvFlexVector shapePrevPositions; NvFlexVector shapePrevRotations; NvFlexVector shapeFlags; // rigids NvFlexVector rigidOffsets; NvFlexVector rigidIndices; NvFlexVector rigidMeshSize; NvFlexVector rigidCoefficients; NvFlexVector rigidPlasticThresholds; NvFlexVector rigidPlasticCreeps; NvFlexVector rigidRotations; NvFlexVector rigidTranslations; NvFlexVector rigidLocalPositions; NvFlexVector rigidLocalNormals; // inflatables NvFlexVector inflatableTriOffsets; NvFlexVector inflatableTriCounts; NvFlexVector inflatableVolumes; NvFlexVector inflatableCoefficients; NvFlexVector inflatablePressures; // springs NvFlexVector springIndices; NvFlexVector springLengths; NvFlexVector springStiffness; NvFlexVector triangles; NvFlexVector triangleNormals; NvFlexVector uvs; SimBuffers(NvFlexLibrary* l) : positions(l), restPositions(l), velocities(l), phases(l), densities(l), anisotropy1(l), anisotropy2(l), anisotropy3(l), normals(l), smoothPositions(l), diffusePositions(l), diffuseVelocities(l), diffuseCount(l), activeIndices(l), shapeGeometry(l), shapePositions(l), shapeRotations(l), shapePrevPositions(l), shapePrevRotations(l), shapeFlags(l), rigidOffsets(l), rigidIndices(l), rigidMeshSize(l), rigidCoefficients(l), rigidPlasticThresholds(l), rigidPlasticCreeps(l), rigidRotations(l), rigidTranslations(l), rigidLocalPositions(l), rigidLocalNormals(l), inflatableTriOffsets(l), inflatableTriCounts(l), inflatableVolumes(l), inflatableCoefficients(l), inflatablePressures(l), springIndices(l), springLengths(l), springStiffness(l), triangles(l), triangleNormals(l), uvs(l) {} }; SimBuffers* g_buffers; void MapBuffers(SimBuffers* buffers) { buffers->positions.map(); buffers->restPositions.map(); buffers->velocities.map(); buffers->phases.map(); buffers->densities.map(); buffers->anisotropy1.map(); buffers->anisotropy2.map(); buffers->anisotropy3.map(); buffers->normals.map(); buffers->diffusePositions.map(); buffers->diffuseVelocities.map(); buffers->diffuseCount.map(); buffers->smoothPositions.map(); buffers->activeIndices.map(); // convexes buffers->shapeGeometry.map(); buffers->shapePositions.map(); buffers->shapeRotations.map(); buffers->shapePrevPositions.map(); buffers->shapePrevRotations.map(); buffers->shapeFlags.map(); buffers->rigidOffsets.map(); buffers->rigidIndices.map(); buffers->rigidMeshSize.map(); buffers->rigidCoefficients.map(); buffers->rigidPlasticThresholds.map(); buffers->rigidPlasticCreeps.map(); buffers->rigidRotations.map(); buffers->rigidTranslations.map(); buffers->rigidLocalPositions.map(); buffers->rigidLocalNormals.map(); buffers->springIndices.map(); buffers->springLengths.map(); buffers->springStiffness.map(); // inflatables buffers->inflatableTriOffsets.map(); buffers->inflatableTriCounts.map(); buffers->inflatableVolumes.map(); buffers->inflatableCoefficients.map(); buffers->inflatablePressures.map(); buffers->triangles.map(); buffers->triangleNormals.map(); buffers->uvs.map(); } void UnmapBuffers(SimBuffers* buffers) { // particles buffers->positions.unmap(); buffers->restPositions.unmap(); buffers->velocities.unmap(); buffers->phases.unmap(); buffers->densities.unmap(); buffers->anisotropy1.unmap(); buffers->anisotropy2.unmap(); buffers->anisotropy3.unmap(); buffers->normals.unmap(); buffers->diffusePositions.unmap(); buffers->diffuseVelocities.unmap(); buffers->diffuseCount.unmap(); buffers->smoothPositions.unmap(); buffers->activeIndices.unmap(); // convexes buffers->shapeGeometry.unmap(); buffers->shapePositions.unmap(); buffers->shapeRotations.unmap(); buffers->shapePrevPositions.unmap(); buffers->shapePrevRotations.unmap(); buffers->shapeFlags.unmap(); // rigids buffers->rigidOffsets.unmap(); buffers->rigidIndices.unmap(); buffers->rigidMeshSize.unmap(); buffers->rigidCoefficients.unmap(); buffers->rigidPlasticThresholds.unmap(); buffers->rigidPlasticCreeps.unmap(); buffers->rigidRotations.unmap(); buffers->rigidTranslations.unmap(); buffers->rigidLocalPositions.unmap(); buffers->rigidLocalNormals.unmap(); // springs buffers->springIndices.unmap(); buffers->springLengths.unmap(); buffers->springStiffness.unmap(); // inflatables buffers->inflatableTriOffsets.unmap(); buffers->inflatableTriCounts.unmap(); buffers->inflatableVolumes.unmap(); buffers->inflatableCoefficients.unmap(); buffers->inflatablePressures.unmap(); // triangles buffers->triangles.unmap(); buffers->triangleNormals.unmap(); buffers->uvs.unmap(); } SimBuffers* AllocBuffers(NvFlexLibrary* lib) { return new SimBuffers(lib); } void DestroyBuffers(SimBuffers* buffers) { // particles buffers->positions.destroy(); buffers->restPositions.destroy(); buffers->velocities.destroy(); buffers->phases.destroy(); buffers->densities.destroy(); buffers->anisotropy1.destroy(); buffers->anisotropy2.destroy(); buffers->anisotropy3.destroy(); buffers->normals.destroy(); buffers->diffusePositions.destroy(); buffers->diffuseVelocities.destroy(); buffers->diffuseCount.destroy(); buffers->smoothPositions.destroy(); buffers->activeIndices.destroy(); // convexes buffers->shapeGeometry.destroy(); buffers->shapePositions.destroy(); buffers->shapeRotations.destroy(); buffers->shapePrevPositions.destroy(); buffers->shapePrevRotations.destroy(); buffers->shapeFlags.destroy(); // rigids buffers->rigidOffsets.destroy(); buffers->rigidIndices.destroy(); buffers->rigidMeshSize.destroy(); buffers->rigidCoefficients.destroy(); buffers->rigidPlasticThresholds.destroy(); buffers->rigidPlasticCreeps.destroy(); buffers->rigidRotations.destroy(); buffers->rigidTranslations.destroy(); buffers->rigidLocalPositions.destroy(); buffers->rigidLocalNormals.destroy(); // springs buffers->springIndices.destroy(); buffers->springLengths.destroy(); buffers->springStiffness.destroy(); // inflatables buffers->inflatableTriOffsets.destroy(); buffers->inflatableTriCounts.destroy(); buffers->inflatableVolumes.destroy(); buffers->inflatableCoefficients.destroy(); buffers->inflatablePressures.destroy(); // triangles buffers->triangles.destroy(); buffers->triangleNormals.destroy(); buffers->uvs.destroy(); delete buffers; } Vec3 g_camPos(6.0f, 8.0f, 18.0f); Vec3 g_camAngle(0.0f, -DegToRad(20.0f), 0.0f); Vec3 g_camVel(0.0f); Vec3 g_camSmoothVel(0.0f); float g_camSpeed; float g_camNear; float g_camFar; Vec3 g_lightPos; Vec3 g_lightDir; Vec3 g_lightTarget; bool g_pause = false; bool g_step = false; bool g_capture = false; bool g_showHelp = true; bool g_tweakPanel = true; bool g_fullscreen = false; bool g_wireframe = false; bool g_debug = false; bool g_emit = false; bool g_warmup = false; float g_windTime = 0.0f; float g_windFrequency = 0.1f; float g_windStrength = 0.0f; bool g_wavePool = false; float g_waveTime = 0.0f; float g_wavePlane; float g_waveFrequency = 1.5f; float g_waveAmplitude = 1.0f; float g_waveFloorTilt = 0.0f; Vec3 g_sceneLower; Vec3 g_sceneUpper; float g_blur; float g_ior; bool g_drawEllipsoids; bool g_drawPoints; bool g_drawMesh; bool g_drawCloth; float g_expandCloth; // amount to expand cloth along normal (to account for particle radius) bool g_drawOpaque; int g_drawSprings; // 0: no draw, 1: draw stretch 2: draw tether bool g_drawBases = false; bool g_drawContacts = false; bool g_drawNormals = false; bool g_drawDiffuse; bool g_drawShapeGrid = false; bool g_drawDensity = false; bool g_drawRopes; float g_pointScale; float g_ropeScale; float g_drawPlaneBias; // move planes along their normal for rendering float g_diffuseScale; float g_diffuseMotionScale; bool g_diffuseShadow; float g_diffuseInscatter; float g_diffuseOutscatter; float g_dt = 1.0f / 60.0f; // the time delta used for simulation float g_realdt; // the real world time delta between updates float g_waitTime; // the CPU time spent waiting for the GPU float g_updateTime; // the CPU time spent on Flex float g_renderTime; // the CPU time spent calling OpenGL to render the scene // the above times don't include waiting for vsync float g_simLatency; // the time the GPU spent between the first and last NvFlexUpdateSolver() operation. Because some GPUs context switch, this can include graphics time. int g_scene = 0; int g_selectedScene = g_scene; int g_levelScroll; // offset for level selection scroll area bool g_resetScene = false; //if the user clicks the reset button or presses the reset key this is set to true; int g_frame = 0; int g_numSolidParticles = 0; int g_mouseParticle = -1; float g_mouseT = 0.0f; Vec3 g_mousePos; float g_mouseMass; bool g_mousePicked = false; // mouse int g_lastx; int g_lasty; int g_lastb = -1; bool g_profile = false; bool g_outputAllFrameTimes = false; bool g_asyncComputeBenchmark = false; ShadowMap* g_shadowMap; Vec4 g_fluidColor; Vec4 g_diffuseColor; Vec3 g_meshColor; Vec3 g_clearColor; float g_lightDistance; float g_fogDistance; FILE* g_ffmpeg; void DrawShapes(); class Scene; vector g_scenes; struct Emitter { Emitter() : mSpeed(0.0f), mEnabled(false), mLeftOver(0.0f), mWidth(8) {} Vec3 mPos; Vec3 mDir; Vec3 mRight; float mSpeed; bool mEnabled; float mLeftOver; int mWidth; }; vector g_emitters(1); // first emitter is the camera 'gun' struct Rope { std::vector mIndices; }; vector g_ropes; inline float sqr(float x) { return x*x; } #include "helpers.h" #include "scenes.h" #include "benchmark.h" void Init(int scene, bool centerCamera = true) { RandInit(); if (g_solver) { if (g_buffers) DestroyBuffers(g_buffers); DestroyFluidRenderBuffers(g_fluidRenderBuffers); DestroyDiffuseRenderBuffers(g_diffuseRenderBuffers); for (auto& iter : g_meshes) { NvFlexDestroyTriangleMesh(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } for (auto& iter : g_fields) { NvFlexDestroyDistanceField(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } for (auto& iter : g_convexes) { NvFlexDestroyConvexMesh(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } g_fields.clear(); g_meshes.clear(); g_convexes.clear(); NvFlexDestroySolver(g_solver); g_solver = NULL; } // alloc buffers g_buffers = AllocBuffers(g_flexLib); // map during initialization MapBuffers(g_buffers); g_buffers->positions.resize(0); g_buffers->velocities.resize(0); g_buffers->phases.resize(0); g_buffers->rigidOffsets.resize(0); g_buffers->rigidIndices.resize(0); g_buffers->rigidMeshSize.resize(0); g_buffers->rigidRotations.resize(0); g_buffers->rigidTranslations.resize(0); g_buffers->rigidCoefficients.resize(0); g_buffers->rigidPlasticThresholds.resize(0); g_buffers->rigidPlasticCreeps.resize(0); g_buffers->rigidLocalPositions.resize(0); g_buffers->rigidLocalNormals.resize(0); g_buffers->springIndices.resize(0); g_buffers->springLengths.resize(0); g_buffers->springStiffness.resize(0); g_buffers->triangles.resize(0); g_buffers->triangleNormals.resize(0); g_buffers->uvs.resize(0); g_meshSkinIndices.resize(0); g_meshSkinWeights.resize(0); g_emitters.resize(1); g_emitters[0].mEnabled = false; g_emitters[0].mSpeed = 1.0f; g_emitters[0].mLeftOver = 0.0f; g_emitters[0].mWidth = 8; g_buffers->shapeGeometry.resize(0); g_buffers->shapePositions.resize(0); g_buffers->shapeRotations.resize(0); g_buffers->shapePrevPositions.resize(0); g_buffers->shapePrevRotations.resize(0); g_buffers->shapeFlags.resize(0); g_ropes.resize(0); // remove collision shapes delete g_mesh; g_mesh = NULL; g_frame = 0; g_pause = false; g_dt = 1.0f / 60.0f; g_waveTime = 0.0f; g_windTime = 0.0f; g_windStrength = 1.0f; g_blur = 1.0f; g_fluidColor = Vec4(0.1f, 0.4f, 0.8f, 1.0f); g_meshColor = Vec3(0.9f, 0.9f, 0.9f); g_drawEllipsoids = false; g_drawPoints = true; g_drawCloth = true; g_expandCloth = 0.0f; g_drawOpaque = false; g_drawSprings = false; g_drawDiffuse = false; g_drawMesh = true; g_drawRopes = true; g_drawDensity = false; g_ior = 1.0f; g_lightDistance = 2.0f; g_fogDistance = 0.005f; g_camSpeed = 0.075f; g_camNear = 0.01f; g_camFar = 1000.0f; g_pointScale = 1.0f; g_ropeScale = 1.0f; g_drawPlaneBias = 0.0f; // sim params g_params.gravity[0] = 0.0f; g_params.gravity[1] = -9.8f; g_params.gravity[2] = 0.0f; g_params.wind[0] = 0.0f; g_params.wind[1] = 0.0f; g_params.wind[2] = 0.0f; g_params.radius = 0.15f; g_params.viscosity = 0.0f; g_params.dynamicFriction = 0.0f; g_params.staticFriction = 0.0f; g_params.particleFriction = 0.0f; // scale friction between particles by default g_params.freeSurfaceDrag = 0.0f; g_params.drag = 0.0f; g_params.lift = 0.0f; g_params.numIterations = 3; g_params.fluidRestDistance = 0.0f; g_params.solidRestDistance = 0.0f; g_params.anisotropyScale = 1.0f; g_params.anisotropyMin = 0.1f; g_params.anisotropyMax = 2.0f; g_params.smoothing = 1.0f; g_params.dissipation = 0.0f; g_params.damping = 0.0f; g_params.particleCollisionMargin = 0.0f; g_params.shapeCollisionMargin = 0.0f; g_params.collisionDistance = 0.0f; g_params.sleepThreshold = 0.0f; g_params.shockPropagation = 0.0f; g_params.restitution = 0.0f; g_params.maxSpeed = FLT_MAX; g_params.maxAcceleration = 100.0f; // approximately 10x gravity g_params.relaxationMode = eNvFlexRelaxationLocal; g_params.relaxationFactor = 1.0f; g_params.solidPressure = 1.0f; g_params.adhesion = 0.0f; g_params.cohesion = 0.025f; g_params.surfaceTension = 0.0f; g_params.vorticityConfinement = 0.0f; g_params.buoyancy = 1.0f; g_params.diffuseThreshold = 100.0f; g_params.diffuseBuoyancy = 1.0f; g_params.diffuseDrag = 0.8f; g_params.diffuseBallistic = 16; g_params.diffuseLifetime = 2.0f; g_numSubsteps = 2; // planes created after particles g_params.numPlanes = 1; g_diffuseScale = 0.5f; g_diffuseColor = 1.0f; g_diffuseMotionScale = 1.0f; g_diffuseShadow = false; g_diffuseInscatter = 0.8f; g_diffuseOutscatter = 0.53f; // reset phase 0 particle color to blue g_colors[0] = Colour(0.0f, 0.5f, 1.0f); g_numSolidParticles = 0; g_waveFrequency = 1.5f; g_waveAmplitude = 1.5f; g_waveFloorTilt = 0.0f; g_emit = false; g_warmup = false; g_mouseParticle = -1; g_maxDiffuseParticles = 0; // number of diffuse particles g_maxNeighborsPerParticle = 96; g_numExtraParticles = 0; // number of particles allocated but not made active g_maxContactsPerParticle = 6; g_sceneLower = FLT_MAX; g_sceneUpper = -FLT_MAX; // initialize solver desc NvFlexSetSolverDescDefaults(&g_solverDesc); // create scene StartGpuWork(); g_scenes[g_scene]->Initialize(); EndGpuWork(); uint32_t numParticles = g_buffers->positions.size(); uint32_t maxParticles = numParticles + g_numExtraParticles*g_numExtraMultiplier; if (g_params.solidRestDistance == 0.0f) g_params.solidRestDistance = g_params.radius; // if fluid present then we assume solid particles have the same radius if (g_params.fluidRestDistance > 0.0f) g_params.solidRestDistance = g_params.fluidRestDistance; // set collision distance automatically based on rest distance if not alraedy set if (g_params.collisionDistance == 0.0f) g_params.collisionDistance = Max(g_params.solidRestDistance, g_params.fluidRestDistance)*0.5f; // default particle friction to 10% of shape friction if (g_params.particleFriction == 0.0f) g_params.particleFriction = g_params.dynamicFriction*0.1f; // add a margin for detecting contacts between particles and shapes if (g_params.shapeCollisionMargin == 0.0f) g_params.shapeCollisionMargin = g_params.collisionDistance*0.5f; // calculate particle bounds Vec3 particleLower, particleUpper; GetParticleBounds(particleLower, particleUpper); // accommodate shapes Vec3 shapeLower, shapeUpper; GetShapeBounds(shapeLower, shapeUpper); // update bounds g_sceneLower = Min(Min(g_sceneLower, particleLower), shapeLower); g_sceneUpper = Max(Max(g_sceneUpper, particleUpper), shapeUpper); g_sceneLower -= g_params.collisionDistance; g_sceneUpper += g_params.collisionDistance; // update collision planes to match flexs Vec3 up = Normalize(Vec3(-g_waveFloorTilt, 1.0f, 0.0f)); (Vec4&)g_params.planes[0] = Vec4(up.x, up.y, up.z, 0.0f); (Vec4&)g_params.planes[1] = Vec4(0.0f, 0.0f, 1.0f, -g_sceneLower.z); (Vec4&)g_params.planes[2] = Vec4(1.0f, 0.0f, 0.0f, -g_sceneLower.x); (Vec4&)g_params.planes[3] = Vec4(-1.0f, 0.0f, 0.0f, g_sceneUpper.x); (Vec4&)g_params.planes[4] = Vec4(0.0f, 0.0f, -1.0f, g_sceneUpper.z); (Vec4&)g_params.planes[5] = Vec4(0.0f, -1.0f, 0.0f, g_sceneUpper.y); g_wavePlane = g_params.planes[2][3]; g_buffers->diffusePositions.resize(g_maxDiffuseParticles); g_buffers->diffuseVelocities.resize(g_maxDiffuseParticles); g_buffers->diffuseCount.resize(1, 0); // for fluid rendering these are the Laplacian smoothed positions g_buffers->smoothPositions.resize(maxParticles); g_buffers->normals.resize(0); g_buffers->normals.resize(maxParticles); // initialize normals (just for rendering before simulation starts) int numTris = g_buffers->triangles.size() / 3; for (int i = 0; i < numTris; ++i) { Vec3 v0 = Vec3(g_buffers->positions[g_buffers->triangles[i * 3 + 0]]); Vec3 v1 = Vec3(g_buffers->positions[g_buffers->triangles[i * 3 + 1]]); Vec3 v2 = Vec3(g_buffers->positions[g_buffers->triangles[i * 3 + 2]]); Vec3 n = Cross(v1 - v0, v2 - v0); g_buffers->normals[g_buffers->triangles[i * 3 + 0]] += Vec4(n, 0.0f); g_buffers->normals[g_buffers->triangles[i * 3 + 1]] += Vec4(n, 0.0f); g_buffers->normals[g_buffers->triangles[i * 3 + 2]] += Vec4(n, 0.0f); } for (int i = 0; i < int(maxParticles); ++i) g_buffers->normals[i] = Vec4(SafeNormalize(Vec3(g_buffers->normals[i]), Vec3(0.0f, 1.0f, 0.0f)), 0.0f); // save mesh positions for skinning if (g_mesh) { g_meshRestPositions = g_mesh->m_positions; } else { g_meshRestPositions.resize(0); } g_solverDesc.maxParticles = maxParticles; g_solverDesc.maxDiffuseParticles = g_maxDiffuseParticles; g_solverDesc.maxNeighborsPerParticle = g_maxNeighborsPerParticle; g_solverDesc.maxContactsPerParticle = g_maxContactsPerParticle; // main create method for the Flex solver g_solver = NvFlexCreateSolver(g_flexLib, &g_solverDesc); // give scene a chance to do some post solver initialization g_scenes[g_scene]->PostInitialize(); // center camera on particles if (centerCamera) { g_camPos = Vec3((g_sceneLower.x + g_sceneUpper.x)*0.5f, min(g_sceneUpper.y*1.25f, 6.0f), g_sceneUpper.z + min(g_sceneUpper.y, 6.0f)*2.0f); g_camAngle = Vec3(0.0f, -DegToRad(15.0f), 0.0f); // give scene a chance to modify camera position g_scenes[g_scene]->CenterCamera(); } // create active indices (just a contiguous block for the demo) g_buffers->activeIndices.resize(g_buffers->positions.size()); for (int i = 0; i < g_buffers->activeIndices.size(); ++i) g_buffers->activeIndices[i] = i; // resize particle buffers to fit g_buffers->positions.resize(maxParticles); g_buffers->velocities.resize(maxParticles); g_buffers->phases.resize(maxParticles); g_buffers->densities.resize(maxParticles); g_buffers->anisotropy1.resize(maxParticles); g_buffers->anisotropy2.resize(maxParticles); g_buffers->anisotropy3.resize(maxParticles); // save rest positions g_buffers->restPositions.resize(g_buffers->positions.size()); for (int i = 0; i < g_buffers->positions.size(); ++i) g_buffers->restPositions[i] = g_buffers->positions[i]; // builds rigids constraints if (g_buffers->rigidOffsets.size()) { assert(g_buffers->rigidOffsets.size() > 1); const int numRigids = g_buffers->rigidOffsets.size() - 1; // If the centers of mass for the rigids are not yet computed, this is done here // (If the CreateParticleShape method is used instead of the NvFlexExt methods, the centers of mass will be calculated here) if (g_buffers->rigidTranslations.size() == 0) { g_buffers->rigidTranslations.resize(g_buffers->rigidOffsets.size() - 1, Vec3()); CalculateRigidCentersOfMass(&g_buffers->positions[0], g_buffers->positions.size(), &g_buffers->rigidOffsets[0], &g_buffers->rigidTranslations[0], &g_buffers->rigidIndices[0], numRigids); } // calculate local rest space positions g_buffers->rigidLocalPositions.resize(g_buffers->rigidOffsets.back()); CalculateRigidLocalPositions(&g_buffers->positions[0], &g_buffers->rigidOffsets[0], &g_buffers->rigidTranslations[0], &g_buffers->rigidIndices[0], numRigids, &g_buffers->rigidLocalPositions[0]); // set rigidRotations to correct length, probably NULL up until here g_buffers->rigidRotations.resize(g_buffers->rigidOffsets.size() - 1, Quat()); } // unmap so we can start transferring data to GPU UnmapBuffers(g_buffers); //----------------------------- // Send data to Flex NvFlexCopyDesc copyDesc; copyDesc.dstOffset = 0; copyDesc.srcOffset = 0; copyDesc.elementCount = numParticles; NvFlexSetParams(g_solver, &g_params); NvFlexSetParticles(g_solver, g_buffers->positions.buffer, ©Desc); NvFlexSetVelocities(g_solver, g_buffers->velocities.buffer, ©Desc); NvFlexSetNormals(g_solver, g_buffers->normals.buffer, ©Desc); NvFlexSetPhases(g_solver, g_buffers->phases.buffer, ©Desc); NvFlexSetRestParticles(g_solver, g_buffers->restPositions.buffer, ©Desc); NvFlexSetActive(g_solver, g_buffers->activeIndices.buffer, ©Desc); NvFlexSetActiveCount(g_solver, numParticles); // springs if (g_buffers->springIndices.size()) { assert((g_buffers->springIndices.size() & 1) == 0); assert((g_buffers->springIndices.size() / 2) == g_buffers->springLengths.size()); NvFlexSetSprings(g_solver, g_buffers->springIndices.buffer, g_buffers->springLengths.buffer, g_buffers->springStiffness.buffer, g_buffers->springLengths.size()); } // rigids if (g_buffers->rigidOffsets.size()) { NvFlexSetRigids(g_solver, g_buffers->rigidOffsets.buffer, g_buffers->rigidIndices.buffer, g_buffers->rigidLocalPositions.buffer, g_buffers->rigidLocalNormals.buffer, g_buffers->rigidCoefficients.buffer, g_buffers->rigidPlasticThresholds.buffer, g_buffers->rigidPlasticCreeps.buffer, g_buffers->rigidRotations.buffer, g_buffers->rigidTranslations.buffer, g_buffers->rigidOffsets.size() - 1, g_buffers->rigidIndices.size()); } // inflatables if (g_buffers->inflatableTriOffsets.size()) { NvFlexSetInflatables(g_solver, g_buffers->inflatableTriOffsets.buffer, g_buffers->inflatableTriCounts.buffer, g_buffers->inflatableVolumes.buffer, g_buffers->inflatablePressures.buffer, g_buffers->inflatableCoefficients.buffer, g_buffers->inflatableTriOffsets.size()); } // dynamic triangles if (g_buffers->triangles.size()) { NvFlexSetDynamicTriangles(g_solver, g_buffers->triangles.buffer, g_buffers->triangleNormals.buffer, g_buffers->triangles.size() / 3); } // collision shapes if (g_buffers->shapeFlags.size()) { NvFlexSetShapes( g_solver, g_buffers->shapeGeometry.buffer, g_buffers->shapePositions.buffer, g_buffers->shapeRotations.buffer, g_buffers->shapePrevPositions.buffer, g_buffers->shapePrevRotations.buffer, g_buffers->shapeFlags.buffer, int(g_buffers->shapeFlags.size())); } // create render buffers g_fluidRenderBuffers = CreateFluidRenderBuffers(maxParticles, g_interop); g_diffuseRenderBuffers = CreateDiffuseRenderBuffers(g_maxDiffuseParticles, g_interop); // perform initial sim warm up if (g_warmup) { printf("Warming up sim..\n"); // warm it up (relax positions to reach rest density without affecting velocity) NvFlexParams copy = g_params; copy.numIterations = 4; NvFlexSetParams(g_solver, ©); const int kWarmupIterations = 100; for (int i = 0; i < kWarmupIterations; ++i) { NvFlexUpdateSolver(g_solver, 0.0001f, 1, false); NvFlexSetVelocities(g_solver, g_buffers->velocities.buffer, NULL); } // udpate host copy NvFlexGetParticles(g_solver, g_buffers->positions.buffer, NULL); NvFlexGetSmoothParticles(g_solver, g_buffers->smoothPositions.buffer, NULL); NvFlexGetAnisotropy(g_solver, g_buffers->anisotropy1.buffer, g_buffers->anisotropy2.buffer, g_buffers->anisotropy3.buffer, NULL); printf("Finished warm up.\n"); } } void Reset() { Init(g_scene, false); } void Shutdown() { // free buffers DestroyBuffers(g_buffers); for (auto& iter : g_meshes) { NvFlexDestroyTriangleMesh(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } for (auto& iter : g_fields) { NvFlexDestroyDistanceField(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } for (auto& iter : g_convexes) { NvFlexDestroyConvexMesh(g_flexLib, iter.first); DestroyGpuMesh(iter.second); } g_fields.clear(); g_meshes.clear(); NvFlexDestroySolver(g_solver); NvFlexShutdown(g_flexLib); #if _WIN32 if (g_ffmpeg) _pclose(g_ffmpeg); #endif } void UpdateEmitters() { float spin = DegToRad(15.0f); const Vec3 forward(-sinf(g_camAngle.x + spin)*cosf(g_camAngle.y), sinf(g_camAngle.y), -cosf(g_camAngle.x + spin)*cosf(g_camAngle.y)); const Vec3 right(Normalize(Cross(forward, Vec3(0.0f, 1.0f, 0.0f)))); g_emitters[0].mDir = Normalize(forward + Vec3(0.0, 0.4f, 0.0f)); g_emitters[0].mRight = right; g_emitters[0].mPos = g_camPos + forward*1.f + Vec3(0.0f, 0.2f, 0.0f) + right*0.65f; // process emitters if (g_emit) { int activeCount = NvFlexGetActiveCount(g_solver); size_t e = 0; // skip camera emitter when moving forward or things get messy if (g_camSmoothVel.z >= 0.025f) e = 1; for (; e < g_emitters.size(); ++e) { if (!g_emitters[e].mEnabled) continue; Vec3 emitterDir = g_emitters[e].mDir; Vec3 emitterRight = g_emitters[e].mRight; Vec3 emitterPos = g_emitters[e].mPos; float r = g_params.fluidRestDistance; int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid); float numParticles = (g_emitters[e].mSpeed / r)*g_dt; // whole number to emit int n = int(numParticles + g_emitters[e].mLeftOver); if (n) g_emitters[e].mLeftOver = (numParticles + g_emitters[e].mLeftOver) - n; else g_emitters[e].mLeftOver += numParticles; // create a grid of particles (n particles thick) for (int k = 0; k < n; ++k) { int emitterWidth = g_emitters[e].mWidth; int numParticles = emitterWidth*emitterWidth; for (int i = 0; i < numParticles; ++i) { float x = float(i%emitterWidth) - float(emitterWidth/2); float y = float((i / emitterWidth) % emitterWidth) - float(emitterWidth/2); if ((sqr(x) + sqr(y)) <= (emitterWidth / 2)*(emitterWidth / 2)) { Vec3 up = Normalize(Cross(emitterDir, emitterRight)); Vec3 offset = r*(emitterRight*x + up*y) + float(k)*emitterDir*r; if (activeCount < g_buffers->positions.size()) { g_buffers->positions[activeCount] = Vec4(emitterPos + offset, 1.0f); g_buffers->velocities[activeCount] = emitterDir*g_emitters[e].mSpeed; g_buffers->phases[activeCount] = phase; g_buffers->activeIndices.push_back(activeCount); activeCount++; } } } } } } } void UpdateCamera() { Vec3 forward(-sinf(g_camAngle.x)*cosf(g_camAngle.y), sinf(g_camAngle.y), -cosf(g_camAngle.x)*cosf(g_camAngle.y)); Vec3 right(Normalize(Cross(forward, Vec3(0.0f, 1.0f, 0.0f)))); g_camSmoothVel = Lerp(g_camSmoothVel, g_camVel, 0.1f); g_camPos += (forward*g_camSmoothVel.z + right*g_camSmoothVel.x + Cross(right, forward)*g_camSmoothVel.y); } void UpdateMouse() { // mouse button is up release particle if (g_lastb == -1) { if (g_mouseParticle != -1) { // restore particle mass g_buffers->positions[g_mouseParticle].w = g_mouseMass; // deselect g_mouseParticle = -1; } } // mouse went down, pick new particle if (g_mousePicked) { assert(g_mouseParticle == -1); Vec3 origin, dir; GetViewRay(g_lastx, g_screenHeight - g_lasty, origin, dir); const int numActive = NvFlexGetActiveCount(g_solver); g_mouseParticle = PickParticle(origin, dir, &g_buffers->positions[0], &g_buffers->phases[0], numActive, g_params.radius*0.8f, g_mouseT); if (g_mouseParticle != -1) { printf("picked: %d, mass: %f v: %f %f %f\n", g_mouseParticle, g_buffers->positions[g_mouseParticle].w, g_buffers->velocities[g_mouseParticle].x, g_buffers->velocities[g_mouseParticle].y, g_buffers->velocities[g_mouseParticle].z); g_mousePos = origin + dir*g_mouseT; g_mouseMass = g_buffers->positions[g_mouseParticle].w; g_buffers->positions[g_mouseParticle].w = 0.0f; // increase picked particle's mass to force it towards the point } g_mousePicked = false; } // update picked particle position if (g_mouseParticle != -1) { Vec3 p = Lerp(Vec3(g_buffers->positions[g_mouseParticle]), g_mousePos, 0.8f); Vec3 delta = p - Vec3(g_buffers->positions[g_mouseParticle]); g_buffers->positions[g_mouseParticle].x = p.x; g_buffers->positions[g_mouseParticle].y = p.y; g_buffers->positions[g_mouseParticle].z = p.z; g_buffers->velocities[g_mouseParticle].x = delta.x / g_dt; g_buffers->velocities[g_mouseParticle].y = delta.y / g_dt; g_buffers->velocities[g_mouseParticle].z = delta.z / g_dt; } } void UpdateWind() { g_windTime += g_dt; const Vec3 kWindDir = Vec3(3.0f, 15.0f, 0.0f); const float kNoise = Perlin1D(g_windTime*g_windFrequency, 10, 0.25f); Vec3 wind = g_windStrength*kWindDir*Vec3(kNoise, fabsf(kNoise), 0.0f); g_params.wind[0] = wind.x; g_params.wind[1] = wind.y; g_params.wind[2] = wind.z; if (g_wavePool) { g_waveTime += g_dt; g_params.planes[2][3] = g_wavePlane + (sinf(float(g_waveTime)*g_waveFrequency - kPi*0.5f)*0.5f + 0.5f)*g_waveAmplitude; } } void SyncScene() { // let the scene send updates to flex directly g_scenes[g_scene]->Sync(); } void UpdateScene() { // give scene a chance to make changes to particle buffers g_scenes[g_scene]->Update(); } void RenderScene() { const int numParticles = NvFlexGetActiveCount(g_solver); const int numDiffuse = g_buffers->diffuseCount[0]; //--------------------------------------------------- // use VBO buffer wrappers to allow Flex to write directly to the OpenGL buffers // Flex will take care of any CUDA interop mapping/unmapping during the get() operations if (numParticles) { if (g_interop) { // copy data directly from solver to the renderer buffers UpdateFluidRenderBuffers(g_fluidRenderBuffers, g_solver, g_drawEllipsoids, g_drawDensity); } else { // copy particle data to GPU render device if (g_drawEllipsoids) { // if fluid surface rendering then update with smooth positions and anisotropy UpdateFluidRenderBuffers(g_fluidRenderBuffers, &g_buffers->smoothPositions[0], (g_drawDensity) ? &g_buffers->densities[0] : (float*)&g_buffers->phases[0], &g_buffers->anisotropy1[0], &g_buffers->anisotropy2[0], &g_buffers->anisotropy3[0], g_buffers->positions.size(), &g_buffers->activeIndices[0], numParticles); } else { // otherwise just send regular positions and no anisotropy UpdateFluidRenderBuffers(g_fluidRenderBuffers, &g_buffers->positions[0], (float*)&g_buffers->phases[0], NULL, NULL, NULL, g_buffers->positions.size(), &g_buffers->activeIndices[0], numParticles); } } } // GPU Render time doesn't include CPU->GPU copy time GraphicsTimerBegin(); if (numDiffuse) { if (g_interop) { // copy data directly from solver to the renderer buffers UpdateDiffuseRenderBuffers(g_diffuseRenderBuffers, g_solver); } else { // copy diffuse particle data from host to GPU render device UpdateDiffuseRenderBuffers(g_diffuseRenderBuffers, &g_buffers->diffusePositions[0], &g_buffers->diffuseVelocities[0], numDiffuse); } } //--------------------------------------- // setup view and state float fov = kPi / 4.0f; float aspect = float(g_screenWidth) / g_screenHeight; Matrix44 proj = ProjectionMatrix(RadToDeg(fov), aspect, g_camNear, g_camFar); Matrix44 view = RotationMatrix(-g_camAngle.x, Vec3(0.0f, 1.0f, 0.0f))*RotationMatrix(-g_camAngle.y, Vec3(cosf(-g_camAngle.x), 0.0f, sinf(-g_camAngle.x)))*TranslationMatrix(-Point3(g_camPos)); //------------------------------------ // lighting pass // expand scene bounds to fit most scenes g_sceneLower = Min(g_sceneLower, Vec3(-2.0f, 0.0f, -2.0f)); g_sceneUpper = Max(g_sceneUpper, Vec3(2.0f, 2.0f, 2.0f)); Vec3 sceneExtents = g_sceneUpper - g_sceneLower; Vec3 sceneCenter = 0.5f*(g_sceneUpper + g_sceneLower); g_lightDir = Normalize(Vec3(5.0f, 15.0f, 7.5f)); g_lightPos = sceneCenter + g_lightDir*Length(sceneExtents)*g_lightDistance; g_lightTarget = sceneCenter; // calculate tight bounds for shadow frustum float lightFov = 2.0f*atanf(Length(g_sceneUpper - sceneCenter) / Length(g_lightPos - sceneCenter)); // scale and clamp fov for aesthetics lightFov = Clamp(lightFov, DegToRad(25.0f), DegToRad(65.0f)); Matrix44 lightPerspective = ProjectionMatrix(RadToDeg(lightFov), 1.0f, 1.0f, 1000.0f); Matrix44 lightView = LookAtMatrix(Point3(g_lightPos), Point3(g_lightTarget)); Matrix44 lightTransform = lightPerspective*lightView; // radius used for drawing float radius = Max(g_params.solidRestDistance, g_params.fluidRestDistance)*0.5f*g_pointScale; //------------------------------------- // shadowing pass if (g_meshSkinIndices.size()) SkinMesh(); // create shadow maps ShadowBegin(g_shadowMap); SetView(lightView, lightPerspective); SetCullMode(false); // give scene a chance to do custom drawing g_scenes[g_scene]->Draw(1); if (g_drawMesh) DrawMesh(g_mesh, g_meshColor); DrawShapes(); if (g_drawCloth && g_buffers->triangles.size()) { DrawCloth(&g_buffers->positions[0], &g_buffers->normals[0], g_buffers->uvs.size() ? &g_buffers->uvs[0].x : NULL, &g_buffers->triangles[0], g_buffers->triangles.size() / 3, g_buffers->positions.size(), 3, g_expandCloth); } if (g_drawRopes) { for (size_t i = 0; i < g_ropes.size(); ++i) DrawRope(&g_buffers->positions[0], &g_ropes[i].mIndices[0], g_ropes[i].mIndices.size(), radius*g_ropeScale, i); } int shadowParticles = numParticles; int shadowParticlesOffset = 0; if (!g_drawPoints) { shadowParticles = 0; if (g_drawEllipsoids) { shadowParticles = numParticles - g_numSolidParticles; shadowParticlesOffset = g_numSolidParticles; } } else { int offset = g_drawMesh ? g_numSolidParticles : 0; shadowParticles = numParticles - offset; shadowParticlesOffset = offset; } if (g_buffers->activeIndices.size()) DrawPoints(g_fluidRenderBuffers, shadowParticles, shadowParticlesOffset, radius, 2048, 1.0f, lightFov, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_drawDensity); ShadowEnd(); //---------------- // lighting pass BindSolidShader(g_lightPos, g_lightTarget, lightTransform, g_shadowMap, 0.0f, Vec4(g_clearColor, g_fogDistance)); SetView(view, proj); SetCullMode(true); // When the benchmark measures async compute, we need a graphics workload that runs for a whole frame. // We do this by rerendering our simple graphics many times. int passes = g_increaseGfxLoadForAsyncComputeTesting ? 50 : 1; for (int i = 0; i != passes; i++) { DrawPlanes((Vec4*)g_params.planes, g_params.numPlanes, g_drawPlaneBias); if (g_drawMesh) DrawMesh(g_mesh, g_meshColor); DrawShapes(); if (g_drawCloth && g_buffers->triangles.size()) DrawCloth(&g_buffers->positions[0], &g_buffers->normals[0], g_buffers->uvs.size() ? &g_buffers->uvs[0].x : NULL, &g_buffers->triangles[0], g_buffers->triangles.size() / 3, g_buffers->positions.size(), 3, g_expandCloth); if (g_drawRopes) { for (size_t i = 0; i < g_ropes.size(); ++i) DrawRope(&g_buffers->positions[0], &g_ropes[i].mIndices[0], g_ropes[i].mIndices.size(), g_params.radius*0.5f*g_ropeScale, i); } // give scene a chance to do custom drawing g_scenes[g_scene]->Draw(0); } UnbindSolidShader(); // first pass of diffuse particles (behind fluid surface) if (g_drawDiffuse) RenderDiffuse(g_fluidRenderer, g_diffuseRenderBuffers, numDiffuse, radius*g_diffuseScale, float(g_screenWidth), aspect, fov, g_diffuseColor, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_diffuseMotionScale, g_diffuseInscatter, g_diffuseOutscatter, g_diffuseShadow, false); if (g_drawEllipsoids) { // draw solid particles separately if (g_numSolidParticles && g_drawPoints) DrawPoints(g_fluidRenderBuffers, g_numSolidParticles, 0, radius, float(g_screenWidth), aspect, fov, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_drawDensity); // render fluid surface RenderEllipsoids(g_fluidRenderer, g_fluidRenderBuffers, numParticles - g_numSolidParticles, g_numSolidParticles, radius, float(g_screenWidth), aspect, fov, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_fluidColor, g_blur, g_ior, g_drawOpaque); // second pass of diffuse particles for particles in front of fluid surface if (g_drawDiffuse) RenderDiffuse(g_fluidRenderer, g_diffuseRenderBuffers, numDiffuse, radius*g_diffuseScale, float(g_screenWidth), aspect, fov, g_diffuseColor, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_diffuseMotionScale, g_diffuseInscatter, g_diffuseOutscatter, g_diffuseShadow, true); } else { // draw all particles as spheres if (g_drawPoints) { int offset = g_drawMesh ? g_numSolidParticles : 0; if (g_buffers->activeIndices.size()) DrawPoints(g_fluidRenderBuffers, numParticles - offset, offset, radius, float(g_screenWidth), aspect, fov, g_lightPos, g_lightTarget, lightTransform, g_shadowMap, g_drawDensity); } } GraphicsTimerEnd(); } void RenderDebug() { if (g_mouseParticle != -1) { // draw mouse spring BeginLines(); DrawLine(g_mousePos, Vec3(g_buffers->positions[g_mouseParticle]), Vec4(1.0f)); EndLines(); } // springs if (g_drawSprings) { Vec4 color; if (g_drawSprings == 1) { // stretch color = Vec4(0.0f, 0.0f, 1.0f, 0.8f); } if (g_drawSprings == 2) { // tether color = Vec4(0.0f, 1.0f, 0.0f, 0.8f); } BeginLines(); int start = 0; for (int i = start; i < g_buffers->springLengths.size(); ++i) { if (g_drawSprings == 1 && g_buffers->springStiffness[i] < 0.0f) continue; if (g_drawSprings == 2 && g_buffers->springStiffness[i] > 0.0f) continue; int a = g_buffers->springIndices[i * 2]; int b = g_buffers->springIndices[i * 2 + 1]; DrawLine(Vec3(g_buffers->positions[a]), Vec3(g_buffers->positions[b]), color); } EndLines(); } // visualize contacts against the environment if (g_drawContacts) { const int maxContactsPerParticle = 6; NvFlexVector contactPlanes(g_flexLib, g_buffers->positions.size()*maxContactsPerParticle); NvFlexVector contactVelocities(g_flexLib, g_buffers->positions.size()*maxContactsPerParticle); NvFlexVector contactIndices(g_flexLib, g_buffers->positions.size()); NvFlexVector contactCounts(g_flexLib, g_buffers->positions.size()); NvFlexGetContacts(g_solver, contactPlanes.buffer, contactVelocities.buffer, contactIndices.buffer, contactCounts.buffer); // ensure transfers have finished contactPlanes.map(); contactVelocities.map(); contactIndices.map(); contactCounts.map(); BeginLines(); for (int i = 0; i < int(g_buffers->activeIndices.size()); ++i) { const int contactIndex = contactIndices[g_buffers->activeIndices[i]]; const unsigned int count = contactCounts[contactIndex]; const float scale = 0.1f; for (unsigned int c = 0; c < count; ++c) { Vec4 plane = contactPlanes[contactIndex*maxContactsPerParticle + c]; DrawLine(Vec3(g_buffers->positions[g_buffers->activeIndices[i]]), Vec3(g_buffers->positions[g_buffers->activeIndices[i]]) + Vec3(plane)*scale, Vec4(0.0f, 1.0f, 0.0f, 0.0f)); } } EndLines(); } if (g_drawBases) { for (int i = 0; i < int(g_buffers->rigidRotations.size()); ++i) { BeginLines(); float size = 0.1f; for (int b = 0; b < 3; ++b) { Vec3 color; color[b] = 1.0f; Matrix33 frame(g_buffers->rigidRotations[i]); DrawLine(Vec3(g_buffers->rigidTranslations[i]), Vec3(g_buffers->rigidTranslations[i] + frame.cols[b] * size), Vec4(color, 0.0f)); } EndLines(); } } if (g_drawNormals) { NvFlexGetNormals(g_solver, g_buffers->normals.buffer, NULL); BeginLines(); for (int i = 0; i < g_buffers->normals.size(); ++i) { DrawLine(Vec3(g_buffers->positions[i]), Vec3(g_buffers->positions[i] - g_buffers->normals[i] * g_buffers->normals[i].w), Vec4(0.0f, 1.0f, 0.0f, 0.0f)); } EndLines(); } } void DrawShapes() { for (int i = 0; i < g_buffers->shapeFlags.size(); ++i) { const int flags = g_buffers->shapeFlags[i]; // unpack flags int type = int(flags&eNvFlexShapeFlagTypeMask); //bool dynamic = int(flags&eNvFlexShapeFlagDynamic) > 0; Vec3 color = Vec3(0.9f); if (flags & eNvFlexShapeFlagTrigger) { color = Vec3(0.6f, 1.0, 0.6f); SetFillMode(true); } // render with prev positions to match particle update order // can also think of this as current/next const Quat rotation = g_buffers->shapePrevRotations[i]; const Vec3 position = Vec3(g_buffers->shapePrevPositions[i]); NvFlexCollisionGeometry geo = g_buffers->shapeGeometry[i]; if (type == eNvFlexShapeSphere) { Mesh* sphere = CreateSphere(20, 20, geo.sphere.radius); Matrix44 xform = TranslationMatrix(Point3(position))*RotationMatrix(Quat(rotation)); sphere->Transform(xform); DrawMesh(sphere, Vec3(color)); delete sphere; } else if (type == eNvFlexShapeCapsule) { Mesh* capsule = CreateCapsule(10, 20, geo.capsule.radius, geo.capsule.halfHeight); // transform to world space Matrix44 xform = TranslationMatrix(Point3(position))*RotationMatrix(Quat(rotation))*RotationMatrix(DegToRad(-90.0f), Vec3(0.0f, 0.0f, 1.0f)); capsule->Transform(xform); DrawMesh(capsule, Vec3(color)); delete capsule; } else if (type == eNvFlexShapeBox) { Mesh* box = CreateCubeMesh(); Matrix44 xform = TranslationMatrix(Point3(position))*RotationMatrix(Quat(rotation))*ScaleMatrix(Vec3(geo.box.halfExtents)*2.0f); box->Transform(xform); DrawMesh(box, Vec3(color)); delete box; } else if (type == eNvFlexShapeConvexMesh) { if (g_convexes.find(geo.convexMesh.mesh) != g_convexes.end()) { GpuMesh* m = g_convexes[geo.convexMesh.mesh]; if (m) { Matrix44 xform = TranslationMatrix(Point3(g_buffers->shapePositions[i]))*RotationMatrix(Quat(g_buffers->shapeRotations[i]))*ScaleMatrix(geo.convexMesh.scale); DrawGpuMesh(m, xform, Vec3(color)); } } } else if (type == eNvFlexShapeTriangleMesh) { if (g_meshes.find(geo.triMesh.mesh) != g_meshes.end()) { GpuMesh* m = g_meshes[geo.triMesh.mesh]; if (m) { Matrix44 xform = TranslationMatrix(Point3(position))*RotationMatrix(Quat(rotation))*ScaleMatrix(geo.triMesh.scale); DrawGpuMesh(m, xform, Vec3(color)); } } } else if (type == eNvFlexShapeSDF) { if (g_fields.find(geo.sdf.field) != g_fields.end()) { GpuMesh* m = g_fields[geo.sdf.field]; if (m) { Matrix44 xform = TranslationMatrix(Point3(position))*RotationMatrix(Quat(rotation))*ScaleMatrix(geo.sdf.scale); DrawGpuMesh(m, xform, Vec3(color)); } } } } SetFillMode(g_wireframe); } // returns the new scene if one is selected int DoUI() { // gui may set a new scene int newScene = -1; if (g_showHelp) { const int numParticles = NvFlexGetActiveCount(g_solver); const int numDiffuse = g_buffers->diffuseCount[0]; int x = g_screenWidth - 200; int y = g_screenHeight - 23; // imgui unsigned char button = 0; if (g_lastb == SDL_BUTTON_LEFT) button = IMGUI_MBUT_LEFT; else if (g_lastb == SDL_BUTTON_RIGHT) button = IMGUI_MBUT_RIGHT; imguiBeginFrame(g_lastx, g_screenHeight - g_lasty, button, 0); x += 180; int fontHeight = 13; if (1) { DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Frame: %d", g_frame); y -= fontHeight * 2; if (!g_ffmpeg) { DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Frame Time: %.2fms", g_realdt*1000.0f); y -= fontHeight * 2; // If detailed profiling is enabled, then these timers will contain the overhead of the detail timers, so we won't display them. if (!g_profile) { DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Sim Time (CPU): %.2fms", g_updateTime*1000.0f); y -= fontHeight; DrawImguiString(x, y, Vec3(0.97f, 0.59f, 0.27f), IMGUI_ALIGN_RIGHT, "Sim Latency (GPU): %.2fms", g_simLatency); y -= fontHeight * 2; BenchmarkUpdateGraph(); } else { y -= fontHeight * 3; } } DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Particle Count: %d", numParticles); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Diffuse Count: %d", numDiffuse); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Rigid Count: %d", g_buffers->rigidOffsets.size() > 0 ? g_buffers->rigidOffsets.size() - 1 : 0); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Spring Count: %d", g_buffers->springLengths.size()); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Num Substeps: %d", g_numSubsteps); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Num Iterations: %d", g_params.numIterations); y -= fontHeight * 2; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Device: %s", g_deviceName); y -= fontHeight * 2; } if (g_profile) { DrawImguiString(x, y, Vec3(0.97f, 0.59f, 0.27f), IMGUI_ALIGN_RIGHT, "Total GPU Sim Latency: %.2fms", g_timers.total); y -= fontHeight * 2; DrawImguiString(x, y, Vec3(0.0f, 1.0f, 0.0f), IMGUI_ALIGN_RIGHT, "GPU Latencies"); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Predict: %.2fms", g_timers.predict); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Create Cell Indices: %.2fms", g_timers.createCellIndices); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Sort Cell Indices: %.2fms", g_timers.sortCellIndices); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Reorder: %.2fms", g_timers.reorder); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "CreateGrid: %.2fms", g_timers.createGrid); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Collide Particles: %.2fms", g_timers.collideParticles); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Collide Shapes: %.2fms", g_timers.collideShapes); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Collide Triangles: %.2fms", g_timers.collideTriangles); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Calculate Density: %.2fms", g_timers.calculateDensity); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Densities: %.2fms", g_timers.solveDensities); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Velocities: %.2fms", g_timers.solveVelocities); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Rigids: %.2fms", g_timers.solveShapes); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Springs: %.2fms", g_timers.solveSprings); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Inflatables: %.2fms", g_timers.solveInflatables); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Solve Contacts: %.2fms", g_timers.solveContacts); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Apply Deltas: %.2fms", g_timers.applyDeltas); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Finalize: %.2fms", g_timers.finalize); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Update Triangles: %.2fms", g_timers.updateTriangles); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Update Normals: %.2fms", g_timers.updateNormals); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Update Bounds: %.2fms", g_timers.updateBounds); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Calculate Anisotropy: %.2fms", g_timers.calculateAnisotropy); y -= fontHeight; DrawImguiString(x, y, Vec3(1.0f), IMGUI_ALIGN_RIGHT, "Update Diffuse: %.2fms", g_timers.updateDiffuse); y -= fontHeight * 2; } x -= 180; int uiOffset = 250; int uiBorder = 20; int uiWidth = 200; int uiHeight = g_screenHeight - uiOffset - uiBorder * 3; int uiLeft = uiBorder; if (g_tweakPanel) imguiBeginScrollArea("Scene", uiLeft, g_screenHeight - uiBorder - uiOffset, uiWidth, uiOffset, &g_levelScroll); else imguiBeginScrollArea("Scene", uiLeft, uiBorder, uiWidth, g_screenHeight - uiBorder - uiBorder, &g_levelScroll); for (int i = 0; i < int(g_scenes.size()); ++i) { unsigned int color = g_scene == i ? imguiRGBA(255, 151, 61, 255) : imguiRGBA(255, 255, 255, 200); if (imguiItem(g_scenes[i]->GetName(), true, color)) // , i == g_selectedScene)) { newScene = i; } } imguiEndScrollArea(); if (g_tweakPanel) { static int scroll = 0; imguiBeginScrollArea("Options", uiLeft, g_screenHeight - uiBorder - uiHeight - uiOffset - uiBorder, uiWidth, uiHeight, &scroll); imguiSeparatorLine(); // global options imguiLabel("Global"); if (imguiCheck("Emit particles", g_emit)) g_emit = !g_emit; if (imguiCheck("Pause", g_pause)) g_pause = !g_pause; imguiSeparatorLine(); if (imguiCheck("Wireframe", g_wireframe)) g_wireframe = !g_wireframe; if (imguiCheck("Draw Points", g_drawPoints)) g_drawPoints = !g_drawPoints; if (imguiCheck("Draw Fluid", g_drawEllipsoids)) g_drawEllipsoids = !g_drawEllipsoids; if (imguiCheck("Draw Mesh", g_drawMesh)) { g_drawMesh = !g_drawMesh; g_drawRopes = !g_drawRopes; } if (imguiCheck("Draw Basis", g_drawBases)) g_drawBases = !g_drawBases; if (imguiCheck("Draw Springs", bool(g_drawSprings != 0))) g_drawSprings = (g_drawSprings) ? 0 : 1; if (imguiCheck("Draw Contacts", g_drawContacts)) g_drawContacts = !g_drawContacts; imguiSeparatorLine(); // scene options g_scenes[g_scene]->DoGui(); if (imguiButton("Reset Scene")) g_resetScene = true; imguiSeparatorLine(); float n = float(g_numSubsteps); if (imguiSlider("Num Substeps", &n, 1, 10, 1)) g_numSubsteps = int(n); n = float(g_params.numIterations); if (imguiSlider("Num Iterations", &n, 1, 20, 1)) g_params.numIterations = int(n); imguiSeparatorLine(); imguiSlider("Gravity X", &g_params.gravity[0], -50.0f, 50.0f, 1.0f); imguiSlider("Gravity Y", &g_params.gravity[1], -50.0f, 50.0f, 1.0f); imguiSlider("Gravity Z", &g_params.gravity[2], -50.0f, 50.0f, 1.0f); imguiSeparatorLine(); imguiSlider("Radius", &g_params.radius, 0.01f, 0.5f, 0.01f); imguiSlider("Solid Radius", &g_params.solidRestDistance, 0.0f, 0.5f, 0.001f); imguiSlider("Fluid Radius", &g_params.fluidRestDistance, 0.0f, 0.5f, 0.001f); // common params imguiSeparatorLine(); imguiSlider("Dynamic Friction", &g_params.dynamicFriction, 0.0f, 1.0f, 0.01f); imguiSlider("Static Friction", &g_params.staticFriction, 0.0f, 1.0f, 0.01f); imguiSlider("Particle Friction", &g_params.particleFriction, 0.0f, 1.0f, 0.01f); imguiSlider("Restitution", &g_params.restitution, 0.0f, 1.0f, 0.01f); imguiSlider("SleepThreshold", &g_params.sleepThreshold, 0.0f, 1.0f, 0.01f); imguiSlider("Shock Propagation", &g_params.shockPropagation, 0.0f, 10.0f, 0.01f); imguiSlider("Damping", &g_params.damping, 0.0f, 10.0f, 0.01f); imguiSlider("Dissipation", &g_params.dissipation, 0.0f, 0.01f, 0.0001f); imguiSlider("SOR", &g_params.relaxationFactor, 0.0f, 5.0f, 0.01f); imguiSlider("Collision Distance", &g_params.collisionDistance, 0.0f, 0.5f, 0.001f); imguiSlider("Collision Margin", &g_params.shapeCollisionMargin, 0.0f, 5.0f, 0.01f); // cloth params imguiSeparatorLine(); imguiSlider("Wind", &g_windStrength, -1.0f, 1.0f, 0.01f); imguiSlider("Drag", &g_params.drag, 0.0f, 1.0f, 0.01f); imguiSlider("Lift", &g_params.lift, 0.0f, 1.0f, 0.01f); imguiSeparatorLine(); // fluid params imguiSlider("Adhesion", &g_params.adhesion, 0.0f, 10.0f, 0.01f); imguiSlider("Cohesion", &g_params.cohesion, 0.0f, 0.2f, 0.0001f); imguiSlider("Surface Tension", &g_params.surfaceTension, 0.0f, 50.0f, 0.01f); imguiSlider("Viscosity", &g_params.viscosity, 0.0f, 120.0f, 0.01f); imguiSlider("Vorticicty Confinement", &g_params.vorticityConfinement, 0.0f, 120.0f, 0.1f); imguiSlider("Solid Pressure", &g_params.solidPressure, 0.0f, 1.0f, 0.01f); imguiSlider("Surface Drag", &g_params.freeSurfaceDrag, 0.0f, 1.0f, 0.01f); imguiSlider("Buoyancy", &g_params.buoyancy, -1.0f, 1.0f, 0.01f); imguiSeparatorLine(); imguiSlider("Anisotropy Scale", &g_params.anisotropyScale, 0.0f, 30.0f, 0.01f); imguiSlider("Smoothing", &g_params.smoothing, 0.0f, 1.0f, 0.01f); // diffuse params imguiSeparatorLine(); imguiSlider("Diffuse Threshold", &g_params.diffuseThreshold, 0.0f, 1000.0f, 1.0f); imguiSlider("Diffuse Buoyancy", &g_params.diffuseBuoyancy, 0.0f, 2.0f, 0.01f); imguiSlider("Diffuse Drag", &g_params.diffuseDrag, 0.0f, 2.0f, 0.01f); imguiSlider("Diffuse Scale", &g_diffuseScale, 0.0f, 1.5f, 0.01f); imguiSlider("Diffuse Alpha", &g_diffuseColor.w, 0.0f, 3.0f, 0.01f); imguiSlider("Diffuse Inscatter", &g_diffuseInscatter, 0.0f, 2.0f, 0.01f); imguiSlider("Diffuse Outscatter", &g_diffuseOutscatter, 0.0f, 2.0f, 0.01f); imguiSlider("Diffuse Motion Blur", &g_diffuseMotionScale, 0.0f, 5.0f, 0.1f); n = float(g_params.diffuseBallistic); if (imguiSlider("Diffuse Ballistic", &n, 1, 40, 1)) g_params.diffuseBallistic = int(n); imguiEndScrollArea(); } imguiEndFrame(); // kick render commands DrawImguiGraph(); } return newScene; } void UpdateFrame() { static double lastTime; // real elapsed frame time double frameBeginTime = GetSeconds(); g_realdt = float(frameBeginTime - lastTime); lastTime = frameBeginTime; // do gamepad input polling double currentTime = frameBeginTime; static double lastJoyTime = currentTime; if (g_gamecontroller && currentTime - lastJoyTime > g_dt) { lastJoyTime = currentTime; int leftStickX = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_LEFTX); int leftStickY = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_LEFTY); int rightStickX = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_RIGHTX); int rightStickY = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_RIGHTY); int leftTrigger = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); int rightTrigger = SDL_GameControllerGetAxis(g_gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); Vec2 leftStick(joyAxisFilter(leftStickX, 0), joyAxisFilter(leftStickY, 0)); Vec2 rightStick(joyAxisFilter(rightStickX, 1), joyAxisFilter(rightStickY, 1)); Vec2 trigger(leftTrigger / 32768.0f, rightTrigger / 32768.0f); if (leftStick.x != 0.0f || leftStick.y != 0.0f || rightStick.x != 0.0f || rightStick.y != 0.0f) { // note constant factor to speed up analog control compared to digital because it is more controllable. g_camVel.z = -4 * g_camSpeed * leftStick.y; g_camVel.x = 4 * g_camSpeed * leftStick.x; // cam orientation g_camAngle.x -= rightStick.x * 0.05f; g_camAngle.y -= rightStick.y * 0.05f; } // Handle left stick motion static bool bLeftStick = false; if ((leftStick.x != 0.0f || leftStick.y != 0.0f) && !bLeftStick) { bLeftStick = true; } else if ((leftStick.x == 0.0f && leftStick.y == 0.0f) && bLeftStick) { bLeftStick = false; g_camVel.z = -4 * g_camSpeed * leftStick.y; g_camVel.x = 4 * g_camSpeed * leftStick.x; } // Handle triggers as controller button events void ControllerButtonEvent(SDL_ControllerButtonEvent event); static bool bLeftTrigger = false; static bool bRightTrigger = false; SDL_ControllerButtonEvent e; if (!bLeftTrigger && trigger.x > 0.0f) { e.type = SDL_CONTROLLERBUTTONDOWN; e.button = SDL_CONTROLLER_BUTTON_LEFT_TRIGGER; ControllerButtonEvent(e); bLeftTrigger = true; } else if (bLeftTrigger && trigger.x == 0.0f) { e.type = SDL_CONTROLLERBUTTONUP; e.button = SDL_CONTROLLER_BUTTON_LEFT_TRIGGER; ControllerButtonEvent(e); bLeftTrigger = false; } if (!bRightTrigger && trigger.y > 0.0f) { e.type = SDL_CONTROLLERBUTTONDOWN; e.button = SDL_CONTROLLER_BUTTON_RIGHT_TRIGGER; ControllerButtonEvent(e); bRightTrigger = true; } else if (bRightTrigger && trigger.y == 0.0f) { e.type = SDL_CONTROLLERBUTTONDOWN; e.button = SDL_CONTROLLER_BUTTON_RIGHT_TRIGGER; ControllerButtonEvent(e); bRightTrigger = false; } } //------------------------------------------------------------------- // Scene Update double waitBeginTime = GetSeconds(); MapBuffers(g_buffers); double waitEndTime = GetSeconds(); // Getting timers causes CPU/GPU sync, so we do it after a map float newSimLatency = NvFlexGetDeviceLatency(g_solver, &g_GpuTimers.computeBegin, &g_GpuTimers.computeEnd, &g_GpuTimers.computeFreq); float newGfxLatency = RendererGetDeviceTimestamps(&g_GpuTimers.renderBegin, &g_GpuTimers.renderEnd, &g_GpuTimers.renderFreq); (void)newGfxLatency; UpdateCamera(); if (!g_pause || g_step) { UpdateEmitters(); UpdateMouse(); UpdateWind(); UpdateScene(); } //------------------------------------------------------------------- // Render double renderBeginTime = GetSeconds(); if (g_profile && (!g_pause || g_step)) { if (g_benchmark) { g_numDetailTimers = NvFlexGetDetailTimers(g_solver, &g_detailTimers); } else { memset(&g_timers, 0, sizeof(g_timers)); NvFlexGetTimers(g_solver, &g_timers); } } StartFrame(Vec4(g_clearColor, 1.0f)); // main scene render RenderScene(); RenderDebug(); int newScene = DoUI(); EndFrame(); // If user has disabled async compute, ensure that no compute can overlap // graphics by placing a sync between them if (!g_useAsyncCompute) NvFlexComputeWaitForGraphics(g_flexLib); UnmapBuffers(g_buffers); // move mouse particle (must be done here as GetViewRay() uses the GL projection state) if (g_mouseParticle != -1) { Vec3 origin, dir; GetViewRay(g_lastx, g_screenHeight - g_lasty, origin, dir); g_mousePos = origin + dir*g_mouseT; } if (g_capture) { TgaImage img; img.m_width = g_screenWidth; img.m_height = g_screenHeight; img.m_data = new uint32_t[g_screenWidth*g_screenHeight]; ReadFrame((int*)img.m_data, g_screenWidth, g_screenHeight); fwrite(img.m_data, sizeof(uint32_t)*g_screenWidth*g_screenHeight, 1, g_ffmpeg); delete[] img.m_data; } double renderEndTime = GetSeconds(); // if user requested a scene reset process it now if (g_resetScene) { Reset(); g_resetScene = false; } //------------------------------------------------------------------- // Flex Update double updateBeginTime = GetSeconds(); // send any particle updates to the solver NvFlexSetParticles(g_solver, g_buffers->positions.buffer, NULL); NvFlexSetVelocities(g_solver, g_buffers->velocities.buffer, NULL); NvFlexSetPhases(g_solver, g_buffers->phases.buffer, NULL); NvFlexSetActive(g_solver, g_buffers->activeIndices.buffer, NULL); NvFlexSetActiveCount(g_solver, g_buffers->activeIndices.size()); // allow scene to update constraints etc SyncScene(); if (g_shapesChanged) { NvFlexSetShapes( g_solver, g_buffers->shapeGeometry.buffer, g_buffers->shapePositions.buffer, g_buffers->shapeRotations.buffer, g_buffers->shapePrevPositions.buffer, g_buffers->shapePrevRotations.buffer, g_buffers->shapeFlags.buffer, int(g_buffers->shapeFlags.size())); g_shapesChanged = false; } if (!g_pause || g_step) { // tick solver NvFlexSetParams(g_solver, &g_params); NvFlexUpdateSolver(g_solver, g_dt, g_numSubsteps, g_profile); g_frame++; g_step = false; } // read back base particle data // Note that flexGet calls don't wait for the GPU, they just queue a GPU copy // to be executed later. // When we're ready to read the fetched buffers we'll Map them, and that's when // the CPU will wait for the GPU flex update and GPU copy to finish. NvFlexGetParticles(g_solver, g_buffers->positions.buffer, NULL); NvFlexGetVelocities(g_solver, g_buffers->velocities.buffer, NULL); NvFlexGetNormals(g_solver, g_buffers->normals.buffer, NULL); // readback triangle normals if (g_buffers->triangles.size()) NvFlexGetDynamicTriangles(g_solver, g_buffers->triangles.buffer, g_buffers->triangleNormals.buffer, g_buffers->triangles.size() / 3); // readback rigid transforms if (g_buffers->rigidOffsets.size()) NvFlexGetRigids(g_solver, NULL, NULL, NULL, NULL, NULL, NULL, NULL, g_buffers->rigidRotations.buffer, g_buffers->rigidTranslations.buffer); if (!g_interop) { // if not using interop then we read back fluid data to host if (g_drawEllipsoids) { NvFlexGetSmoothParticles(g_solver, g_buffers->smoothPositions.buffer, NULL); NvFlexGetAnisotropy(g_solver, g_buffers->anisotropy1.buffer, g_buffers->anisotropy2.buffer, g_buffers->anisotropy3.buffer, NULL); } // read back diffuse data to host if (g_drawDensity) NvFlexGetDensities(g_solver, g_buffers->densities.buffer, NULL); if (GetNumDiffuseRenderParticles(g_diffuseRenderBuffers)) { NvFlexGetDiffuseParticles(g_solver, g_buffers->diffusePositions.buffer, g_buffers->diffuseVelocities.buffer, g_buffers->diffuseCount.buffer); } } else { // read back just the new diffuse particle count, render buffers will be updated during rendering NvFlexGetDiffuseParticles(g_solver, NULL, NULL, g_buffers->diffuseCount.buffer); } double updateEndTime = GetSeconds(); //------------------------------------------------------- // Update the on-screen timers float newUpdateTime = float(updateEndTime - updateBeginTime); float newRenderTime = float(renderEndTime - renderBeginTime); float newWaitTime = float(waitBeginTime - waitEndTime); // Exponential filter to make the display easier to read const float timerSmoothing = 0.05f; g_updateTime = (g_updateTime == 0.0f) ? newUpdateTime : Lerp(g_updateTime, newUpdateTime, timerSmoothing); g_renderTime = (g_renderTime == 0.0f) ? newRenderTime : Lerp(g_renderTime, newRenderTime, timerSmoothing); g_waitTime = (g_waitTime == 0.0f) ? newWaitTime : Lerp(g_waitTime, newWaitTime, timerSmoothing); g_simLatency = (g_simLatency == 0.0f) ? newSimLatency : Lerp(g_simLatency, newSimLatency, timerSmoothing); if (g_benchmark) newScene = BenchmarkUpdate(); // flush out the last frame before freeing up resources in the event of a scene change // this is necessary for d3d12 PresentFrame(g_vsync); // if gui or benchmark requested a scene change process it now if (newScene != -1) { g_scene = newScene; Init(g_scene); } } #if ENABLE_AFTERMATH_SUPPORT void DumpAftermathData() { GFSDK_Aftermath_ContextData dataOut; GFSDK_Aftermath_Status statusOut; NvFlexGetDataAftermath(g_flexLib, &dataOut, &statusOut); wprintf(L"Last Aftermath event: %s\n", (wchar_t *)dataOut.markerData); } #endif void ReshapeWindow(int width, int height) { if (!g_benchmark) printf("Reshaping\n"); ReshapeRender(g_window); if (!g_fluidRenderer || (width != g_screenWidth || height != g_screenHeight)) { if (g_fluidRenderer) DestroyFluidRenderer(g_fluidRenderer); g_fluidRenderer = CreateFluidRenderer(width, height); } g_screenWidth = width; g_screenHeight = height; } void InputArrowKeysDown(int key, int x, int y) { switch (key) { case SDLK_DOWN: { if (g_selectedScene < int(g_scenes.size()) - 1) g_selectedScene++; // update scroll UI to center on selected scene g_levelScroll = max((g_selectedScene - 4) * 24, 0); break; } case SDLK_UP: { if (g_selectedScene > 0) g_selectedScene--; // update scroll UI to center on selected scene g_levelScroll = max((g_selectedScene - 4) * 24, 0); break; } case SDLK_LEFT: { if (g_scene > 0) --g_scene; Init(g_scene); // update scroll UI to center on selected scene g_levelScroll = max((g_scene - 4) * 24, 0); break; } case SDLK_RIGHT: { if (g_scene < int(g_scenes.size()) - 1) ++g_scene; Init(g_scene); // update scroll UI to center on selected scene g_levelScroll = max((g_scene - 4) * 24, 0); break; } } } void InputArrowKeysUp(int key, int x, int y) { } bool InputKeyboardDown(unsigned char key, int x, int y) { if (key > '0' && key <= '9') { g_scene = key - '0' - 1; Init(g_scene); return false; } float kSpeed = g_camSpeed; switch (key) { case 'w': { g_camVel.z = kSpeed; break; } case 's': { g_camVel.z = -kSpeed; break; } case 'a': { g_camVel.x = -kSpeed; break; } case 'd': { g_camVel.x = kSpeed; break; } case 'q': { g_camVel.y = kSpeed; break; } case 'z': { //g_drawCloth = !g_drawCloth; g_camVel.y = -kSpeed; break; } case 'u': { #ifndef ANDROID if (g_fullscreen) { SDL_SetWindowFullscreen(g_window, 0); ReshapeWindow(1280, 720); g_fullscreen = false; } else { SDL_SetWindowFullscreen(g_window, SDL_WINDOW_FULLSCREEN_DESKTOP); g_fullscreen = true; } #endif break; } case 'r': { g_resetScene = true; break; } case 'y': { g_wavePool = !g_wavePool; break; } case 'c': { #if _WIN32 if (!g_ffmpeg) { // open ffmpeg stream int i = 0; char buf[255]; FILE* f = NULL; do { sprintf(buf, "../../movies/output%d.mp4", i); f = fopen(buf, "rb"); if (f) fclose(f); ++i; } while (f); const char* str = "ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s 1280x720 -i - " "-threads 0 -preset fast -y -crf 19 -pix_fmt yuv420p -tune animation -vf vflip %s"; char cmd[1024]; sprintf(cmd, str, buf); g_ffmpeg = _popen(cmd, "wb"); assert(g_ffmpeg); } else { _pclose(g_ffmpeg); g_ffmpeg = NULL; } g_capture = !g_capture; g_frame = 0; #endif break; } case 'p': { g_pause = !g_pause; break; } case 'o': { g_step = true; break; } case 'h': { g_showHelp = !g_showHelp; break; } case 'e': { g_drawEllipsoids = !g_drawEllipsoids; break; } case 't': { g_drawOpaque = !g_drawOpaque; break; } case 'v': { g_drawPoints = !g_drawPoints; break; } case 'f': { g_drawSprings = (g_drawSprings + 1) % 3; break; } case 'i': { g_drawDiffuse = !g_drawDiffuse; break; } case 'm': { g_drawMesh = !g_drawMesh; break; } case 'n': { g_drawRopes = !g_drawRopes; break; } case 'j': { g_windTime = 0.0f; g_windStrength = 1.5f; g_windFrequency = 0.2f; break; } case '.': { g_profile = !g_profile; break; } case 'g': { if (g_params.gravity[1] != 0.0f) g_params.gravity[1] = 0.0f; else g_params.gravity[1] = -9.8f; break; } case '-': { if (g_params.numPlanes) g_params.numPlanes--; break; } case ' ': { g_emit = !g_emit; break; } case ';': { g_debug = !g_debug; break; } case 13: { g_scene = g_selectedScene; Init(g_scene); break; } case 27: { // return quit = true return true; } #if ENABLE_AFTERMATH_SUPPORT case 'l': DumpAftermathData(); break; #endif }; g_scenes[g_scene]->KeyDown(key); return false; } void InputKeyboardUp(unsigned char key, int x, int y) { switch (key) { case 'w': case 's': { g_camVel.z = 0.0f; break; } case 'a': case 'd': { g_camVel.x = 0.0f; break; } case 'q': case 'z': { g_camVel.y = 0.0f; break; } }; } void MouseFunc(int b, int state, int x, int y) { switch (state) { case SDL_RELEASED: { g_lastx = x; g_lasty = y; g_lastb = -1; break; } case SDL_PRESSED: { g_lastx = x; g_lasty = y; g_lastb = b; #ifdef ANDROID extern void setStateLeft(bool bLeftDown); setStateLeft(false); #else if ((SDL_GetModState() & KMOD_LSHIFT) && g_lastb == SDL_BUTTON_LEFT) { // record that we need to update the picked particle g_mousePicked = true; } #endif break; } }; } void MousePassiveMotionFunc(int x, int y) { g_lastx = x; g_lasty = y; } void MouseMotionFunc(unsigned state, int x, int y) { float dx = float(x - g_lastx); float dy = float(y - g_lasty); g_lastx = x; g_lasty = y; if (state & SDL_BUTTON_RMASK) { const float kSensitivity = DegToRad(0.1f); const float kMaxDelta = FLT_MAX; g_camAngle.x -= Clamp(dx*kSensitivity, -kMaxDelta, kMaxDelta); g_camAngle.y -= Clamp(dy*kSensitivity, -kMaxDelta, kMaxDelta); } } bool g_Error = false; void ErrorCallback(NvFlexErrorSeverity severity, const char* msg, const char* file, int line) { printf("Flex: %s - %s:%d\n", msg, file, line); g_Error = (severity == eNvFlexLogError); //assert(0); asserts are bad for TeamCity } void ControllerButtonEvent(SDL_ControllerButtonEvent event) { // map controller buttons to keyboard keys if (event.type == SDL_CONTROLLERBUTTONDOWN) { InputKeyboardDown(GetKeyFromGameControllerButton(SDL_GameControllerButton(event.button)), 0, 0); InputArrowKeysDown(GetKeyFromGameControllerButton(SDL_GameControllerButton(event.button)), 0, 0); if (event.button == SDL_CONTROLLER_BUTTON_LEFT_TRIGGER) { // Handle picking events using the game controller g_lastx = g_screenWidth / 2; g_lasty = g_screenHeight / 2; g_lastb = 1; // record that we need to update the picked particle g_mousePicked = true; } } else { InputKeyboardUp(GetKeyFromGameControllerButton(SDL_GameControllerButton(event.button)), 0, 0); InputArrowKeysUp(GetKeyFromGameControllerButton(SDL_GameControllerButton(event.button)), 0, 0); if (event.button == SDL_CONTROLLER_BUTTON_LEFT_TRIGGER) { // Handle picking events using the game controller g_lastx = g_screenWidth / 2; g_lasty = g_screenHeight / 2; g_lastb = -1; } } } void ControllerDeviceUpdate() { if (SDL_NumJoysticks() > 0) { SDL_JoystickEventState(SDL_ENABLE); if (SDL_IsGameController(0)) { g_gamecontroller = SDL_GameControllerOpen(0); } } } void SDLInit(const char* title) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) // Initialize SDL's Video subsystem and game controllers printf("Unable to initialize SDL"); unsigned int flags = SDL_WINDOW_RESIZABLE; #if !FLEX_DX if (g_graphics == 0) { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL; } #endif g_window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, g_screenWidth, g_screenHeight, flags); g_windowId = SDL_GetWindowID(g_window); } void SDLMainLoop() { #if ENABLE_AFTERMATH_SUPPORT __try #endif { bool quit = false; SDL_Event e; while (!quit) { UpdateFrame(); while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_QUIT: quit = true; break; case SDL_KEYDOWN: if (e.key.keysym.sym < 256 && (e.key.keysym.mod == KMOD_NONE || (e.key.keysym.mod & KMOD_NUM))) quit = InputKeyboardDown(e.key.keysym.sym, 0, 0); InputArrowKeysDown(e.key.keysym.sym, 0, 0); break; case SDL_KEYUP: if (e.key.keysym.sym < 256 && (e.key.keysym.mod == 0 || (e.key.keysym.mod & KMOD_NUM))) InputKeyboardUp(e.key.keysym.sym, 0, 0); InputArrowKeysUp(e.key.keysym.sym, 0, 0); break; case SDL_MOUSEMOTION: if (e.motion.state) MouseMotionFunc(e.motion.state, e.motion.x, e.motion.y); else MousePassiveMotionFunc(e.motion.x, e.motion.y); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: MouseFunc(e.button.button, e.button.state, e.motion.x, e.motion.y); break; case SDL_WINDOWEVENT: if (e.window.windowID == g_windowId) { if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) ReshapeWindow(e.window.data1, e.window.data2); } break; case SDL_WINDOWEVENT_LEAVE: g_camVel = Vec3(0.0f, 0.0f, 0.0f); break; case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONDOWN: ControllerButtonEvent(e.cbutton); break; case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: ControllerDeviceUpdate(); break; } } } } #if ENABLE_AFTERMATH_SUPPORT __except (true) { DumpAftermathData(); } #endif } int main(int argc, char* argv[]) { // process command line args for (int i = 1; i < argc; ++i) { int d; if (sscanf(argv[i], "-device=%d", &d)) g_device = d; if (sscanf(argv[i], "-extensions=%d", &d)) g_extensions = d != 0; if (strcmp(argv[i], "-benchmark") == 0) { g_benchmark = true; g_profile = true; g_outputAllFrameTimes = false; g_vsync = false; g_fullscreen = true; } if (strcmp(argv[i], "-d3d12") == 0) { g_d3d12 = true; // Currently interop doesn't work on d3d12 g_interop = false; } if (strcmp(argv[i], "-benchmarkAllFrameTimes") == 0) { g_benchmark = true; g_outputAllFrameTimes = true; } if (strcmp(argv[i], "-tc") == 0) { g_teamCity = true; } if (sscanf(argv[i], "-msaa=%d", &d)) g_msaaSamples = d; int w = 1280; int h = 720; if (sscanf(argv[i], "-fullscreen=%dx%d", &w, &h) == 2) { g_screenWidth = w; g_screenHeight = h; g_fullscreen = true; } else if (strcmp(argv[i], "-fullscreen") == 0) { g_screenWidth = w; g_screenHeight = h; g_fullscreen = true; } if (sscanf(argv[i], "-windowed=%dx%d", &w, &h) == 2) { g_screenWidth = w; g_screenHeight = h; g_fullscreen = false; } else if (strstr(argv[i], "-windowed")) { g_screenWidth = w; g_screenHeight = h; g_fullscreen = false; } if (sscanf(argv[i], "-vsync=%d", &d)) g_vsync = d != 0; if (sscanf(argv[i], "-multiplier=%d", &d) == 1) { g_numExtraMultiplier = d; } if (strcmp(argv[i], "-disabletweak") == 0) { g_tweakPanel = false; } if (strcmp(argv[i], "-disableinterop") == 0) { g_interop = false; } if (sscanf(argv[i], "-asynccompute=%d", &d) == 1) { g_useAsyncCompute = (d != 0); } if (sscanf(argv[i], "-graphics=%d", &d) == 1) { if (d >= 0 && d <= 2) g_graphics = d; } } // opening scene g_scenes.push_back(new PotPourri("Pot Pourri")); // soft body scenes SoftBody::Instance octopus("../../data/softs/octopus.obj"); octopus.mScale = Vec3(32.0f); octopus.mClusterSpacing = 2.75f; octopus.mClusterRadius = 3.0f; octopus.mClusterStiffness = 0.15f; octopus.mSurfaceSampling = 1.0f; SoftBody* softOctopusSceneNew = new SoftBody("Soft Octopus"); softOctopusSceneNew->AddStack(octopus, 1, 3, 1); SoftBody::Instance rope("../../data/rope.obj"); rope.mScale = Vec3(50.0f); rope.mClusterSpacing = 1.5f; rope.mClusterRadius = 0.0f; rope.mClusterStiffness = 0.55f; SoftBody* softRopeSceneNew = new SoftBody("Soft Rope"); softRopeSceneNew->AddInstance(rope); SoftBody::Instance bowl("../../data/bowl_high.ply"); bowl.mScale = Vec3(10.0f); bowl.mClusterSpacing = 2.0f; bowl.mClusterRadius = 2.0f; bowl.mClusterStiffness = 0.55f; SoftBody* softBowlSceneNew = new SoftBody("Soft Bowl"); softBowlSceneNew->AddInstance(bowl); SoftBody::Instance cloth("../../data/box_ultra_high.ply"); cloth.mScale = Vec3(20.0f, 0.2f, 20.0f); cloth.mClusterSpacing = 1.0f; cloth.mClusterRadius = 2.0f; cloth.mClusterStiffness = 0.2f; cloth.mLinkRadius = 2.0f; cloth.mLinkStiffness = 1.0f; cloth.mSkinningFalloff = 1.0f; cloth.mSkinningMaxDistance = 100.f; SoftBody* softClothSceneNew = new SoftBody("Soft Cloth"); softClothSceneNew->mRadius = 0.05f; softClothSceneNew->AddInstance(cloth); SoftBody::Instance rod("../../data/box_very_high.ply"); rod.mScale = Vec3(20.0f, 2.0f, 2.0f); rod.mTranslation = Vec3(-0.3f, 1.0f, 0.0f); rod.mClusterSpacing = 2.0f; rod.mClusterRadius = 2.0f; rod.mClusterStiffness = 0.225f; SoftBodyFixed* softRodSceneNew = new SoftBodyFixed("Soft Rod"); softRodSceneNew->AddStack(rod, 3); SoftBody::Instance teapot("../../data/teapot.ply"); teapot.mScale = Vec3(25.0f); teapot.mClusterSpacing = 3.0f; teapot.mClusterRadius = 0.0f; teapot.mClusterStiffness = 0.1f; SoftBody* softTeapotSceneNew = new SoftBody("Soft Teapot"); softTeapotSceneNew->AddInstance(teapot); SoftBody::Instance armadillo("../../data/armadillo.ply"); armadillo.mScale = Vec3(25.0f); armadillo.mClusterSpacing = 3.0f; armadillo.mClusterRadius = 0.0f; SoftBody* softArmadilloSceneNew = new SoftBody("Soft Armadillo"); softArmadilloSceneNew->AddInstance(armadillo); SoftBody::Instance softbunny("../../data/bunny.ply"); softbunny.mScale = Vec3(20.0f); softbunny.mClusterSpacing = 3.5f; softbunny.mClusterRadius = 0.0f; softbunny.mClusterStiffness = 0.2f; SoftBody* softBunnySceneNew = new SoftBody("Soft Bunny"); softBunnySceneNew->AddInstance(softbunny); // plastic scenes SoftBody::Instance plasticbunny("../../data/bunny.ply"); plasticbunny.mScale = Vec3(10.0f); plasticbunny.mClusterSpacing = 1.0f; plasticbunny.mClusterRadius = 0.0f; plasticbunny.mClusterStiffness = 0.0f; plasticbunny.mGlobalStiffness = 1.0f; plasticbunny.mClusterPlasticThreshold = 0.0015f; plasticbunny.mClusterPlasticCreep = 0.15f; plasticbunny.mTranslation[1] = 5.0f; SoftBody* plasticBunniesSceneNew = new SoftBody("Plastic Bunnies"); plasticBunniesSceneNew->mPlinth = true; plasticBunniesSceneNew->AddStack(plasticbunny, 1, 10, 1, true); SoftBody::Instance bunny1("../../data/bunny.ply"); bunny1.mScale = Vec3(10.0f); bunny1.mClusterSpacing = 1.0f; bunny1.mClusterRadius = 0.0f; bunny1.mClusterStiffness = 0.0f; bunny1.mGlobalStiffness = 1.0f; bunny1.mClusterPlasticThreshold = 0.0015f; bunny1.mClusterPlasticCreep = 0.15f; bunny1.mTranslation[1] = 5.0f; SoftBody::Instance bunny2("../../data/bunny.ply"); bunny2.mScale = Vec3(10.0f); bunny2.mClusterSpacing = 1.0f; bunny2.mClusterRadius = 0.0f; bunny2.mClusterStiffness = 0.0f; bunny2.mGlobalStiffness = 1.0f; bunny2.mClusterPlasticThreshold = 0.0015f; bunny2.mClusterPlasticCreep = 0.30f; bunny2.mTranslation[1] = 5.0f; bunny2.mTranslation[0] = 2.0f; SoftBody* plasticComparisonScene = new SoftBody("Plastic Comparison"); plasticComparisonScene->AddInstance(bunny1); plasticComparisonScene->AddInstance(bunny2); plasticComparisonScene->mPlinth = true; SoftBody::Instance stackBox("../../data/box_high.ply"); stackBox.mScale = Vec3(10.0f); stackBox.mClusterSpacing = 1.5f; stackBox.mClusterRadius = 0.0f; stackBox.mClusterStiffness = 0.0f; stackBox.mGlobalStiffness = 1.0f; stackBox.mClusterPlasticThreshold = 0.0015f; stackBox.mClusterPlasticCreep = 0.25f; stackBox.mTranslation[1] = 1.0f; SoftBody::Instance stackSphere("../../data/sphere.ply"); stackSphere.mScale = Vec3(10.0f); stackSphere.mClusterSpacing = 1.5f; stackSphere.mClusterRadius = 0.0f; stackSphere.mClusterStiffness = 0.0f; stackSphere.mGlobalStiffness = 1.0f; stackSphere.mClusterPlasticThreshold = 0.0015f; stackSphere.mClusterPlasticCreep = 0.25f; stackSphere.mTranslation[1] = 2.0f; SoftBody* plasticStackScene = new SoftBody("Plastic Stack"); plasticStackScene->AddInstance(stackBox); plasticStackScene->AddInstance(stackSphere); for (int i = 0; i < 3; i++) { stackBox.mTranslation[1] += 2.0f; stackSphere.mTranslation[1] += 2.0f; plasticStackScene->AddInstance(stackBox); plasticStackScene->AddInstance(stackSphere); } g_scenes.push_back(softOctopusSceneNew); g_scenes.push_back(softTeapotSceneNew); g_scenes.push_back(softRopeSceneNew); g_scenes.push_back(softClothSceneNew); g_scenes.push_back(softBowlSceneNew); g_scenes.push_back(softRodSceneNew); g_scenes.push_back(softArmadilloSceneNew); g_scenes.push_back(softBunnySceneNew); g_scenes.push_back(plasticBunniesSceneNew); g_scenes.push_back(plasticComparisonScene); g_scenes.push_back(plasticStackScene); // collision scenes g_scenes.push_back(new FrictionRamp("Friction Ramp")); g_scenes.push_back(new FrictionMovingShape("Friction Moving Box", 0)); g_scenes.push_back(new FrictionMovingShape("Friction Moving Sphere", 1)); g_scenes.push_back(new FrictionMovingShape("Friction Moving Capsule", 2)); g_scenes.push_back(new FrictionMovingShape("Friction Moving Mesh", 3)); g_scenes.push_back(new ShapeCollision("Shape Collision")); g_scenes.push_back(new ShapeChannels("Shape Channels")); g_scenes.push_back(new TriangleCollision("Triangle Collision")); g_scenes.push_back(new LocalSpaceFluid("Local Space Fluid")); g_scenes.push_back(new LocalSpaceCloth("Local Space Cloth")); g_scenes.push_back(new CCDFluid("World Space Fluid")); // cloth scenes g_scenes.push_back(new EnvironmentalCloth("Env Cloth Small", 6, 6, 40, 16)); g_scenes.push_back(new EnvironmentalCloth("Env Cloth Large", 16, 32, 10, 3)); g_scenes.push_back(new FlagCloth("Flag Cloth")); g_scenes.push_back(new Inflatable("Inflatables")); g_scenes.push_back(new ClothLayers("Cloth Layers")); g_scenes.push_back(new SphereCloth("Sphere Cloth")); g_scenes.push_back(new Tearing("Tearing")); g_scenes.push_back(new Pasta("Pasta")); // game mesh scenes g_scenes.push_back(new GameMesh("Game Mesh Rigid", 0)); g_scenes.push_back(new GameMesh("Game Mesh Particles", 1)); g_scenes.push_back(new GameMesh("Game Mesh Fluid", 2)); g_scenes.push_back(new GameMesh("Game Mesh Cloth", 3)); g_scenes.push_back(new RigidDebris("Rigid Debris")); // viscous fluids g_scenes.push_back(new Viscosity("Viscosity Low", 0.5f)); g_scenes.push_back(new Viscosity("Viscosity Med", 3.0f)); g_scenes.push_back(new Viscosity("Viscosity High", 5.0f, 0.12f)); g_scenes.push_back(new Adhesion("Adhesion")); g_scenes.push_back(new GooGun("Goo Gun", true)); // regular fluids g_scenes.push_back(new Buoyancy("Buoyancy")); g_scenes.push_back(new Melting("Melting")); g_scenes.push_back(new SurfaceTension("Surface Tension Low", 0.0f)); g_scenes.push_back(new SurfaceTension("Surface Tension Med", 10.0f)); g_scenes.push_back(new SurfaceTension("Surface Tension High", 20.0f)); g_scenes.push_back(new DamBreak("DamBreak 5cm", 0.05f)); g_scenes.push_back(new DamBreak("DamBreak 10cm", 0.1f)); g_scenes.push_back(new DamBreak("DamBreak 15cm", 0.15f)); g_scenes.push_back(new RockPool("Rock Pool")); g_scenes.push_back(new RayleighTaylor2D("Rayleigh Taylor 2D")); // misc feature scenes g_scenes.push_back(new TriggerVolume("Trigger Volume")); g_scenes.push_back(new ForceField("Force Field")); g_scenes.push_back(new InitialOverlap("Initial Overlap")); // rigid body scenes g_scenes.push_back(new RigidPile("Rigid2", 2)); g_scenes.push_back(new RigidPile("Rigid4", 4)); g_scenes.push_back(new RigidPile("Rigid8", 12)); g_scenes.push_back(new BananaPile("Bananas")); g_scenes.push_back(new LowDimensionalShapes("Low Dimensional Shapes")); // granular scenes g_scenes.push_back(new GranularPile("Granular Pile")); // coupling scenes g_scenes.push_back(new ParachutingBunnies("Parachuting Bunnies")); g_scenes.push_back(new WaterBalloon("Water Balloons")); g_scenes.push_back(new RigidFluidCoupling("Rigid Fluid Coupling")); g_scenes.push_back(new FluidBlock("Fluid Block")); g_scenes.push_back(new FluidClothCoupling("Fluid Cloth Coupling Water", false)); g_scenes.push_back(new FluidClothCoupling("Fluid Cloth Coupling Goo", true)); g_scenes.push_back(new BunnyBath("Bunny Bath Dam", true)); // init graphics RenderInitOptions options; #ifndef ANDROID DemoContext* demoContext = nullptr; #if FLEX_DX // Flex DX demo will always create the renderer using the same DX api as the flex lib if (g_d3d12) { // workaround for a driver issue with D3D12 with msaa, force it to off // options.numMsaaSamples = 1; g_graphics = 2; } else { g_graphics = 1; } #else switch (g_graphics) { case 0: break; case 1: break; case 2: // workaround for a driver issue with D3D12 with msaa, force it to off // options.numMsaaSamples = 1; // Currently interop doesn't work on d3d12 g_interop = false; break; default: assert(0); } #endif // Create the demo context CreateDemoContext(g_graphics); std::string str; #if FLEX_DX if (g_d3d12) str = "Flex Demo (Compute: DX12) "; else str = "Flex Demo (Compute: DX11) "; #else str = "Flex Demo (Compute: CUDA) "; #endif switch (g_graphics) { case 0: str += "(Graphics: OpenGL)"; break; case 1: str += "(Graphics: DX11)"; break; case 2: str += "(Graphics: DX12)"; break; } const char* title = str.c_str(); SDLInit(title); options.window = g_window; options.numMsaaSamples = g_msaaSamples; options.asyncComputeBenchmark = g_asyncComputeBenchmark; options.defaultFontHeight = -1; options.fullscreen = g_fullscreen; InitRender(options); if (g_fullscreen) SDL_SetWindowFullscreen(g_window, SDL_WINDOW_FULLSCREEN_DESKTOP); ReshapeWindow(g_screenWidth, g_screenHeight); #endif // ifndef ANDROID #if _WIN32 && !FLEX_DX // use the PhysX GPU selected from the NVIDIA control panel if (g_device == -1) g_device = NvFlexDeviceGetSuggestedOrdinal(); // Create an optimized CUDA context for Flex and set it on the // calling thread. This is an optional call, it is fine to use // a regular CUDA context, although creating one through this API // is recommended for best performance. bool success = NvFlexDeviceCreateCudaContext(g_device); if (!success) { printf("Error creating CUDA context.\n"); exit(-1); } #endif NvFlexInitDesc desc; desc.deviceIndex = g_device; desc.enableExtensions = g_extensions; desc.renderDevice = 0; desc.renderContext = 0; desc.computeContext = 0; desc.computeType = eNvFlexCUDA; #if FLEX_DX if (g_d3d12) desc.computeType = eNvFlexD3D12; else desc.computeType = eNvFlexD3D11; bool userSpecifiedGpuToUseForFlex = (g_device != -1); if (userSpecifiedGpuToUseForFlex) { // Flex doesn't currently support interop between different D3DDevices. // If the user specifies which physical device to use, then Flex always // creates its own D3DDevice, even if graphics is on the same physical device. // So specified physical device always means no interop. g_interop = false; } else { // Ask Flex to run on the same GPU as rendering GetRenderDevice(&desc.renderDevice, &desc.renderContext); } // Shared resources are unimplemented on D3D12, // so disable it for now. if (g_d3d12) g_interop = false; // Setting runOnRenderContext = true doesn't prevent async compute, it just // makes Flex send compute and graphics to the GPU on the same queue. // // So to allow the user to toggle async compute, we set runOnRenderContext = false // and provide a toggleable sync between compute and graphics in the app. // // Search for g_useAsyncCompute for details desc.runOnRenderContext = false; #else // Shared resources are unimplemented on D3D12, // so disable it for now. if (g_d3d12) g_interop = false; #endif // Init Flex library, note that no CUDA methods should be called before this // point to ensure we get the device context we want g_flexLib = NvFlexInit(NV_FLEX_VERSION, ErrorCallback, &desc); if (g_Error || g_flexLib == NULL) { printf("Could not initialize Flex, exiting.\n"); exit(-1); } // store device name strcpy(g_deviceName, NvFlexGetDeviceName(g_flexLib)); printf("Compute Device: %s\n\n", g_deviceName); if (g_benchmark) g_scene = BenchmarkInit(); // create shadow maps g_shadowMap = ShadowCreate(); // init default scene StartGpuWork(); Init(g_scene); EndGpuWork(); SDLMainLoop(); if (g_fluidRenderer) DestroyFluidRenderer(g_fluidRenderer); DestroyFluidRenderBuffers(g_fluidRenderBuffers); DestroyDiffuseRenderBuffers(g_diffuseRenderBuffers); ShadowDestroy(g_shadowMap); Shutdown(); DestroyRender(); SDL_DestroyWindow(g_window); SDL_Quit(); return 0; }