粒子系统有一个名为updateEmitter的函数需要注意一下,它在timeStepStart函数和onInitialize()函数中被调用,用于随机生成粒子系统的粒子数据。不过在实例demo中,粒子只生成了一次,毕竟每帧重新生成粒子是非常耗时的。粒子发射器的核心代码如下。
void CalfFluidEngine::VolumeParticleEmitter3::onUpdate(double currentTimeInSeconds, double timeIntervalInSeconds) { auto particles = GetTarget(); if (particles == nullptr) { return; } if (_numberOfEmittedParticles > 0 && _isOneShot) { return; } std::vector<Vector3D> newPositions; std::vector<Vector3D> newVelocities; std::vector<Vector3D> Forces; emit(particles, &newPositions, &newVelocities); particles->AddParticles(newPositions, newVelocities, Forces); } void CalfFluidEngine::VolumeParticleEmitter3::emit(const std::shared_ptr<ParticleSystemData3>& particles, std::vector<Vector3D>* newPositions, std::vector<Vector3D>* newVelocities) { if (_surface == nullptr) return; _surface->Update(); BoundingBox3D region = _bounds; if (_surface->IsBounded()) { BoundingBox3D surfaceBox = _surface->GetBoundingBox(); region.lowerCorner = max(region.lowerCorner, surfaceBox.lowerCorner); region.upperCorner = min(region.upperCorner, surfaceBox.upperCorner); } const double j = GetJitter(); const double maxJitterDist = 0.5 * j * _spacing; if (_allowOverlapping || _isOneShot) { _pointGenerator->ForEachPoint(region, _spacing, [&](const Vector3D& point) { Vector3D randomDir = uniformSampleSphere(random(_rng), random(_rng)); Vector3D offset = maxJitterDist * randomDir; Vector3D candidate = point + offset; if (_surface->SignedDistance(candidate) <= 0.0) { if (_numberOfEmittedParticles < _maxNumberOfParticles) { newPositions->push_back(candidate); ++_numberOfEmittedParticles; } else { return false; } } return true; }); } else { PointHashGridSearcher3 neighborSearcher( Vector3<size_t>( kDefaultHashGridResolution, kDefaultHashGridResolution, kDefaultHashGridResolution), 2.0 * _spacing); if (!_allowOverlapping) { neighborSearcher.Build(particles->GetPositions()); } _pointGenerator->ForEachPoint(region, _spacing, [&](const Vector3D& point) { Vector3D randomDir = uniformSampleSphere(random(_rng), random(_rng)); Vector3D offset = maxJitterDist * randomDir; Vector3D candidate = point + offset; if (_surface->SignedDistance(candidate) <= 0.0 && (!_allowOverlapping &&!neighborSearcher.HasNearbyPoint(candidate, _spacing))) { if (_numberOfEmittedParticles < _maxNumberOfParticles) { newPositions->push_back(candidate); neighborSearcher.Add(candidate); ++_numberOfEmittedParticles; } else { return false; } } return true; }); } newVelocities->resize(newPositions->size()); tbb::parallel_for( tbb::blocked_range<size_t>(0, newVelocities->size()), [&](const tbb::blocked_range<size_t> & b) { for (size_t i = b.begin(); i != b.end(); ++i) (*newVelocities)[i] = velocityAt((*newPositions)[i]); }); } Vector3D CalfFluidEngine::VolumeParticleEmitter3::velocityAt(const Vector3D & point) const { Vector3D r = point - _surface->transform.GetTranslation(); return _linearVel + Vector3D::Cross(_angularVel,r) + _initialVel; } void CalfFluidEngine::BccLatticePointGenerator::ForEachPoint(const BoundingBox3D & boundingBox, double spacing, const std::function<bool(const Vector3D&)>& callback) const { double halfSpacing = spacing / 2.0; double boxWidth = boundingBox.GetWidth(); double boxHeight = boundingBox.GetHeight(); double boxDepth = boundingBox.GetDepth(); Vector3D position; bool hasOffset = false; bool shouldQuit = false; for (int k = 0; k * halfSpacing <= boxDepth && !shouldQuit; ++k) { position.z = k * halfSpacing + boundingBox.lowerCorner.z; double offset = (hasOffset) ? halfSpacing : 0.0; for (int j = 0; j * spacing + offset <= boxHeight && !shouldQuit; ++j) { position.y = j * spacing + offset + boundingBox.lowerCorner.y; for (int i = 0; i * spacing + offset <= boxWidth; ++i) { position.x = i * spacing + offset + boundingBox.lowerCorner.x; if (!callback(position)) { shouldQuit = true; break; } } } hasOffset = !hasOffset; } } 邻域粒子搜索《Fluid Engine Development》 学习笔记2-基礎 (4)
内容版权声明:除非注明,否则皆为本站原创文章。