/// <inheritdoc/> public void DrawBillboard(ref BillboardArgs billboard, PackedTexture texture) { int index, dummy; _renderBatch.Submit(PrimitiveType.TriangleList, 4, 6, out index, out dummy); OnDrawBillboard(ref billboard, texture, _renderBatch.Vertices, index); }
private void DrawParticlesOldToNew(ParticleSystemData particleSystemData, bool requiresTransformation, ref Vector3 scale, ref Pose pose, ref Vector3 color, float alpha, float angleOffset) { var b = new BillboardArgs { Orientation = particleSystemData.BillboardOrientation, Softness = particleSystemData.Softness, ReferenceAlpha = particleSystemData.AlphaTest, }; int numberOfParticles = particleSystemData.Particles.Count; var particles = particleSystemData.Particles.Array; bool isViewPlaneAligned = (particleSystemData.BillboardOrientation.Normal == BillboardNormal.ViewPlaneAligned); bool isAxisInViewSpace = particleSystemData.BillboardOrientation.IsAxisInViewSpace; for (int i = 0; i < numberOfParticles; i++) { if (particles[i].IsAlive) // Skip dead particles. { if (requiresTransformation) { b.Position = pose.ToWorldPosition(particles[i].Position * scale); b.Normal = isViewPlaneAligned ? _defaultNormal : pose.ToWorldDirection(particles[i].Normal); b.Axis = isAxisInViewSpace ? particles[i].Axis : pose.ToWorldDirection(particles[i].Axis); b.Size = particles[i].Size * scale.Y; // Assume uniform scale for size. } else { b.Position = particles[i].Position; b.Normal = isViewPlaneAligned ? _defaultNormal : particles[i].Normal; b.Axis = particles[i].Axis; b.Size = particles[i].Size; } b.Angle = particles[i].Angle + angleOffset; b.Color = particles[i].Color * color; b.Alpha = particles[i].Alpha * alpha; b.AnimationTime = particles[i].AnimationTime; b.BlendMode = particles[i].BlendMode; var texture = particleSystemData.Texture ?? _debugTexture; _billboardBatch.DrawBillboard(ref b, texture); } } }
private void Draw(BillboardNode node) { var billboard = (ImageBillboard)node.Billboard; var data = new BillboardArgs { Position = node.PoseWorld.Position, Normal = (billboard.Orientation.Normal == BillboardNormal.ViewPlaneAligned) ? _defaultNormal : node.Normal, Axis = node.Axis, Orientation = billboard.Orientation, Size = node.ScaleWorld.Y * billboard.Size, // Assume uniform scale for size. Softness = Numeric.IsNaN(billboard.Softness) ? -1 : billboard.Softness, Color = node.Color * billboard.Color, Alpha = node.Alpha * billboard.Alpha, ReferenceAlpha = billboard.AlphaTest, AnimationTime = (Numeric.IsNaN(node.AnimationTime)) ? billboard.AnimationTime : node.AnimationTime, BlendMode = billboard.BlendMode, }; var texture = billboard.Texture ?? _debugTexture; _billboardBatch.DrawBillboard(ref data, texture); }
protected override void OnDrawBillboard(ref BillboardArgs b, PackedTexture texture, BillboardVertex[] vertices, int index) { // Bottom left vertex var v = new BillboardVertex(); v.Position = b.Position; v.Normal = b.Normal; v.Axis = b.Axis; v.Color3F = b.Color; v.Alpha = b.Alpha; v.TextureCoordinate = new Vector2F(0, 1); v.Orientation = b.Orientation; v.Angle = b.Angle; v.Size = b.Size; v.Softness = b.Softness; v.ReferenceAlpha = b.ReferenceAlpha; v.AnimationTime = b.AnimationTime; v.BlendMode = b.BlendMode; v.Texture = texture; vertices[index] = v; index++; // Top left vertex v.TextureCoordinate.Y = 0; vertices[index] = v; index++; // Top right vertex v.TextureCoordinate.X = 1; vertices[index] = v; index++; // Bottom right vertex v.TextureCoordinate.Y = 1; vertices[index] = v; }
private void DrawParticlesBackToFront(ParticleSystemData particleSystemData, bool requiresTransformation, ref Vector3 scale, ref Pose pose, ref Vector3 color, float alpha, float angleOffset) { var b = new BillboardArgs { Orientation = particleSystemData.BillboardOrientation, Softness = particleSystemData.Softness, ReferenceAlpha = particleSystemData.AlphaTest, }; int numberOfParticles = particleSystemData.Particles.Count; var particles = particleSystemData.Particles.Array; if (_particleIndices == null) { _particleIndices = new ArrayList <ParticleIndex>(numberOfParticles); } else { _particleIndices.Clear(); _particleIndices.EnsureCapacity(numberOfParticles); } // Use linear distance for viewpoint-oriented and world-oriented billboards. bool useLinearDistance = (particleSystemData.BillboardOrientation.Normal != BillboardNormal.ViewPlaneAligned); // Compute positions and distance to camera. for (int i = 0; i < numberOfParticles; i++) { if (particles[i].IsAlive) // Skip dead particles. { var particleIndex = new ParticleIndex(); particleIndex.Index = i; if (requiresTransformation) { particleIndex.Position = pose.ToWorldPosition(particles[i].Position * scale); } else { particleIndex.Position = particles[i].Position; } // Planar distance: Project vector onto look direction. Vector3 cameraToParticle = particleIndex.Position - _cameraPose.Position; particleIndex.Distance = Vector3.Dot(cameraToParticle, _cameraForward); if (useLinearDistance) { particleIndex.Distance = cameraToParticle.Length * Math.Sign(particleIndex.Distance); } _particleIndices.Add(ref particleIndex); } } // Sort particles back-to-front. _particleIndices.Sort(ParticleIndexComparer.Instance); bool isViewPlaneAligned = (particleSystemData.BillboardOrientation.Normal == BillboardNormal.ViewPlaneAligned); bool isAxisInViewSpace = particleSystemData.BillboardOrientation.IsAxisInViewSpace; // Draw sorted particles. var indices = _particleIndices.Array; numberOfParticles = _particleIndices.Count; // Dead particles have been removed. for (int i = 0; i < numberOfParticles; i++) { int index = indices[i].Index; b.Position = indices[i].Position; if (requiresTransformation) { b.Normal = isViewPlaneAligned ? _defaultNormal : pose.ToWorldDirection(particles[index].Normal); b.Axis = isAxisInViewSpace ? particles[index].Axis : pose.ToWorldDirection(particles[index].Axis); b.Size = particles[index].Size * scale.Y; // Assume uniform scale for size. } else { b.Normal = isViewPlaneAligned ? _defaultNormal : particles[index].Normal; b.Axis = particles[index].Axis; b.Size = particles[index].Size; } b.Angle = particles[index].Angle + angleOffset; b.Color = particles[index].Color * color; b.Alpha = particles[index].Alpha * alpha; b.AnimationTime = particles[index].AnimationTime; b.BlendMode = particles[index].BlendMode; var texture = particleSystemData.Texture ?? _debugTexture; _billboardBatch.DrawBillboard(ref b, texture); } }
/// <summary> /// Adds the specified billboard (4 vertices) to the vertex buffer. /// </summary> /// <param name="b">The billboard.</param> /// <param name="vertices">The vertex buffer.</param> /// <param name="texture">The packed texture.</param> /// <param name="index">The index of the next free slot in the vertex buffer.</param> protected abstract void OnDrawBillboard(ref BillboardArgs b, PackedTexture texture, T[] vertices, int index);
protected override void OnDrawBillboard(ref BillboardArgs b, PackedTexture texture, VertexPositionColorTexture[] vertices, int index) { // The billboard orientation is defined by three vectors: normal (pointing to the camera), // up and right (both lying in the billboard plane). // normal and up are given. right is computed using the cross product up x normal. // normal and up should be perpendicular, but usually they are not. Therefore, one vector // must be corrected. For spherical billboards, the normal is fixed and the up vector // is corrected. For cylindrical billboards (= axial billboards), the up vector is fixed // and the b.Normal is corrected. // Normal if (b.Orientation.Normal == BillboardNormal.ViewpointOriented) { Vector3 normal = _cameraPose.Position - b.Position; if (normal.TryNormalize()) { b.Normal = normal; } } // Axis = up vector if (b.Orientation.IsAxisInViewSpace) { b.Axis = _cameraPose.ToWorldDirection(b.Axis); } if (1 - Vector3.Dot(b.Normal, b.Axis) < Numeric.EpsilonF) { // Normal and axis are parallel. // --> Bend normal by adding a fraction of the camera down vector. b.Normal += _cameraDown * 0.001f; b.Normal.Normalize(); } // Compute right. //Vector3 right = Vector3.Cross(b.Axis, b.Normal); // Inlined: Vector3 right; right.X = b.Axis.Y * b.Normal.Z - b.Axis.Z * b.Normal.Y; right.Y = b.Axis.Z * b.Normal.X - b.Axis.X * b.Normal.Z; right.Z = b.Axis.X * b.Normal.Y - b.Axis.Y * b.Normal.X; if (!right.TryNormalize()) { right = b.Normal.Orthonormal1; // Normal and axis are parallel --> Choose random perpendicular vector. } if (b.Orientation.IsAxisFixed) { // Make sure normal is perpendicular to right and up. //normal = Vector3.Cross(right, b.Axis); // Inlined: b.Normal.X = right.Y * b.Axis.Z - right.Z * b.Axis.Y; b.Normal.Y = right.Z * b.Axis.X - right.X * b.Axis.Z; b.Normal.Z = right.X * b.Axis.Y - right.Y * b.Axis.X; // No need to normalize because right and up are normalized and perpendicular. } else { // Make sure axis is perpendicular to normal and right. //b.Axis = Vector3.Cross(b.Normal, right); // Inlined: b.Axis.X = b.Normal.Y * right.Z - b.Normal.Z * right.Y; b.Axis.Y = b.Normal.Z * right.X - b.Normal.X * right.Z; b.Axis.Z = b.Normal.X * right.Y - b.Normal.Y * right.X; // No need to normalize because normal and right are normalized and perpendicular. } Vector3 upRotated; Vector3 rightRotated; if (b.Angle != 0.0f) { // Rotate up and right. // Here is the readable code. //Matrix rotation = Matrix.CreateRotation(b.Normal, b.Angle); //Vector3 upRotated = rotation * b.Axis; //Vector3 rightRotated = rotation * right; // Inlined code: float x = b.Normal.X; float y = b.Normal.Y; float z = b.Normal.Z; float x2 = x * x; float y2 = y * y; float z2 = z * z; float xy = x * y; float xz = x * z; float yz = y * z; float cos = (float)Math.Cos(b.Angle); float sin = (float)Math.Sin(b.Angle); float xsin = x * sin; float ysin = y * sin; float zsin = z * sin; float oneMinusCos = 1.0f - cos; float m00 = x2 + cos * (1.0f - x2); float m01 = xy * oneMinusCos - zsin; float m02 = xz * oneMinusCos + ysin; float m10 = xy * oneMinusCos + zsin; float m11 = y2 + cos * (1.0f - y2); float m12 = yz * oneMinusCos - xsin; float m20 = xz * oneMinusCos - ysin; float m21 = yz * oneMinusCos + xsin; float m22 = z2 + cos * (1.0f - z2); upRotated.X = m00 * b.Axis.X + m01 * b.Axis.Y + m02 * b.Axis.Z; upRotated.Y = m10 * b.Axis.X + m11 * b.Axis.Y + m12 * b.Axis.Z; upRotated.Z = m20 * b.Axis.X + m21 * b.Axis.Y + m22 * b.Axis.Z; rightRotated.X = m00 * right.X + m01 * right.Y + m02 * right.Z; rightRotated.Y = m10 * right.X + m11 * right.Y + m12 * right.Z; rightRotated.Z = m20 * right.X + m21 * right.Y + m22 * right.Z; } else { // Angle is 0 - no rotation. upRotated = b.Axis; rightRotated = right; } Vector2F texCoordTopLeft = texture.GetTextureCoordinates(Vector2F.Zero, b.AnimationTime); Vector2F texCoordBottomRight = texture.GetTextureCoordinates(Vector2F.One, b.AnimationTime); // Handle mirroring. if (b.Size.X < 0) { b.Size.X = -b.Size.X; MathHelper.Swap(ref texCoordTopLeft.X, ref texCoordBottomRight.X); } if (b.Size.Y < 0) { b.Size.Y = -b.Size.Y; MathHelper.Swap(ref texCoordTopLeft.Y, ref texCoordBottomRight.Y); } b.Size.X /= 2.0f; b.Size.Y /= 2.0f; // Offset from billboard center to right edge. Vector3 hOffset; hOffset.X = rightRotated.X * b.Size.X; hOffset.Y = rightRotated.Y * b.Size.X; hOffset.Z = rightRotated.Z * b.Size.X; // Offset from reference point to top edge. Vector3 vOffset; vOffset.X = upRotated.X * b.Size.Y; vOffset.Y = upRotated.Y * b.Size.Y; vOffset.Z = upRotated.Z * b.Size.Y; // Premultiply alpha. Vector4 color4 = new Vector4 { X = b.Color.X * b.Alpha, Y = b.Color.Y * b.Alpha, Z = b.Color.Z * b.Alpha, // Apply blend mode (0 = additive, 1 = alpha blend). W = b.Alpha * b.BlendMode }; var color = new Color(color4); // Bottom left vertex vertices[index].Position.X = b.Position.X - hOffset.X - vOffset.X; vertices[index].Position.Y = b.Position.Y - hOffset.Y - vOffset.Y; vertices[index].Position.Z = b.Position.Z - hOffset.Z - vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordTopLeft.X; vertices[index].TextureCoordinate.Y = texCoordBottomRight.Y; index++; // Top left vertex vertices[index].Position.X = b.Position.X - hOffset.X + vOffset.X; vertices[index].Position.Y = b.Position.Y - hOffset.Y + vOffset.Y; vertices[index].Position.Z = b.Position.Z - hOffset.Z + vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordTopLeft.X; vertices[index].TextureCoordinate.Y = texCoordTopLeft.Y; index++; // Top right vertex vertices[index].Position.X = b.Position.X + hOffset.X + vOffset.X; vertices[index].Position.Y = b.Position.Y + hOffset.Y + vOffset.Y; vertices[index].Position.Z = b.Position.Z + hOffset.Z + vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordBottomRight.X; vertices[index].TextureCoordinate.Y = texCoordTopLeft.Y; index++; // Bottom right vertex vertices[index].Position.X = b.Position.X + hOffset.X - vOffset.X; vertices[index].Position.Y = b.Position.Y + hOffset.Y - vOffset.Y; vertices[index].Position.Z = b.Position.Z + hOffset.Z - vOffset.Z; vertices[index].Color = color; vertices[index].TextureCoordinate.X = texCoordBottomRight.X; vertices[index].TextureCoordinate.Y = texCoordBottomRight.Y; }