| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033 |
- // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
- // SPDX-FileCopyrightText: 2026 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #include <Jolt/Jolt.h>
- #include <Jolt/Physics/Hair/Hair.h>
- #include <Jolt/Physics/Hair/HairShaders.h>
- #include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
- #include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
- #include <Jolt/Physics/PhysicsSystem.h>
- #include <Jolt/Core/Profiler.h>
- #ifdef JPH_DEBUG_RENDERER
- #include <Jolt/Renderer/DebugRenderer.h>
- #endif
- JPH_NAMESPACE_BEGIN
- Hair::Hair(const HairSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, ObjectLayer inLayer) :
- mSettings(inSettings),
- mPrevPosition(inPosition),
- mPosition(inPosition),
- mPrevRotation(inRotation),
- mRotation(inRotation),
- mLayer(inLayer)
- {
- }
- Hair::~Hair()
- {
- // Delete debug data
- if (mPositions != nullptr)
- delete [] mPositions;
- if (mRotations != nullptr)
- delete [] mRotations;
- if (mVelocities != nullptr)
- delete [] mVelocities;
- if (mRenderPositionsOverridden)
- delete [] mRenderPositions;
- }
- void Hair::Init(ComputeSystem *inComputeSystem)
- {
- // Create compute buffers
- size_t num_vertices_padded = mSettings->GetNumVerticesPadded();
- size_t grid_size = mSettings->mNeutralDensity.size();
- size_t num_render_vertices = mSettings->mRenderVertices.size();
- if (!mSettings->mScalpInverseBindPose.empty() && !mSettings->mScalpVertices.empty())
- {
- mScalpJointMatricesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, mSettings->mScalpInverseBindPose.size() * sizeof(Mat44), sizeof(Mat44)).Get();
- mScalpVerticesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, mSettings->mScalpVertices.size(), sizeof(Float3)).Get();
- mScalpTrianglesCB = mSettings->mScalpTrianglesCB;
- }
- if (mScalpVerticesCB != nullptr)
- {
- mGlobalPoseTransformsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, mSettings->mSimStrands.size(), sizeof(JPH_HairGlobalPoseTransform)).Get();
- }
- else
- {
- // No vertices provided externally and none in settings, use identity transforms
- JPH_HairGlobalPoseTransform identity;
- identity.mPosition = JPH_float3(0, 0, 0);
- identity.mRotation = JPH_float4(0, 0, 0, 1);
- Array<JPH_HairGlobalPoseTransform> identity_array(mSettings->mSimStrands.size(), identity);
- mGlobalPoseTransformsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, mSettings->mSimStrands.size(), sizeof(JPH_HairGlobalPoseTransform), identity_array.data()).Get();
- }
- mCollisionPlanesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, num_vertices_padded, sizeof(JPH_HairCollisionPlane)).Get();
- mMaterialsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, mSettings->mMaterials.size(), sizeof(JPH_HairMaterial)).Get();
- mPreviousPositionsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, num_vertices_padded, sizeof(JPH_HairPosition)).Get();
- mPositionsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, num_vertices_padded, sizeof(JPH_HairPosition)).Get();
- mVelocitiesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, num_vertices_padded, sizeof(JPH_HairVelocity)).Get();
- mConstantsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::ConstantBuffer, 1, sizeof(JPH_HairUpdateContext)).Get();
- mVelocityAndDensityCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, grid_size, sizeof(Float4)).Get();
- if (!mRenderPositionsOverridden)
- mRenderPositionsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, num_render_vertices, sizeof(Float3)).Get();
- }
- void Hair::InitializeContext(UpdateContext &outCtx, float inDeltaTime, const PhysicsSystem &inSystem)
- {
- float clamped_delta_time = min(inDeltaTime, mSettings->mMaxDeltaTime);
- outCtx.mNumIterations = (uint)std::round(clamped_delta_time * mSettings->mNumIterationsPerSecond);
- outCtx.mDeltaTime = outCtx.mNumIterations > 0? clamped_delta_time / outCtx.mNumIterations : 0.0f;
- outCtx.mTimeRatio = outCtx.mDeltaTime * float(HairSettings::cDefaultIterationsPerSecond);
- outCtx.mHalfDeltaTime = 0.5f * outCtx.mDeltaTime;
- outCtx.mInvDeltaTimeSq = outCtx.mDeltaTime > 0.0f? 1.0f / Square(outCtx.mDeltaTime) : 1.0e12f;
- outCtx.mTwoDivDeltaTime = outCtx.mDeltaTime > 0.0f? 2.0f / outCtx.mDeltaTime : 1.0e12f;
- outCtx.mSubStepGravity = (mRotation.Conjugated() * inSystem.GetGravity()) * outCtx.mDeltaTime;
- // Calculate delta transform from previous to current position and rotation
- outCtx.mHasTransformChanged = mPosition != mPrevPosition || mRotation != mPrevRotation;
- RMat44 prev_com = RMat44::sRotationTranslation(mPrevRotation, mPrevPosition);
- outCtx.mDeltaTransform = (GetWorldTransform().InversedRotationTranslation() * prev_com).ToMat44();
- outCtx.mDeltaTransformQuat = outCtx.mDeltaTransform.GetQuaternion();
- mPrevPosition = mPosition;
- mPrevRotation = mRotation;
- // Check if we need collision detection / grid
- outCtx.mNeedsCollision = false;
- outCtx.mNeedsGrid = false;
- outCtx.mGlobalPoseOnly = true;
- for (const HairSettings::Material &material : mSettings->mMaterials)
- {
- outCtx.mNeedsCollision |= material.mEnableCollision;
- outCtx.mNeedsGrid |= material.NeedsGrid();
- outCtx.mGlobalPoseOnly &= material.GlobalPoseOnly();
- }
- if (outCtx.mNeedsCollision)
- {
- struct Collector : public CollideShapeBodyCollector
- {
- Collector(const PhysicsSystem &inSystem, RMat44Arg inTransform, const AABox &inLocalBounds, Array<LeafShape> &ioHits) :
- mSystem(inSystem),
- mTransform(inTransform),
- mInverseTransform(inTransform.InversedRotationTranslation()),
- mLocalBounds(inLocalBounds),
- mHits(ioHits)
- {
- }
- virtual void AddHit(const BodyID &inResult) override
- {
- BodyLockRead lock(mSystem.GetBodyLockInterface(), inResult);
- if (lock.Succeeded())
- {
- const Body &body = lock.GetBody();
- if (body.IsRigidBody()
- && !body.IsSensor())
- {
- // Calculate transform of this body relative to the hair instance
- Mat44 com = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();
- // Collect leaf shapes
- struct LeafShapeCollector : public TransformedShapeCollector
- {
- LeafShapeCollector(RMat44Arg inHeadTransform, const Body &inBody, Array<LeafShape> &ioHits) : mHeadTransform(inHeadTransform), mBody(inBody), mHits(ioHits) { }
- virtual void AddHit(const TransformedShape &inResult) override
- {
- mHits.emplace_back(Mat44::sRotationTranslation(inResult.mShapeRotation, Vec3(inResult.mShapePositionCOM)),
- inResult.GetShapeScale(),
- mHeadTransform.Multiply3x3Transposed(mBody.GetPointVelocity(mHeadTransform * inResult.mShapePositionCOM)), // Calculate velocity of shape at its center of mass position
- mHeadTransform.Multiply3x3Transposed(mBody.GetAngularVelocity()),
- inResult.mShape);
- }
- RMat44 mHeadTransform;
- const Body & mBody;
- Array<LeafShape> & mHits;
- };
- LeafShapeCollector collector(mTransform, body, mHits);
- body.GetShape()->CollectTransformedShapes(mLocalBounds, com.GetTranslation(), com.GetQuaternion(), Vec3::sOne(), SubShapeIDCreator(), collector, { });
- }
- }
- }
- private:
- const PhysicsSystem & mSystem;
- RMat44 mTransform;
- RMat44 mInverseTransform;
- AABox mLocalBounds;
- Array<LeafShape> & mHits;
- };
- // Calculate world space bounding box
- RMat44 transform = GetWorldTransform();
- AABox world_bounds = mSettings->mSimulationBounds.Transformed(transform);
- // Collect shapes that intersect with the bounding box
- Collector collector(inSystem, transform, mSettings->mSimulationBounds, outCtx.mShapes);
- DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(mLayer);
- DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(mLayer);
- inSystem.GetBroadPhaseQuery().CollideAABox(world_bounds, collector, broadphase_layer_filter, object_layer_filter);
- // If no shapes were found, we don't need collision
- if (outCtx.mShapes.empty())
- outCtx.mNeedsCollision = false;
- }
- }
- void Hair::Update(float inDeltaTime, Mat44Arg inJointToHair, const Mat44 *inJointMatrices, const PhysicsSystem &inSystem, const HairShaders &inShaders, ComputeSystem *inComputeSystem, ComputeQueue *inComputeQueue)
- {
- UpdateContext ctx;
- InitializeContext(ctx, inDeltaTime, inSystem);
- if (inJointMatrices != nullptr && mScalpJointMatricesCB != nullptr)
- {
- JPH_PROFILE("Prepare for Skinning");
- Mat44 *joints = mScalpJointMatricesCB->Map<Mat44>(ComputeBuffer::EMode::Write);
- mSettings->PrepareForScalpSkinning(inJointToHair, inJointMatrices, joints);
- mScalpJointMatricesCB->Unmap();
- }
- if (ctx.mNeedsCollision)
- {
- JPH_PROFILE("Create Collision Shapes");
- // First determine buffer sizes
- uint num_shapes = 0;
- uint num_faces = 0;
- uint num_vertices = 0;
- uint num_header = 0;
- uint num_indices = 0;
- uint max_vertices_per_face = 0;
- uint max_points = 0;
- for (const LeafShape &shape : ctx.mShapes)
- if (shape.mShape->GetSubType() == EShapeSubType::ConvexHull)
- {
- const ConvexHullShape *ch = static_cast<const ConvexHullShape *>(shape.mShape.GetPtr());
- ++num_shapes;
- ++num_header; // Write number of vertices
- uint np = ch->GetNumPoints();
- max_points = max(max_points, np);
- num_vertices += np;
- uint nf = ch->GetNumFaces();
- num_faces += nf;
- for (uint f = 0; f < nf; ++f)
- {
- num_header += 2; // Write indices start + end
- uint num_vertices_in_face = ch->GetNumVerticesInFace(f);
- num_indices += num_vertices_in_face;
- max_vertices_per_face = max(max_vertices_per_face, num_vertices_in_face);
- }
- }
- ++num_header; // Terminator
- num_indices += num_header;
- // Now allocate buffers
- if (mCollisionShapesCB == nullptr || mCollisionShapesCB->GetSize() < num_shapes)
- {
- mCollisionShapesCB = nullptr;
- mCollisionShapesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, num_shapes, sizeof(JPH_HairCollisionShape)).Get();
- }
- if (mShapePlanesCB == nullptr || mShapePlanesCB->GetSize() < num_faces)
- {
- mShapePlanesCB = nullptr;
- mShapePlanesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, max(num_faces, 1u), sizeof(Float4)).Get();
- }
- if (mShapeVerticesCB == nullptr || mShapeVerticesCB->GetSize() < num_vertices)
- {
- mShapeVerticesCB = nullptr;
- mShapeVerticesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, max(num_vertices, 1u), sizeof(Float3)).Get();
- }
- if (mShapeIndicesCB == nullptr || mShapeIndicesCB->GetSize() < num_indices)
- {
- mShapeIndicesCB = nullptr;
- mShapeIndicesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::UploadBuffer, num_indices, sizeof(uint32)).Get();
- }
- JPH_HairCollisionShape *collision_shapes = mCollisionShapesCB->Map<JPH_HairCollisionShape>(ComputeBuffer::EMode::Write);
- Float4 *shape_planes = mShapePlanesCB->Map<Float4>(ComputeBuffer::EMode::Write);
- Float3 *shape_vertices = mShapeVerticesCB->Map<Float3>(ComputeBuffer::EMode::Write);
- uint32 *shape_indices = mShapeIndicesCB->Map<uint32>(ComputeBuffer::EMode::Write);
- uint *face_indices = (uint *)JPH_STACK_ALLOC(max_vertices_per_face * sizeof(uint));
- Vec3 *points = (Vec3 *)JPH_STACK_ALLOC(max_points * sizeof(Vec3));
- // Convert the hulls to compute buffers
- Float4 *sp = shape_planes;
- Float3 *sv = shape_vertices;
- uint32 *sh = shape_indices;
- JPH_HairCollisionShape *cs = collision_shapes;
- uint32 *si = shape_indices + num_header;
- for (const LeafShape &shape : ctx.mShapes)
- if (shape.mShape->GetSubType() == EShapeSubType::ConvexHull)
- {
- const ConvexHullShape *ch = static_cast<const ConvexHullShape *>(shape.mShape.GetPtr());
- // Store collision shape
- shape.mTransform.GetTranslation().StoreFloat3(&cs->mCenterOfMass);
- shape.mLinearVelocity.StoreFloat3(&cs->mLinearVelocity);
- shape.mAngularVelocity.StoreFloat3(&cs->mAngularVelocity);
- ++cs;
- // Store points transformed to hair space
- Mat44 shape_transform = shape.mTransform.PreScaled(shape.mScale);
- uint first_vertex_index = uint(sv - shape_vertices);
- for (uint p = 0, np = ch->GetNumPoints(); p < np; ++p)
- {
- Vec3 v = shape_transform * ch->GetPoint(p);
- points[p] = v; // Store points in a temporary buffer so we avoid reading from GPU memory
- v.StoreFloat3(sv);
- ++sv;
- }
- // Store number of faces
- uint nf = ch->GetNumFaces();
- *sh = nf;
- ++sh;
- // Store the indices
- if (ScaleHelpers::IsInsideOut(shape.mScale))
- {
- // Reverse winding order
- for (uint f = 0; f < nf; ++f)
- {
- // Store indices
- uint nv = ch->GetFaceVertices(f, max_vertices_per_face, face_indices);
- uint32 indices_start = uint32(si - shape_indices);
- *sh = indices_start;
- ++sh;
- *sh = indices_start + nv;
- ++sh;
- for (int v = int(nv) - 1; v >= 0; --v, ++si)
- *si = face_indices[v] + first_vertex_index;
- // Calculate plane (avoids reading from GPU memory)
- Plane::sFromPointsCCW(points[face_indices[2]], points[face_indices[1]], points[face_indices[0]]).StoreFloat4(sp);
- ++sp;
- }
- }
- else
- {
- // Keep winding order
- for (uint f = 0; f < nf; ++f)
- {
- // Store indices
- uint nv = ch->GetFaceVertices(f, max_vertices_per_face, face_indices);
- uint32 indices_start = uint32(si - shape_indices);
- *sh++ = indices_start;
- *sh++ = indices_start + nv;
- for (uint v = 0; v < nv; ++v)
- *si++ = face_indices[v] + first_vertex_index;
- // Calculate plane (avoids reading from GPU memory)
- Plane::sFromPointsCCW(points[face_indices[0]], points[face_indices[1]], points[face_indices[2]]).StoreFloat4(sp);
- ++sp;
- }
- }
- }
- *sh = 0; // Terminator
- ++sh;
- JPH_ASSERT(uint(cs - collision_shapes) == num_shapes);
- JPH_ASSERT(uint(sp - shape_planes) == num_faces);
- JPH_ASSERT(uint(sv - shape_vertices) == num_vertices);
- JPH_ASSERT(uint(sh - shape_indices) == num_header);
- JPH_ASSERT(uint(si - shape_indices) == num_indices);
- // Unmap buffers
- mCollisionShapesCB->Unmap();
- mShapePlanesCB->Unmap();
- mShapeVerticesCB->Unmap();
- mShapeIndicesCB->Unmap();
- }
- {
- JPH_PROFILE("Set materials");
- JPH_HairMaterial *materials = mMaterialsCB->Map<JPH_HairMaterial>(ComputeBuffer::EMode::Write);
- for (size_t i = 0, n = mSettings->mMaterials.size(); i < n; ++i)
- {
- const HairSettings::Material &m_in = mSettings->mMaterials[i];
- JPH_HairMaterial &m_out = materials[i];
- GradientSampler world_transform_influence(m_in.mWorldTransformInfluence);
- m_out.mWorldTransformInfluence = world_transform_influence.ToFloat4();
- GradientSampler global_pose(ctx.mGlobalPoseOnly? m_in.mGlobalPose : m_in.mGlobalPose.MakeStepDependent(ctx.mTimeRatio));
- m_out.mGlobalPose = global_pose.ToFloat4();
- GradientSampler global_pose_skin_to_root(m_in.mSkinGlobalPose);
- m_out.mSkinGlobalPose = global_pose_skin_to_root.ToFloat4();
- GradientSampler gravity_factor(m_in.mGravityFactor);
- m_out.mGravityFactor = gravity_factor.ToFloat4();
- GradientSampler hair_radius(m_in.mHairRadius);
- m_out.mHairRadius = hair_radius.ToFloat4();
- m_out.mBendComplianceMultiplier = m_in.mBendComplianceMultiplier;
- GradientSampler grid_velocity_factor(m_in.mGridVelocityFactor.MakeStepDependent(ctx.mTimeRatio));
- m_out.mGridVelocityFactor = grid_velocity_factor.ToFloat4();
- m_out.mEnableCollision = ctx.mNeedsCollision && m_in.mEnableCollision? 1 : 0;
- m_out.mEnableLRA = m_in.mEnableLRA? 1 : 0;
- m_out.mEnableGrid = m_in.mGridVelocityFactor.mMin != 0.0f || m_in.mGridVelocityFactor.mMax != 0.0f || m_in.mGridDensityForceFactor != 0.0f;
- m_out.mFriction = m_in.mFriction;
- m_out.mExpLinearDampingDeltaTime = std::exp(-m_in.mLinearDamping * ctx.mDeltaTime);
- m_out.mExpAngularDampingDeltaTime = std::exp(-m_in.mAngularDamping * ctx.mDeltaTime);
- m_out.mBendComplianceInvDeltaTimeSq = m_in.mBendCompliance * ctx.mInvDeltaTimeSq;
- m_out.mStretchComplianceInvDeltaTimeSq = m_in.mStretchCompliance * ctx.mInvDeltaTimeSq;
- m_out.mGridDensityForceFactor = m_in.mGridDensityForceFactor;
- m_out.mInertiaMultiplier = m_in.mInertiaMultiplier;
- m_out.mMaxLinearVelocitySq = Square(m_in.mMaxLinearVelocity);
- m_out.mMaxAngularVelocitySq = Square(m_in.mMaxAngularVelocity);
- }
- mMaterialsCB->Unmap();
- }
- {
- JPH_PROFILE("Set constants");
- JPH_HairUpdateContext *cdata = mConstantsCB->Map<JPH_HairUpdateContext>(ComputeBuffer::EMode::Write);
- cdata->cNumStrands = uint32(mSettings->mSimStrands.size());
- cdata->cNumVertices = mSettings->GetNumVerticesPadded();
- cdata->cNumGridPoints = (uint32)mSettings->mNeutralDensity.size();
- cdata->cNumRenderVertices = (uint)mSettings->mRenderVertices.size();
- HairSettings::GridSampler grid_sampler(mSettings);
- memcpy(&cdata->cGridSizeMin2, &grid_sampler.mGridSizeMin2, 3 * sizeof(float));
- cdata->cTwoDivDeltaTime = ctx.mTwoDivDeltaTime;
- grid_sampler.mGridSizeMin1.StoreFloat3(&cdata->cGridSizeMin1);
- cdata->cDeltaTime = ctx.mDeltaTime;
- grid_sampler.mOffset.StoreFloat3(&cdata->cGridOffset);
- cdata->cHalfDeltaTime = ctx.mHalfDeltaTime;
- grid_sampler.mScale.StoreFloat3(&cdata->cGridScale);
- cdata->cInvDeltaTimeSq = ctx.mInvDeltaTimeSq;
- ctx.mSubStepGravity.StoreFloat3(&cdata->cSubStepGravity);
- cdata->cNumSkinVertices = (uint)mSettings->mScalpVertices.size();
- memcpy(&cdata->cGridStride, &grid_sampler.mGridStride, 3 * sizeof(uint32));
- cdata->cNumSkinWeightsPerVertex = mSettings->mScalpNumSkinWeightsPerVertex;
- for (int i = 0; i < 4; ++i)
- ctx.mDeltaTransform.GetColumn4(i).StoreFloat4(&cdata->cDeltaTransform[i]);
- for (int i = 0; i < 4; ++i)
- mScalpToHead.GetColumn4(i).StoreFloat4(&cdata->cScalpToHead[i]);
- ctx.mDeltaTransformQuat.StoreFloat4(&cdata->cDeltaTransformQuat);
- mConstantsCB->Unmap();
- }
- {
- JPH_PROFILE("Set iteration constants");
- // Ensure that we have the right number of constant buffers allocated
- uint old_size = uint(mIterationConstantsCB.size());
- if (old_size < ctx.mNumIterations)
- {
- mIterationConstantsCB.resize(ctx.mNumIterations);
- for (uint i = old_size; i < ctx.mNumIterations; ++i)
- mIterationConstantsCB[i] = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::ConstantBuffer, 1, sizeof(JPH_HairIterationContext)).Get();
- }
- // Fill in the constant buffers
- JPH_HairIterationContext iteration_data;
- for (uint i = 0; i < ctx.mNumIterations; ++i)
- {
- iteration_data.cAccumulatedDeltaTime = ctx.mDeltaTime * (i + 1);
- iteration_data.cIterationFraction = 1.0f / float(ctx.mNumIterations - i);
- JPH_HairIterationContext *idata = mIterationConstantsCB[i]->Map<JPH_HairIterationContext>(ComputeBuffer::EMode::Write);
- *idata = iteration_data;
- mIterationConstantsCB[i]->Unmap();
- }
- }
- {
- JPH_PROFILE("Queue Compute");
- uint dispatch_per_vertex = (mSettings->GetNumVerticesPadded() + cHairPerVertexBatch - 1) / cHairPerVertexBatch;
- uint dispatch_per_vertex_skip_first_vertex = (mSettings->GetNumVerticesPadded() - (uint)mSettings->mSimStrands.size() + cHairPerVertexBatch - 1) / cHairPerVertexBatch; // Skip the first vertex of each strand
- uint dispatch_per_grid_cell = uint((mSettings->mNeutralDensity.size() + cHairPerGridCellBatch - 1) / cHairPerGridCellBatch);
- uint dispatch_per_strand = uint((mSettings->mSimStrands.size() + cHairPerStrandBatch - 1) / cHairPerStrandBatch);
- uint dispatch_per_render_vertex = uint((mSettings->mRenderVertices.size() + cHairPerRenderVertexBatch - 1) / cHairPerRenderVertexBatch);
- bool was_teleported = mTeleported;
- mTeleported = false;
- if (was_teleported)
- {
- // Initialize positions and velocities
- inComputeQueue->SetShader(inShaders.mTeleportCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gInitialBishops", mSettings->mVerticesBishopCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gVelocities", mVelocitiesCB);
- inComputeQueue->Dispatch(dispatch_per_vertex);
- }
- else if (!ctx.mGlobalPoseOnly && ctx.mHasTransformChanged)
- {
- // Apply delta transform
- inComputeQueue->SetShader(inShaders.mApplyDeltaTransformCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gVelocities", mVelocitiesCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- }
- if (mScalpJointMatricesCB != nullptr)
- {
- // Skin the scalp mesh
- inComputeQueue->SetShader(inShaders.mSkinVerticesCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gScalpVertices", mSettings->mScalpVerticesCB);
- inComputeQueue->SetBuffer("gScalpSkinWeights", mSettings->mScalpSkinWeightsCB);
- inComputeQueue->SetBuffer("gScalpJointMatrices", mScalpJointMatricesCB);
- inComputeQueue->SetRWBuffer("gScalpVerticesOut", mScalpVerticesCB);
- inComputeQueue->Dispatch(uint((mSettings->mScalpVertices.size() + cHairPerVertexBatch - 1) / cHairPerVertexBatch));
- }
- if (mScalpVerticesCB != nullptr)
- {
- // Determine if we directly write to the position / transform buffers or if we need to interpolate
- bool needs_interpolate = !ctx.mGlobalPoseOnly && !was_teleported;
- // Create target buffers if they don't exist yet
- if (mTargetPositionsCB == nullptr && needs_interpolate)
- {
- mTargetPositionsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, mSettings->mSimStrands.size(), sizeof(JPH_HairPosition)).Get();
- mTargetGlobalPoseTransformsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::RWBuffer, mSettings->mSimStrands.size(), sizeof(JPH_HairGlobalPoseTransform)).Get();
- }
- // Skin the strand roots to the scalp mesh
- inComputeQueue->SetShader(inShaders.mSkinRootsCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gSkinPoints", mSettings->mSkinPointsCB);
- inComputeQueue->SetBuffer("gScalpVertices", mScalpVerticesCB);
- inComputeQueue->SetBuffer("gScalpTriangles", mScalpTrianglesCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gInitialBishops", mSettings->mVerticesBishopCB);
- inComputeQueue->SetRWBuffer("gPositions", needs_interpolate? mTargetPositionsCB : mPositionsCB);
- inComputeQueue->SetRWBuffer("gGlobalPoseTransforms", needs_interpolate? mTargetGlobalPoseTransformsCB : mGlobalPoseTransformsCB);
- inComputeQueue->Dispatch(dispatch_per_strand);
- }
- if (ctx.mGlobalPoseOnly)
- {
- // Only run global pose logic
- inComputeQueue->SetShader(inShaders.mApplyGlobalPoseCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gInitialBishops", mSettings->mVerticesBishopCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetBuffer("gGlobalPoseTransforms", mGlobalPoseTransformsCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- }
- else if (ctx.mNumIterations > 0)
- {
- if (ctx.mNeedsCollision)
- {
- // Calculate collision planes
- inComputeQueue->SetShader(inShaders.mCalculateCollisionPlanesCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetBuffer("gShapePlanes", mShapePlanesCB);
- inComputeQueue->SetBuffer("gShapeVertices", mShapeVerticesCB);
- inComputeQueue->SetBuffer("gShapeIndices", mShapeIndicesCB);
- inComputeQueue->SetRWBuffer("gCollisionPlanes", mCollisionPlanesCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- }
- if (ctx.mNeedsGrid)
- {
- // Clear the grid
- inComputeQueue->SetShader(inShaders.mGridClearCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetRWBuffer("gVelocityAndDensity", mVelocityAndDensityCB);
- inComputeQueue->Dispatch(dispatch_per_grid_cell);
- // Accumulate vertices into the grid
- inComputeQueue->SetShader(inShaders.mGridAccumulateCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetBuffer("gVelocities", mVelocitiesCB);
- inComputeQueue->SetRWBuffer("gVelocityAndDensity", mVelocityAndDensityCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- // Normalize velocities in the grid
- inComputeQueue->SetShader(inShaders.mGridNormalizeCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetRWBuffer("gVelocityAndDensity", mVelocityAndDensityCB);
- inComputeQueue->Dispatch(dispatch_per_grid_cell);
- }
- // First integrate
- inComputeQueue->SetShader(inShaders.mIntegrateCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gNeutralDensity", mSettings->mNeutralDensityCB);
- inComputeQueue->SetBuffer("gVelocityAndDensity", mVelocityAndDensityCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetBuffer("gVelocities", mVelocitiesCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gPreviousPositions", mPreviousPositionsCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- for (uint it = 0; it < ctx.mNumIterations; ++it)
- {
- if (mTargetPositionsCB != nullptr && !was_teleported)
- {
- // Update skinned roots for this iteration (interpolate them towards the target positions)
- inComputeQueue->SetShader(inShaders.mUpdateRootsCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetConstantBuffer("gIterationContext", mIterationConstantsCB[it]);
- inComputeQueue->SetBuffer("gTargetPositions", mTargetPositionsCB);
- inComputeQueue->SetBuffer("gTargetGlobalPoseTransforms", mTargetGlobalPoseTransformsCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gGlobalPoseTransforms", mGlobalPoseTransformsCB);
- inComputeQueue->Dispatch(dispatch_per_strand);
- }
- // Then update the constraints per strand
- inComputeQueue->SetShader(inShaders.mUpdateStrandsCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gOmega0s", mSettings->mVerticesOmega0CB);
- inComputeQueue->SetBuffer("gInitialLengths", mSettings->mVerticesLengthCB);
- inComputeQueue->SetBuffer("gStrandVertexCounts", mSettings->mStrandVertexCountsCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->Dispatch(dispatch_per_strand);
- if (it == ctx.mNumIterations - 1)
- {
- // Last iteration: only update velocities
- inComputeQueue->SetShader(inShaders.mUpdateVelocityCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetConstantBuffer("gIterationContext", mIterationConstantsCB[it]);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gInitialBishops", mSettings->mVerticesBishopCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetBuffer("gPreviousPositions", mPreviousPositionsCB);
- inComputeQueue->SetBuffer("gGlobalPoseTransforms", mGlobalPoseTransformsCB);
- inComputeQueue->SetBuffer("gCollisionShapes", mCollisionShapesCB);
- inComputeQueue->SetBuffer("gCollisionPlanes", mCollisionPlanesCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gVelocities", mVelocitiesCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- }
- else
- {
- // Other iterations: update velocities then integrate again
- inComputeQueue->SetShader(inShaders.mUpdateVelocityIntegrateCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetConstantBuffer("gIterationContext", mIterationConstantsCB[it]);
- inComputeQueue->SetBuffer("gVerticesFixed", mSettings->mVerticesFixedCB);
- inComputeQueue->SetBuffer("gStrandFractions", mSettings->mVerticesStrandFractionCB);
- inComputeQueue->SetBuffer("gInitialPositions", mSettings->mVerticesPositionCB);
- inComputeQueue->SetBuffer("gInitialBishops", mSettings->mVerticesBishopCB);
- inComputeQueue->SetBuffer("gNeutralDensity", mSettings->mNeutralDensityCB);
- inComputeQueue->SetBuffer("gVelocityAndDensity", mVelocityAndDensityCB);
- inComputeQueue->SetBuffer("gStrandMaterialIndex", mSettings->mStrandMaterialIndexCB);
- inComputeQueue->SetBuffer("gMaterials", mMaterialsCB);
- inComputeQueue->SetBuffer("gGlobalPoseTransforms", mGlobalPoseTransformsCB);
- inComputeQueue->SetBuffer("gCollisionShapes", mCollisionShapesCB);
- inComputeQueue->SetBuffer("gCollisionPlanes", mCollisionPlanesCB);
- inComputeQueue->SetRWBuffer("gPreviousPositions", mPreviousPositionsCB);
- inComputeQueue->SetRWBuffer("gPositions", mPositionsCB);
- inComputeQueue->Dispatch(dispatch_per_vertex_skip_first_vertex);
- }
- }
- }
- // Remap simulation positions to render positions
- inComputeQueue->SetShader(inShaders.mCalculateRenderPositionsCS);
- inComputeQueue->SetConstantBuffer("gContext", mConstantsCB);
- inComputeQueue->SetBuffer("gSVertexInfluences", mSettings->mSVertexInfluencesCB);
- inComputeQueue->SetBuffer("gPositions", mPositionsCB);
- inComputeQueue->SetRWBuffer("gRenderPositions", mRenderPositionsCB);
- inComputeQueue->Dispatch(dispatch_per_render_vertex);
- }
- }
- void Hair::ReadBackGPUState(ComputeQueue *inComputeQueue)
- {
- if (mPositionsReadBackCB == nullptr)
- {
- // Create read back buffers
- if (mScalpVerticesCB != nullptr)
- mScalpVerticesReadBackCB = mScalpVerticesCB->CreateReadBackBuffer().Get();
- mPositionsReadBackCB = mPositionsCB->CreateReadBackBuffer().Get();
- mVelocitiesReadBackCB = mVelocitiesCB->CreateReadBackBuffer().Get();
- mVelocityAndDensityReadBackCB = mVelocityAndDensityCB->CreateReadBackBuffer().Get();
- mRenderPositionsReadBackCB = mRenderPositionsCB->CreateReadBackBuffer().Get();
- }
- {
- JPH_PROFILE("Transfer data from GPU");
- // Read back the skinned vertices
- if (mScalpVerticesCB != nullptr)
- inComputeQueue->ScheduleReadback(mScalpVerticesReadBackCB, mScalpVerticesCB);
- // Read back the vertices
- inComputeQueue->ScheduleReadback(mPositionsReadBackCB, mPositionsCB);
- inComputeQueue->ScheduleReadback(mVelocitiesReadBackCB, mVelocitiesCB);
- inComputeQueue->ScheduleReadback(mRenderPositionsReadBackCB, mRenderPositionsCB);
- // Read back the velocity and density
- inComputeQueue->ScheduleReadback(mVelocityAndDensityReadBackCB, mVelocityAndDensityCB);
- // Wait for the compute queue to finish
- inComputeQueue->ExecuteAndWait();
- }
- {
- JPH_PROFILE("Reorder hair data");
- // Reorder position and velocity data
- const JPH_HairPosition *positions = mPositionsReadBackCB->Map<JPH_HairPosition>(ComputeBuffer::EMode::Read);
- const JPH_HairVelocity *velocities = mVelocitiesReadBackCB->Map<JPH_HairVelocity>(ComputeBuffer::EMode::Read);
- size_t num_vertices = mSettings->mSimVertices.size();
- if (mPositions == nullptr)
- mPositions = new Float3 [num_vertices];
- if (mRotations == nullptr)
- mRotations = new Quat [num_vertices];
- if (mVelocities == nullptr)
- mVelocities = new JPH_HairVelocity [num_vertices];
- uint32 num_strands = (uint32)mSettings->mSimStrands.size();
- for (uint32 s = 0; s < num_strands; ++s)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[s];
- for (uint32 v = 0; v < strand.VertexCount(); ++v)
- {
- uint32 in_index = s + v * num_strands;
- uint32 out_index = strand.mStartVtx + v;
- mPositions[out_index] = Float3(positions[in_index].mPosition);
- mRotations[out_index] = Quat(positions[in_index].mRotation);
- mVelocities[out_index] = velocities[in_index];
- }
- }
- mPositionsReadBackCB->Unmap();
- mVelocitiesReadBackCB->Unmap();
- }
- }
- void Hair::LockReadBackBuffers()
- {
- if (mScalpVerticesReadBackCB != nullptr)
- mScalpVertices = mScalpVerticesReadBackCB->Map<Float3>(ComputeBuffer::EMode::Read);
- mVelocityAndDensity = mVelocityAndDensityReadBackCB->Map<Float4>(ComputeBuffer::EMode::Read);
- if (mRenderPositionsOverridden)
- {
- uint num_render_vertices = (uint)mSettings->mRenderVertices.size();
- if (mRenderPositions == nullptr)
- mRenderPositions = new Float3 [num_render_vertices];
- mRenderPositionsToFloat3(mRenderPositionsReadBackCB, const_cast<Float3 *>(mRenderPositions), num_render_vertices);
- }
- else
- mRenderPositions = mRenderPositionsReadBackCB->Map<Float3>(ComputeBuffer::EMode::Read);
- }
- void Hair::UnlockReadBackBuffers()
- {
- if (mScalpVerticesReadBackCB != nullptr)
- mScalpVerticesReadBackCB->Unmap();
- mVelocityAndDensityReadBackCB->Unmap();
- if (!mRenderPositionsOverridden)
- mRenderPositionsReadBackCB->Unmap();
- }
- #ifdef JPH_DEBUG_RENDERER
- void Hair::Draw(const DrawSettings &inSettings, DebugRenderer *inRenderer)
- {
- LockReadBackBuffers();
- const Float3 *positions = GetPositions();
- const Float3 *render_positions = GetRenderPositions();
- const Quat *rotations = GetRotations();
- StridedPtr<const Float3> velocities = GetVelocities();
- StridedPtr<const Float3> angular_velocities = GetAngularVelocities();
- const Float4 *grid_velocity_and_density = GetGridVelocityAndDensity();
- const Float3 *scalp_vertices = GetScalpVertices();
- float arrow_size = 0.01f * mSettings->mSimulationBounds.GetSize().ReduceMin();
- RMat44 com = GetWorldTransform();
- // Draw the render strands
- if (inSettings.mDrawRenderStrands)
- {
- JPH_PROFILE("Draw Render Strands");
- // Calculate a map of sim vertex index to strand index
- Array<uint> sim_vertex_to_strand;
- sim_vertex_to_strand.resize(mSettings->mSimVertices.size(), 0);
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- for (uint v = strand.mStartVtx; v < strand.mEndVtx; ++v)
- sim_vertex_to_strand[v] = i;
- }
- Hash<uint32> hasher;
- switch (inSettings.mRenderStrandColor)
- {
- case ERenderStrandColor::PerRenderStrand:
- {
- Color color = Color::sGreen;
- for (const HairSettings::RStrand &strand : mSettings->mRenderStrands)
- {
- uint32 strand_idx = sim_vertex_to_strand[mSettings->mRenderVertices[strand.mStartVtx].mInfluences[0].mVertexIndex];
- if (strand_idx >= inSettings.mSimulationStrandBegin && strand_idx < inSettings.mSimulationStrandEnd)
- {
- RVec3 x0 = com * Vec3(render_positions[strand.mStartVtx]);
- for (uint32 v = strand.mStartVtx + 1; v < strand.mEndVtx; ++v)
- {
- RVec3 x1 = com * Vec3(render_positions[v]);
- inRenderer->DrawLine(x0, x1, color);
- x0 = x1;
- }
- color = Color(uint32(hasher(color.GetUInt32())) | 0xff000000);
- }
- }
- }
- break;
- case ERenderStrandColor::PerSimulatedStrand:
- for (const HairSettings::RStrand &strand : mSettings->mRenderStrands)
- {
- uint32 strand_idx = sim_vertex_to_strand[mSettings->mRenderVertices[strand.mStartVtx].mInfluences[0].mVertexIndex];
- if (strand_idx >= inSettings.mSimulationStrandBegin && strand_idx < inSettings.mSimulationStrandEnd)
- {
- Color color = Color(uint32(hasher(strand_idx)) | 0xff000000);
- RVec3 x0 = com * Vec3(render_positions[strand.mStartVtx]);
- for (uint32 v = strand.mStartVtx + 1; v < strand.mEndVtx; ++v)
- {
- RVec3 x1 = com * Vec3(render_positions[v]);
- inRenderer->DrawLine(x0, x1, color);
- x0 = x1;
- }
- }
- }
- break;
- case ERenderStrandColor::GravityFactor:
- case ERenderStrandColor::WorldTransformInfluence:
- case ERenderStrandColor::GridVelocityFactor:
- case ERenderStrandColor::GlobalPose:
- case ERenderStrandColor::SkinGlobalPose:
- for (const HairSettings::RStrand &strand : mSettings->mRenderStrands)
- {
- uint32 strand_idx = sim_vertex_to_strand[mSettings->mRenderVertices[strand.mStartVtx].mInfluences[0].mVertexIndex];
- const HairSettings::Material &material = mSettings->mMaterials[mSettings->mSimStrands[strand_idx].mMaterialIndex];
- // Prepare sampler
- GradientSampler sampler;
- if (inSettings.mRenderStrandColor == ERenderStrandColor::GravityFactor)
- sampler = GradientSampler(material.mGravityFactor);
- else if (inSettings.mRenderStrandColor == ERenderStrandColor::WorldTransformInfluence)
- sampler = GradientSampler(material.mWorldTransformInfluence);
- else if (inSettings.mRenderStrandColor == ERenderStrandColor::GridVelocityFactor)
- sampler = GradientSampler(material.mGridVelocityFactor);
- else if (inSettings.mRenderStrandColor == ERenderStrandColor::GlobalPose)
- sampler = GradientSampler(material.mGlobalPose);
- else
- sampler = GradientSampler(material.mSkinGlobalPose);
- if (strand_idx >= inSettings.mSimulationStrandBegin && strand_idx < inSettings.mSimulationStrandEnd)
- {
- RVec3 x0 = com * Vec3(render_positions[strand.mStartVtx]);
- for (uint32 v = strand.mStartVtx + 1; v < strand.mEndVtx; ++v)
- {
- RVec3 x1 = com * Vec3(render_positions[v]);
- uint32 simulated_vtx = mSettings->mRenderVertices[v].mInfluences[0].mVertexIndex;
- float factor = sampler.Sample(mSettings->mSimVertices[simulated_vtx].mStrandFraction);
- inRenderer->DrawLine(x0, x1, Color::sGreenRedGradient(factor));
- x0 = x1;
- }
- }
- }
- break;
- }
- }
- // Draw the rods
- if (inSettings.mDrawRods)
- {
- JPH_PROFILE("Draw Rods");
- Color color = Color::sRed;
- Hash<uint32> hasher;
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- RVec3 x0 = com * Vec3(positions[strand.mStartVtx]);
- for (uint32 v = strand.mStartVtx + 1; v < strand.mEndVtx; ++v)
- {
- RVec3 x1 = com * Vec3(positions[v]);
- inRenderer->DrawLine(x0, x1, color);
- x0 = x1;
- }
- color = Color(uint32(hasher(color.GetUInt32())) | 0xff000000);
- }
- }
- // Draw the rods in their unloaded pose
- if (inSettings.mDrawUnloadedRods)
- {
- JPH_PROFILE("Draw Unloaded Rods");
- Color color = Color::sYellow;
- Hash<uint32> hasher;
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- RVec3 x0 = com * Vec3(positions[strand.mStartVtx]);
- Quat rotation = mRotation * rotations[strand.mStartVtx];
- for (uint32 v = strand.mStartVtx + 1; v < strand.mEndVtx; ++v)
- {
- RVec3 x1 = x0 + rotation.RotateAxisZ() * mSettings->mSimVertices[v - 1].mLength;
- inRenderer->DrawLine(x0, x1, color);
- rotation = (rotation * Quat(mSettings->mSimVertices[v].mOmega0)).Normalized();
- x0 = x1;
- }
- color = Color(uint32(hasher(color.GetUInt32())) | 0xff000000);
- }
- }
- // Draw vertex velocities
- if (inSettings.mDrawVertexVelocity)
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- for (uint32 v = strand.mStartVtx; v < strand.mEndVtx; ++v)
- {
- Vec3 velocity(velocities[v]);
- if (velocity.LengthSq() > 1.0e-6f)
- {
- Vec3 pos = Vec3(positions[v]);
- inRenderer->DrawArrow(com * pos, com * (pos + velocity), Color::sGreen, arrow_size);
- }
- }
- }
- // Draw angular velocities
- if (inSettings.mDrawAngularVelocity)
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- for (uint32 v = strand.mStartVtx; v < strand.mEndVtx; ++v)
- {
- Vec3 angular_velocity(angular_velocities[v]);
- if (angular_velocity.LengthSq() > 1.0e-6f)
- {
- Vec3 pos = Vec3(positions[v]);
- inRenderer->DrawArrow(com * pos, com * (pos + 0.1f * angular_velocity), Color::sOrange, arrow_size);
- }
- }
- }
- // Draw rod orientations
- if (inSettings.mDrawOrientations)
- for (uint i = 0, n = (uint)mSettings->mSimStrands.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SStrand &strand = mSettings->mSimStrands[i];
- for (uint32 v = strand.mStartVtx; v < strand.mEndVtx; ++v)
- inRenderer->DrawCoordinateSystem(com * Mat44::sRotationTranslation(rotations[v], Vec3(positions[v])), arrow_size);
- }
- // Draw grid bounds
- if (inSettings.mDrawNeutralDensity || inSettings.mDrawGridDensity || inSettings.mDrawGridVelocity)
- inRenderer->DrawWireBox(com, mSettings->mSimulationBounds, Color::sGrey);
- // Draw neutral density
- if (inSettings.mDrawNeutralDensity)
- {
- Vec3 offset = mSettings->mSimulationBounds.mMin;
- Vec3 scale = mSettings->mSimulationBounds.GetSize() / Vec3(mSettings->mGridSize.ToFloat());
- float marker_size = 0.5f * scale.ReduceMin();
- for (uint32 z = 0; z < mSettings->mGridSize.GetX(); ++z)
- for (uint32 y = 0; y < mSettings->mGridSize.GetY(); ++y)
- for (uint32 x = 0; x < mSettings->mGridSize.GetZ(); ++x)
- {
- float density = mSettings->GetNeutralDensity(x, y, z);
- JPH_ASSERT(density >= 0.0f);
- if (density > 0.0f)
- {
- Vec3 pos = offset + Vec3(UVec4(x, y, z, 0).ToFloat()) * scale;
- inRenderer->DrawMarker(com * pos, Color::sGreenRedGradient(density * mSettings->mDensityScale), marker_size);
- }
- }
- }
- // Draw current density
- if (inSettings.mDrawGridDensity || inSettings.mDrawGridVelocity)
- {
- Vec3 offset = mSettings->mSimulationBounds.mMin;
- Vec3 scale = mSettings->mSimulationBounds.GetSize() / Vec3(mSettings->mGridSize.ToFloat());
- float marker_size = 0.5f * scale.ReduceMin();
- for (uint32 z = 0; z < mSettings->mGridSize.GetX(); ++z)
- for (uint32 y = 0; y < mSettings->mGridSize.GetY(); ++y)
- for (uint32 x = 0; x < mSettings->mGridSize.GetZ(); ++x)
- {
- const Float4 &velocity_and_density = grid_velocity_and_density[x + y * mSettings->mGridSize.GetX() + z * mSettings->mGridSize.GetX() * mSettings->mGridSize.GetY()];
- float density = velocity_and_density.w;
- Vec3 velocity = Vec3::sLoadFloat3Unsafe((const Float3 &)velocity_and_density);
- if (density > 0.0f)
- {
- RVec3 pos = com * (offset + Vec3(UVec4(x, y, z, 0).ToFloat()) * scale);
- if (inSettings.mDrawGridDensity)
- inRenderer->DrawMarker(pos, Color::sGreenRedGradient(density * mSettings->mDensityScale), marker_size);
- if (inSettings.mDrawGridVelocity && velocity.LengthSq() > 1.0e-6f)
- inRenderer->DrawArrow(pos, pos + com.Multiply3x3(velocity), Color::sYellow, arrow_size);
- }
- }
- }
- if (inSettings.mDrawSkinPoints)
- for (uint i = 0, n = (uint)mSettings->mSkinPoints.size(); i < n; ++i)
- if (i >= inSettings.mSimulationStrandBegin && i < inSettings.mSimulationStrandEnd)
- {
- const HairSettings::SkinPoint &sp = mSettings->mSkinPoints[i];
- const IndexedTriangleNoMaterial &tri = mSettings->mScalpTriangles[sp.mTriangleIndex];
- RVec3 v0 = com * Vec3(scalp_vertices[tri.mIdx[0]]);
- RVec3 v1 = com * Vec3(scalp_vertices[tri.mIdx[1]]);
- RVec3 v2 = com * Vec3(scalp_vertices[tri.mIdx[2]]);
- inRenderer->DrawWireTriangle(v0, v1, v2, Color::sYellow);
- RVec3 point = Real(sp.mU) * v0 + Real(sp.mV) * v1 + Real(1.0f - sp.mU - sp.mV) * v2;
- Vec3 tangent = Vec3(v1 - v0).Normalized();
- Vec3 normal = tangent.Cross(Vec3(v2 - v0)).Normalized();
- Vec3 binormal = tangent.Cross(normal);
- RMat44 basis(Vec4(normal, 0), Vec4(binormal, 0), Vec4(tangent, 0), point);
- inRenderer->DrawCoordinateSystem(basis, 0.01f);
- }
- // Draw initial gravity
- if (inSettings.mDrawInitialGravity)
- inRenderer->DrawArrow(com.GetTranslation(), com * mSettings->mInitialGravity, Color::sBlue, 0.05f * mSettings->mInitialGravity.Length());
- UnlockReadBackBuffers();
- }
- #endif // JPH_DEBUG_RENDERER
- JPH_NAMESPACE_END
|