public QuadEmitterRenderState(RenderingDevice device, PartSysEmitter emitter) : base(device, emitter, false) { bufferBinding = new BufferBinding(device, material.Resource.VertexShader).Ref(); var maxCount = emitter.GetSpec().GetMaxParticles(); vertexBuffer = device.CreateEmptyVertexBuffer(SpriteVertex.Size * 4 * maxCount, debugName:"ParticlesQuadEmitter"); bufferBinding.Resource .AddBuffer<SpriteVertex>(vertexBuffer, 0) .AddElement(VertexElementType.Float4, VertexElementSemantic.Position) .AddElement(VertexElementType.Color, VertexElementSemantic.Color) .AddElement(VertexElementType.Float2, VertexElementSemantic.TexCoord); }
public static Material CreateMaterial(RenderingDevice device, PartSysEmitter emitter, bool pointSprites) { var blendState = new BlendSpec(); blendState.blendEnable = true; switch (emitter.GetSpec().GetBlendMode()) { case PartSysBlendMode.Add: blendState.srcBlend = BlendOperand.SrcAlpha; blendState.destBlend = BlendOperand.One; break; case PartSysBlendMode.Subtract: blendState.srcBlend = BlendOperand.Zero; blendState.destBlend = BlendOperand.InvSrcAlpha; break; case PartSysBlendMode.Blend: blendState.srcBlend = BlendOperand.SrcAlpha; blendState.destBlend = BlendOperand.InvSrcAlpha; break; case PartSysBlendMode.Multiply: blendState.srcBlend = BlendOperand.Zero; blendState.destBlend = BlendOperand.SrcColor; break; default: break; } // Particles respect the depth buffer, but do not modify it DepthStencilSpec depthStencilState = new DepthStencilSpec(); depthStencilState.depthEnable = true; depthStencilState.depthWrite = false; RasterizerSpec rasterizerState = new RasterizerSpec(); rasterizerState.cullMode = CullMode.None; var samplers = new List <MaterialSamplerSpec>(); var shaderName = "diffuse_only_ps"; var textureName = emitter.GetSpec().GetTextureName(); if (textureName.Length > 0) { var samplerState = new SamplerSpec { addressU = TextureAddress.Clamp, addressV = TextureAddress.Clamp, minFilter = TextureFilterType.Linear, magFilter = TextureFilterType.Linear, mipFilter = TextureFilterType.Linear }; var texture = device.GetTextures().Resolve(textureName, true); samplers.Add(new MaterialSamplerSpec(texture, samplerState)); shaderName = "textured_simple_ps"; } using var pixelShader = device.GetShaders().LoadPixelShader(shaderName); var vsName = pointSprites ? "particles_points_vs" : "particles_quads_vs"; using var vertexShader = device.GetShaders().LoadVertexShader(vsName); return(device.CreateMaterial(blendState, depthStencilState, rasterizerState, samplers.ToArray(), vertexShader, pixelShader)); }
public override void Render(IGameViewport viewport, PartSysEmitter emitter) { var it = emitter.NewIterator(); var animParams = AnimatedModelParams.Default; animParams.rotation3d = true; // Lazily initialize render state if (!emitter.HasRenderState()) { // Resolve the mesh filename var baseName = ResolveBasename(emitter.GetSpec().GetMeshName()); var skmName = baseName + ".skm"; var skaName = baseName + ".ska"; try { var animId = new EncodedAnimId(NormalAnimType.ItemIdle); // This seems to be item_idle var model = _modelFactory.FromFilenames(skmName, skaName, animId, animParams); emitter.SetRenderState( new ModelEmitterRenderState(model) ); } catch (Exception e) { Logger.Error("Unable to load model {0} for particle system {1}: {2}", baseName, emitter.GetSpec().GetParent().GetName(), e); emitter.SetRenderState(new ModelEmitterRenderState(null)); } } var renderState = (ModelEmitterRenderState)emitter.GetRenderState(); if (renderState.Model == null) { return; // The loader above was unable to load the model for this emitter } var overrides = new MdfRenderOverrides { ignoreLighting = true, overrideDiffuse = true }; var yaw = emitter.GetParamState(PartSysParamId.part_yaw); var pitch = emitter.GetParamState(PartSysParamId.part_pitch); var roll = emitter.GetParamState(PartSysParamId.part_roll); while (it.HasNext()) { var particleIdx = it.Next(); var age = emitter.GetParticleAge(particleIdx); overrides.overrideColor = GeneralEmitterRenderState.GetParticleColor(emitter, particleIdx); // Yes, this is *actually* swapped for Y / Z var particleState = emitter.GetParticleState(); animParams.offsetX = particleState.GetState(ParticleStateField.PSF_POS_VAR_X, particleIdx); animParams.offsetY = particleState.GetState(ParticleStateField.PSF_POS_VAR_Z, particleIdx); animParams.offsetZ = particleState.GetState(ParticleStateField.PSF_POS_VAR_Y, particleIdx); if (yaw != null) { animParams.rotationYaw = Angles.ToRadians(yaw.GetValue(emitter, particleIdx, age)); } if (pitch != null) { animParams.rotationPitch = Angles.ToRadians(pitch.GetValue(emitter, particleIdx, age)); } if (roll != null) { animParams.rotationRoll = Angles.ToRadians(roll.GetValue(emitter, particleIdx, age)); } renderState.Model.SetTime(animParams, age); _modelRenderer.Render(viewport, renderState.Model, animParams, new List <Light3d>(), overrides); } }
protected bool GetEmitterWorldMatrix(IGameViewport viewport, PartSysEmitter emitter, out Matrix4x4 worldMatrix) { worldMatrix = Matrix4x4.Identity; var spec = emitter.GetSpec(); var particleSpace = spec.GetParticleSpace(); var emitterSpace = spec.GetSpace(); if (particleSpace == PartSysParticleSpace.SameAsEmitter) { if (emitterSpace == PartSysEmitterSpace.ObjectPos || emitterSpace == PartSysEmitterSpace.ObjectYpr) { Matrix4x4 localMat; if (emitterSpace == PartSysEmitterSpace.ObjectYpr) { var angle = emitter.GetObjRotation() + MathF.PI; localMat = Matrix4x4.CreateRotationY(angle); } else { localMat = Matrix4x4.Identity; } // Set the translation component of the transformation matrix localMat.Translation = emitter.GetObjPos(); worldMatrix = localMat * viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors(worldMatrix); return(true); } if (emitterSpace == PartSysEmitterSpace.NodePos || emitterSpace == PartSysEmitterSpace.NodeYpr) { var external = emitter.External; Matrix4x4 boneMatrix; if (emitterSpace == PartSysEmitterSpace.NodeYpr) { if (!external.GetBoneWorldMatrix(emitter.GetAttachedTo(), spec.GetNodeName(), out boneMatrix)) { // This effectively acts as a fallback if the bone doesn't exist boneMatrix = Matrix4x4.CreateTranslation(emitter.GetObjPos()); } worldMatrix = boneMatrix * viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors(worldMatrix); return(true); } if (external.GetBoneWorldMatrix(emitter.GetAttachedTo(), spec.GetNodeName(), out boneMatrix)) { worldMatrix = Matrix4x4.CreateTranslation(boneMatrix.Translation) * viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors(worldMatrix); return(true); } return(false); } worldMatrix = viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors(worldMatrix); return(true); } if (particleSpace == PartSysParticleSpace.World) { worldMatrix = viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors(worldMatrix); return(true); } if (emitterSpace != PartSysEmitterSpace.ObjectPos && emitterSpace != PartSysEmitterSpace.ObjectYpr) { if (emitterSpace != PartSysEmitterSpace.NodePos && emitterSpace != PartSysEmitterSpace.NodeYpr) { return(true); } var external = emitter.External; Matrix4x4 boneMatrix; if (emitterSpace == PartSysEmitterSpace.NodeYpr) { // Use the entire bone matrix if possible external.GetBoneWorldMatrix(emitter.GetAttachedTo(), spec.GetNodeName(), out boneMatrix); } else { // Only use the bone translation part if (!external.GetBoneWorldMatrix(emitter.GetAttachedTo(), spec.GetNodeName(), out boneMatrix)) { return(false); } boneMatrix = Matrix4x4.CreateTranslation(boneMatrix.Translation); // TODO: This might not be needed... } worldMatrix = viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors2(boneMatrix); return(true); } Matrix4x4 matrix; if (emitterSpace == PartSysEmitterSpace.ObjectYpr) { var angle = emitter.GetObjRotation() + MathF.PI; matrix = Matrix4x4.CreateRotationY(angle); } else { matrix = Matrix4x4.Identity; } worldMatrix = viewport.Camera.GetViewProj(); ExtractScreenSpaceUnitVectors2(matrix); return(true); }
public static void SimulateParticleSpawn(IPartSysExternal external, PartSysEmitter emitter, int particleIdx, float timeToSimulate) { var spec = emitter.GetSpec(); var partSpawnTime = emitter.GetParticleSpawnTime(particleIdx); var worldPosVar = emitter.GetWorldPosVar(); var particleX = worldPosVar.X; var particleY = worldPosVar.Y; var particleZ = worldPosVar.Z; var emitOffsetX = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_offset_X, partSpawnTime); var emitOffsetY = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_offset_Y, partSpawnTime); var emitOffsetZ = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_offset_Z, partSpawnTime); if (spec.GetOffsetCoordSys() == PartSysCoordSys.Polar) { var coords = SphericalDegToCartesian(emitOffsetX, emitOffsetY, emitOffsetZ); particleX += coords.X; particleY += coords.Y; particleZ += coords.Z; } else { particleX += emitOffsetX; particleY += emitOffsetY; particleZ += emitOffsetZ; } switch (spec.GetSpace()) { case PartSysEmitterSpace.ObjectPos: case PartSysEmitterSpace.ObjectYpr: { if (spec.GetParticleSpace() != PartSysParticleSpace.SameAsEmitter) { // TODO: Figure out this formula... var scale = 1.0f - emitter.GetParticleAge(particleIdx) / timeToSimulate; var prevObjPos = emitter.GetPrevObjPos(); var objPos = emitter.GetObjPos(); var dX = prevObjPos.X + (objPos.X - prevObjPos.X) * scale; var dY = prevObjPos.Y + (objPos.Y - prevObjPos.Y) * scale; var dZ = prevObjPos.Z + (objPos.Z - prevObjPos.Z) * scale; var rotation = 0.0f; if (spec.GetSpace() == PartSysEmitterSpace.ObjectYpr) { rotation = emitter.GetPrevObjRotation() + (emitter.GetObjRotation() - emitter.GetPrevObjRotation()) * scale; } RotateAndMove(dX, dY, dZ, rotation, ref particleX, ref particleY, ref particleZ); } } break; case PartSysEmitterSpace.Bones: { var scale = 1.0f - emitter.GetParticleAge(particleIdx) / timeToSimulate; if (emitter.GetBoneState() == null) { break; } if (emitter.GetBoneState().GetRandomPos(scale, out var bonePos)) { particleX = bonePos.X; particleY = bonePos.Y; particleZ = bonePos.Z; } } break; case PartSysEmitterSpace.NodePos: case PartSysEmitterSpace.NodeYpr: { if (spec.GetParticleSpace() != PartSysParticleSpace.SameAsEmitter) { var scale = 1.0f - emitter.GetParticleAge(particleIdx) / timeToSimulate; if (spec.GetSpace() == PartSysEmitterSpace.NodeYpr) { if (!external.GetBoneWorldMatrix(emitter.GetAttachedTo(), spec.GetNodeName(), out var boneM)) { boneM = Matrix4x4.Identity; } var ppos = new Vector3(particleX, particleY, particleZ); var newpos = Vector3.Transform(ppos, boneM); particleX = newpos.X; particleY = newpos.Y; particleZ = newpos.Z; } else { var prevObjPos = emitter.GetPrevObjPos(); var objPos = emitter.GetObjPos(); var dX = prevObjPos.X + (objPos.X - prevObjPos.X) * scale; var dY = prevObjPos.Y + (objPos.Y - prevObjPos.Y) * scale; var dZ = prevObjPos.Z + (objPos.Z - prevObjPos.Z) * scale; RotateAndMove(dX, dY, dZ, 0.0f, ref particleX, ref particleY, ref particleZ); } } } break; default: break; } var state = emitter.GetParticleState(); state.SetState(ParticleStateField.PSF_X, particleIdx, particleX); state.SetState(ParticleStateField.PSF_Y, particleIdx, particleY); state.SetState(ParticleStateField.PSF_Z, particleIdx, particleZ); // Initialize particle color SetParticleParam(emitter, particleIdx, PartSysParamId.emit_init_red, partSpawnTime, ParticleStateField.PSF_RED, 255.0f); SetParticleParam(emitter, particleIdx, PartSysParamId.emit_init_green, partSpawnTime, ParticleStateField.PSF_GREEN, 255.0f); SetParticleParam(emitter, particleIdx, PartSysParamId.emit_init_blue, partSpawnTime, ParticleStateField.PSF_BLUE, 255.0f); SetParticleParam(emitter, particleIdx, PartSysParamId.emit_init_alpha, partSpawnTime, ParticleStateField.PSF_ALPHA, 255.0f); // Initialize particle velocity var partVelX = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_init_vel_X, partSpawnTime); var partVelY = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_init_vel_Y, partSpawnTime); var partVelZ = GetParticleValue(emitter, particleIdx, PartSysParamId.emit_init_vel_Z, partSpawnTime); if (spec.GetSpace() == PartSysEmitterSpace.ObjectYpr) { if (spec.GetParticleSpace() != PartSysParticleSpace.SameAsEmitter) { var scale = 1.0f - emitter.GetParticleAge(particleIdx) / timeToSimulate; var rotation = 0.0f; if (spec.GetSpace() == PartSysEmitterSpace.ObjectYpr) { rotation = (emitter.GetObjRotation() - emitter.GetPrevObjRotation()) * scale + emitter.GetPrevObjRotation(); } // Rotate the velocity vector according to the current object rotation in the world // TODO: Even for rotation == 0, this will flip the velocity vector Rotate2D(rotation, ref partVelX, ref partVelZ); } } else if (spec.GetSpace() == PartSysEmitterSpace.NodeYpr) { if (spec.GetParticleSpace() != PartSysParticleSpace.SameAsEmitter) { var objId = emitter.GetAttachedTo(); if (!external.GetBoneWorldMatrix(objId, spec.GetNodeName(), out var boneMatrix)) { boneMatrix = Matrix4x4.Identity; } // Construct a directional vector (not a positional one, w=0 here) for the velocity var dirVec = new Vector3(partVelX, partVelY, partVelZ); dirVec = Vector3.TransformNormal(dirVec, boneMatrix); partVelX = dirVec.X; partVelY = dirVec.Y; partVelZ = dirVec.Z; } } // Are particle coordinates defined as polar coordinates? Convert them to cartesian here if (spec.GetParticleVelocityCoordSys() == PartSysCoordSys.Polar) { var cartesianVel = SphericalDegToCartesian(partVelX, partVelY, partVelZ); partVelX = cartesianVel.X; partVelY = cartesianVel.Y; partVelZ = cartesianVel.Z; } state.SetState(ParticleStateField.PSF_VEL_X, particleIdx, partVelX); state.SetState(ParticleStateField.PSF_VEL_Y, particleIdx, partVelY); state.SetState(ParticleStateField.PSF_VEL_Z, particleIdx, partVelZ); // I don't know why it's taken at lifetime 0. // TODO: Figure out if this actually *never* changes? var posVarX = GetParticleValue(emitter, particleIdx, PartSysParamId.part_posVariation_X, 0); var posVarY = GetParticleValue(emitter, particleIdx, PartSysParamId.part_posVariation_Y, 0); var posVarZ = GetParticleValue(emitter, particleIdx, PartSysParamId.part_posVariation_Z, 0); // For a polar system, convert these positions to cartesian to apply them to the // rendering position if (spec.GetParticlePosCoordSys() == PartSysCoordSys.Polar) { state.SetState(ParticleStateField.PSF_POS_AZIMUTH, particleIdx, 0); state.SetState(ParticleStateField.PSF_POS_INCLINATION, particleIdx, 0); state.SetState(ParticleStateField.PSF_POS_RADIUS, particleIdx, 0); // Convert to cartesian and add to the actual current particle position // As usual, x, y, z here are (azimuth, inclination, radius) var cartesianPos = SphericalDegToCartesian(posVarX, posVarY, posVarZ); posVarX = cartesianPos.X; posVarY = cartesianPos.Y; posVarZ = cartesianPos.Z; } // Apply the position variation to the initial position and store it posVarX += particleX; posVarY += particleY; posVarZ += particleZ; state.SetState(ParticleStateField.PSF_POS_VAR_X, particleIdx, posVarX); state.SetState(ParticleStateField.PSF_POS_VAR_Y, particleIdx, posVarY); state.SetState(ParticleStateField.PSF_POS_VAR_Z, particleIdx, posVarZ); /* * The following code will apply particle movement after * spawning a particle retroactively. This should only happen * for high frequency particle systems that spawn multiple particles * per frame. * Also note how particle age instead of particle lifetime is used here * to access parameters of the emitter. */ var partAge = emitter.GetParticleAge(particleIdx); if (partAge != 0) { var partAgeSquared = partAge * partAge; particleX += partVelX * partAge; particleY += partVelY * partAge; particleZ += partVelZ * partAge; var param = emitter.GetParamState(PartSysParamId.part_accel_X); if (param != null) { var accelX = param.GetValue(emitter, particleIdx, partAge); particleX += accelX * partAgeSquared * 0.5f; partVelX += accelX * partAge; } param = emitter.GetParamState(PartSysParamId.part_accel_Y); if (param != null) { var accelY = param.GetValue(emitter, particleIdx, partAge); particleY += accelY * partAgeSquared * 0.5f; partVelY += accelY * partAge; } param = emitter.GetParamState(PartSysParamId.part_accel_Z); if (param != null) { var accelZ = param.GetValue(emitter, particleIdx, partAge); particleZ += accelZ * partAgeSquared * 0.5f; partVelZ += accelZ * partAge; } state.SetState(ParticleStateField.PSF_VEL_X, particleIdx, partVelX); state.SetState(ParticleStateField.PSF_VEL_Y, particleIdx, partVelY); state.SetState(ParticleStateField.PSF_VEL_Z, particleIdx, partVelZ); param = emitter.GetParamState(PartSysParamId.part_velVariation_X); if (param != null) { particleX += param.GetValue(emitter, particleIdx, partAge) * partAge; } param = emitter.GetParamState(PartSysParamId.part_velVariation_Y); if (param != null) { particleY += param.GetValue(emitter, particleIdx, partAge) * partAge; } param = emitter.GetParamState(PartSysParamId.part_velVariation_Z); if (param != null) { particleZ += param.GetValue(emitter, particleIdx, partAge) * partAge; } state.SetState(ParticleStateField.PSF_X, particleIdx, particleX); state.SetState(ParticleStateField.PSF_Y, particleIdx, particleY); state.SetState(ParticleStateField.PSF_Z, particleIdx, particleZ); } // Simulate rotation for anything other than a point particle if (spec.GetParticleType() != PartSysParticleType.Point) { var emitterAge = emitter.GetParticleSpawnTime(particleIdx); var rotation = emitter.GetParamValue(PartSysParamId.emit_yaw, particleIdx, emitterAge, 0.0f); emitter.GetParticleState().SetState(ParticleStateField.PSF_ROTATION, particleIdx, rotation); } }
public static void SimulateParticleMovement(PartSysEmitter emitter, float timeToSimulateSecs) { var spec = emitter.GetSpec(); // Used as a factor in integrating the acceleration to retroactively calculate // its influence on the particle position var accelIntegrationFactor = timeToSimulateSecs * timeToSimulateSecs * 0.5f; var state = emitter.GetParticleState(); var it = emitter.NewIterator(); while (it.HasNext()) { var particleIdx = it.Next(); var particleAge = emitter.GetParticleAge(particleIdx); var valueSource = new ParticleValueSource(emitter, particleIdx, particleAge); var x = state.GetState(ParticleStateField.PSF_X, particleIdx); var y = state.GetState(ParticleStateField.PSF_Y, particleIdx); var z = state.GetState(ParticleStateField.PSF_Z, particleIdx); var velX = state.GetState(ParticleStateField.PSF_VEL_X, particleIdx); var velY = state.GetState(ParticleStateField.PSF_VEL_Y, particleIdx); var velZ = state.GetState(ParticleStateField.PSF_VEL_Z, particleIdx); // Calculate new position of particle based on velocity x += timeToSimulateSecs * velX; y += timeToSimulateSecs * velY; z += timeToSimulateSecs * velZ; // Apply acceleration to velocity (retroactively to position as well) float value; if (valueSource.GetValue(PartSysParamId.part_accel_X, out value)) { x += accelIntegrationFactor * value; velX += timeToSimulateSecs * value; } if (valueSource.GetValue(PartSysParamId.part_accel_Y, out value)) { y += accelIntegrationFactor * value; velY += timeToSimulateSecs * value; } if (valueSource.GetValue(PartSysParamId.part_accel_Z, out value)) { z += accelIntegrationFactor * value; velZ += timeToSimulateSecs * value; } /* * Apply Velocity Var */ if (spec.GetParticleVelocityCoordSys() == PartSysCoordSys.Polar) { if (spec.GetParticlePosCoordSys() != PartSysCoordSys.Polar) { // Velocity is polar, positions are not . convert velocity var azimuth = emitter.GetParamValue(PartSysParamId.part_velVariation_X, particleIdx, particleAge); var inclination = emitter.GetParamValue(PartSysParamId.part_velVariation_Y, particleIdx, particleAge); var radius = emitter.GetParamValue(PartSysParamId.part_velVariation_Z, particleIdx, particleAge); var cartesianVel = SphericalDegToCartesian(azimuth, inclination, radius); x += cartesianVel.X * timeToSimulateSecs; y += cartesianVel.Y * timeToSimulateSecs; z += cartesianVel.Z * timeToSimulateSecs; } else { // Modify the spherical coordinates of the particle directly if (valueSource.GetValue(PartSysParamId.part_velVariation_X, out value)) { ref var azimuth = ref state.GetStatePtr(ParticleStateField.PSF_POS_AZIMUTH, particleIdx); azimuth += value * timeToSimulateSecs; } if (valueSource.GetValue(PartSysParamId.part_velVariation_Y, out value)) { ref var inclination = ref state.GetStatePtr(ParticleStateField.PSF_POS_INCLINATION, particleIdx); inclination += value * timeToSimulateSecs; } if (valueSource.GetValue(PartSysParamId.part_velVariation_Z, out value)) { ref var radius = ref state.GetStatePtr(ParticleStateField.PSF_POS_RADIUS, particleIdx); radius += value * timeToSimulateSecs; } }