private static void SetParticleParam(PartSysEmitter emitter, int particleIdx, PartSysParamId id, float atLifetime, ParticleStateField stateField, float defaultValue) { var value = GetParticleValue(emitter, particleIdx, id, atLifetime, defaultValue); emitter.GetParticleState().SetState(stateField, particleIdx, value); }
private static float GetParticleValue(PartSysEmitter emitter, int particleIdx, PartSysParamId paramId, float atLifetime, float defaultValue = 0.0f) { var state = emitter.GetParamState(paramId); return(state?.GetValue(emitter, particleIdx, atLifetime) ?? defaultValue); }
private static byte GetParticleColorComponent(ParticleStateField stateField, PartSysParamId paramId, PartSysEmitter emitter, int particleIdx) { var colorParam = emitter.GetParamState(paramId); byte value; if (colorParam != null) { var partAge = emitter.GetParticleAge(particleIdx); var partColor = colorParam.GetValue(emitter, particleIdx, partAge); partColor += emitter.GetParticleState().GetState(stateField, particleIdx); if (partColor >= 255) { value = 255; } else if (partColor < 0) { value = 0; } else { value = (byte)partColor; } } else { value = (byte)emitter.GetParticleState().GetState(stateField, particleIdx); } return(value); }
public override float GetValue(PartSysEmitter emitter, int particleIdx, float lifetimeSec) { for (var i = 0; i < _frames.Length - 1; ++i) { var frame = _frames[i]; var nextFrame = _frames[i + 1]; // Don't LERP if we're right on the frame border // (or in case of the first frame, possibly earlier) if (lifetimeSec <= frame.start) { return(frame.value); } else if (lifetimeSec >= nextFrame.start) { continue; // The lifetime is beyond the current keyframe gap } // The lifetime is between start of frame and start of nextFrame // So let's lerp the value. var timeSinceFrame = lifetimeSec - frame.start; return(frame.value + frame.deltaPerSec * timeSinceFrame); } // Return the value of the last frame since we seem to be beyond it return(_frames[^ 1].value);
public override void Render(IGameViewport viewport, PartSysEmitter emitter) { if (!GetEmitterWorldMatrix(viewport, emitter, out var worldMatrix)) { return; } _device.SetVertexShaderConstants(0, ref worldMatrix); _device.SetIndexBuffer(_indexBuffer); RenderParticles(emitter); }
public static void SimulateParticleAging(PartSysEmitter emitter, float timeToSimulateSec) { var it = emitter.NewIterator(); var ages = emitter.GetParticles(); while (it.HasNext()) { var particleIdx = it.Next(); ages[particleIdx] += timeToSimulateSec; } emitter.PruneExpiredParticles(); }
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 PackedLinearColorA GetParticleColor(PartSysEmitter emitter, int particleIdx) { var red = GetParticleColorComponent(ParticleStateField.PSF_RED, PartSysParamId.part_red, emitter, particleIdx); var green = GetParticleColorComponent(ParticleStateField.PSF_GREEN, PartSysParamId.part_green, emitter, particleIdx); var blue = GetParticleColorComponent(ParticleStateField.PSF_BLUE, PartSysParamId.part_blue, emitter, particleIdx); var alpha = GetParticleColorComponent(ParticleStateField.PSF_ALPHA, PartSysParamId.part_alpha, emitter, particleIdx); return(new PackedLinearColorA(red, green, blue, alpha)); }
private void RenderParticles(PartSysEmitter emitter) { var it = emitter.NewIterator(); var totalCount = emitter.GetActiveCount(); if (totalCount == 0) { return; } // Lazily initialize render state if (!emitter.HasRenderState()) { emitter.SetRenderState( new QuadEmitterRenderState(_device, emitter)); } var renderState = (QuadEmitterRenderState)emitter.GetRenderState(); using var spriteMemory = MemoryPool.Rent(4 * totalCount); var sUpdateBuffer = spriteMemory.Memory.Span.Slice(0, 4 * totalCount); var i = 0; while (it.HasNext()) { var particleIdx = it.Next(); FillVertex(emitter, particleIdx, sUpdateBuffer, i); i += 4; } renderState.vertexBuffer.Resource.Update <SpriteVertex>(sUpdateBuffer); _device.SetMaterial(renderState.material); renderState.bufferBinding.Resource.Bind(); // Draw the batch _device.DrawIndexed(PrimitiveType.TriangleList, 4 * totalCount, 6 * totalCount); }
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 GeneralEmitterRenderState(RenderingDevice device, PartSysEmitter emitter, bool pointSprites) { material = CreateMaterial(device, emitter, pointSprites).Ref(); }
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; } }
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 abstract void FillVertex(PartSysEmitter emitter, int particleIdx, Span <SpriteVertex> vertices, int vertexIdx);
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 abstract float GetValue(PartSysEmitter emitter, int particleIdx, float lifetimeSec);
public ParticleValueSource(PartSysEmitter emitter, int particleIdx, float particleAge) { this.emitter = emitter; this.particleIdx = particleIdx; this.particleAge = particleAge; }
protected override void FillVertex(PartSysEmitter emitter, int particleIdx, Span <SpriteVertex> vertices, int vertexIdx) { // Calculate the particle scale (default is 1) var scale = 1.0f; var scaleParam = emitter.GetParamState(PartSysParamId.part_scale_X); if (scaleParam != null) { scale = scaleParam.GetValue(emitter, particleIdx, emitter.GetParticleAge(particleIdx)); } Vector4 halfPartHeightX; Vector4 halfPartHeightY; var rotationParam = emitter.GetParamState(PartSysParamId.part_yaw); if (rotationParam != null) { var rotation = rotationParam.GetValue( emitter, particleIdx, emitter.GetParticleAge(particleIdx)); rotation += emitter.GetParticleState().GetState(ParticleStateField.PSF_ROTATION, particleIdx); rotation = Angles.ToRadians(rotation); var cosRot = MathF.Cos(rotation) * scale; var sinRot = MathF.Sin(rotation) * scale; halfPartHeightX = screenSpaceUnitX * cosRot - screenSpaceUnitY * sinRot; halfPartHeightY = screenSpaceUnitY * cosRot + screenSpaceUnitX * sinRot; } else { halfPartHeightX = screenSpaceUnitX * scale; halfPartHeightY = screenSpaceUnitY * scale; } var partPos = new Vector4( emitter.GetParticleState().GetState(ParticleStateField.PSF_POS_VAR_X, particleIdx), emitter.GetParticleState().GetState(ParticleStateField.PSF_POS_VAR_Y, particleIdx), emitter.GetParticleState().GetState(ParticleStateField.PSF_POS_VAR_Z, particleIdx), 1 ); // Upper left corner vertices[vertexIdx + 0].pos = partPos - halfPartHeightX + halfPartHeightY; // Upper right corner vertices[vertexIdx + 1].pos = partPos + halfPartHeightX + halfPartHeightY; // Lower right corner vertices[vertexIdx + 2].pos = partPos + halfPartHeightX - halfPartHeightY; // Lower left corner vertices[vertexIdx + 3].pos = partPos - halfPartHeightX - halfPartHeightY; // Set the diffuse color for all corners var diffuse = GeneralEmitterRenderState.GetParticleColor(emitter, particleIdx); vertices[vertexIdx + 0].diffuse = diffuse; vertices[vertexIdx + 1].diffuse = diffuse; vertices[vertexIdx + 2].diffuse = diffuse; vertices[vertexIdx + 3].diffuse = diffuse; // Set UV coordinates for the sprite. We need to do this every frame // because we're using DISCARD for locking vertices[vertexIdx + 0].u = 0; vertices[vertexIdx + 0].v = 1; vertices[vertexIdx + 1].u = 1; vertices[vertexIdx + 1].v = 1; vertices[vertexIdx + 2].u = 1; vertices[vertexIdx + 2].v = 0; vertices[vertexIdx + 3].u = 0; vertices[vertexIdx + 3].v = 0; }
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 abstract void Render(IGameViewport viewport, PartSysEmitter emitter);