Skip to content

Latest commit

 

History

History
179 lines (133 loc) · 5.88 KB

Description-EN.md

File metadata and controls

179 lines (133 loc) · 5.88 KB

Description-EN

Implementation

There is a FluidSystem that implements FLIP using the GVDB Library, and a FluidEmulator that links it to Unreal Engine.

The FluidSystem uses the actual CUDA Kernel to simulate particles in the environment specified by the FluidSystem, while the FluidEmulator receives the particles' vector information and passes it to Unreal Engine.

To explain it step by step,

1. Simulation Initialization

1 2

In the Unreal Engine Editor, place the obstacles as Children of the FluidEmulator as shown above and start the simulation,

bool AGVDBTestActor::InitFluidEmulator()
{
	float3 obsMaxAry[32];
	float3 obsMinAry[32];

	int i = 0;
	for (UStaticMeshComponent* SM : SMs)
	{
		const FVector relativePosition = SM->GetRelativeLocation();
		const FVector relativeScale = SM->GetRelativeScale3D();

		obsMaxAry[i] = make_float3(
			relativePosition.X + relativeScale.X * 125,
			relativePosition.Z + relativeScale.Z * 125 - relativeScale.Z * 125 * 2.35f,
			relativePosition.Y + relativeScale.Y * 125
		);
		obsMinAry[i] = make_float3(
			relativePosition.X - relativeScale.X * 125,
			relativePosition.Z - relativeScale.Z * 125 - relativeScale.Z * 125 * 2.35f,
			relativePosition.Y - relativeScale.Y * 125
		);

		i++;
	}

	return fluidEmulator.init(particleCnt, obsMinAry, obsMaxAry, obsCnt);
}

The GVDBTestActor' that owns the FluidEmulator' reads the Transforms of the objects placed as children at the start of the simulation, modifies them to match the Environment of the FluidSystem', and passes them to the FluidEmulator'.

bool FluidEmulator::init(int numpnts, float3* obsMinAry, float3* obsMaxAry, int obsCnt)
{
	...

	// Initialize
	fluid.SetDebug(false);

	gvdb.SetDebug(false);
	gvdb.SetVerbose(false);
	gvdb.SetProfile(false, true);
	gvdb.SetCudaDevice(GVDB_DEV_FIRST);
	gvdb.Initialize();

	...

	// Configure volume
	reconfigure();

	// Initialize Fluid System
	m_numpnts = numpnts;

	fluid.Initialize();
	fluid.AddObstacleInformation(obsMinAry, obsMaxAry, obsCnt);
	fluid.Start(m_numpnts);

	...
}

The FluidEmulator then takes the Transform and Particle count of the Objects as parameters and proceeds to Initialize them.

2. Collision Handling

void FluidSystem::AddObstacleInformation(float3* obsMinAry, float3* obsMaxAry, int obsCnt)
{
	m_Param[POBSCNT] = obsCnt;

	for (int i = 0; i < obsCnt; i++)
	{
		m_Ary[POBSMINARY][i] = obsMinAry[i];
		m_Ary[POBSMAXARY][i] = obsMaxAry[i];
	}

	m_Param[PGRAV] = 3.5f;
	m_Param[PVISC] = 1.50f;
}

Set the Transforms of the Objects received in the Initialize step to the Simulation Parameters.

for (int o = 0; o < fparam.obsCnt; o++)
{
	for (int j = 0; j < 10; j++)
	{
		if (fparam.obsMinAry[o].x <= pos.x && pos.x <= fparam.obsMaxAry[o].x && fparam.obsMinAry[o].y <= pos.y&& pos.y <= fparam.obsMaxAry[o].y && (fparam.obsMaxAry[o].z - 1.0f * (float)j <= pos.z || pos.z <= fparam.obsMinAry[o].z + 1.0f * (float)j))
		{
			diff = fparam.pradius - (fparam.obsMinAry[o].z + 1.0f * (float)j - pos.z) * ss;
			if (diff > 0.001f && pos.z <= fparam.obsMinAry[o].z + 1.0f * (float)j && j == 0)
			{
				norm = make_float3(0, 0, -1);
				adj = fparam.pextstiff * diff - fparam.pdamp * dot(norm, veval);
				norm *= adj; accel += norm;
				break;
			}
			else if (diff > 0.001f && pos.z <= fparam.obsMinAry[o].z + 1.0f * (float)j && j != 0)
			{
				if (vel.z > 0)
					vel.z = -std::abs(vel.z * 0.8);
				break;
			}

			...
		}
	}
}

The actual collision handling is then done in the CUDA Kernel, which uses the parameter values set above to handle collision.

3. Rendering Particle

To get the position information of these simulated Particles into Unreal Engine, we return the reverse order of Initialize: FluidSystem -> FluidEmulator -> GVDBTestActor,

void AGVDBTestActor::RenderFluidEmulation()
{
	const Vector3DF* map = fluidEmulator.display();
	TArray<FVector> buffer;
	for (int p = 0; p < fluidEmulator.m_numpnts; p++)
	{
		buffer.Add(FVector(map[p].x, map[p].y, map[p].z));
	}

	UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector(NiagaraComponent, "InputPositionArray", buffer);
}

In GVDBTestActor, insert values into Niagara's parameters as follows.

3 4

To actually render the Vector information received from the Niagara System, we created an Update Position module that sets the position of the Niagara Particles to the received Vector information. The Unique ID of the Niagara Particle was matched one-to-one with the Vector information using Index.

Particle Renderer

We used two different Renderers for rendering: Sprite Renderer and Mesh Renderer.

5

For the Sprite Renderer, we created a Sprite Material like the one above to create a fluid-like look. This method has performance benefits since it is not rendering using an actual Mesh, but it is a bit awkward to achieve the appearance of a real fluid.

Sprite Renderer Example

0.5M_Sprite.mp4

Mesh Renderer Example

0.5M_Mesh.mp4