/// <inheritdoc/> public override void Extract() { var modelObjectInfo = RootRenderFeature.RenderData.GetData(renderObjectInfoKey); foreach (var objectNodeReference in RootRenderFeature.ObjectNodeReferences) { var objectNode = RootRenderFeature.GetObjectNode(objectNodeReference); var renderMesh = objectNode.RenderObject as RenderMesh; if (renderMesh == null) { continue; } int meshIndex = 0; var modelComponent = renderMesh.Source as ModelComponent; if (modelComponent == null) { continue; } for (int i = 0; i < modelComponent.Model.Meshes.Count; i++) { if (modelComponent.Model.Meshes[i] == renderMesh.Mesh) { meshIndex = i; break; } } modelObjectInfo[objectNodeReference] = new PickingObjectInfo(RuntimeIdHelper.ToRuntimeId(modelComponent), meshIndex, renderMesh.Mesh.MaterialIndex); } }
/// <summary> /// Uncache identifier of the component specified />. /// </summary> /// <param name="component">The component to uncache</param> public void UncacheEntityComponent([NotNull] EntityComponent component) { if (component == null) { throw new ArgumentNullException(nameof(component)); } idToEntity.Remove(RuntimeIdHelper.ToRuntimeId(component)); }
/// <summary> /// Cache identifier of the component specified />. /// </summary> /// <param name="component">The component to cache</param> public void CacheEntityComponent([NotNull] EntityComponent component) { if (component == null) { throw new ArgumentNullException(nameof(component)); } idToEntity[RuntimeIdHelper.ToRuntimeId(component)] = component.Entity; }
private void CollectComponentIds(Entity entity) { foreach (var component in entity.Components) { componentsIds.Add(RuntimeIdHelper.ToRuntimeId(component)); } foreach (var child in entity.GetChildren()) { CollectComponentIds(child); } }
public override void Extract() { var objectInfoDataHolder = RootRenderFeature.RenderData.GetData(_objectInfoPropertyKey); var blendMatricesDataHolder = RootRenderFeature.RenderData.GetData(_blendMatricesPropertyKey); foreach (var objectNodeReference in RootRenderFeature.ObjectNodeReferences) { var objectNode = RootRenderFeature.GetObjectNode(objectNodeReference); if (!(objectNode.RenderObject is RenderMesh renderMesh)) { continue; } blendMatricesDataHolder[objectNodeReference] = renderMesh.BlendMatrices; // This is for our skinned models. int meshIndex = 0; if (!(renderMesh.Source is ModelComponent modelComponent)) { continue; } for (int i = 0; i < modelComponent.Model.Meshes.Count; i++) { if (modelComponent.Model.Meshes[i] == renderMesh.Mesh) { meshIndex = i; break; } } // RuntimeIdHelper.ToRuntimeId is how Stride does it for its 'Picking' scene. // We should probably change this to use something more appropriate for our data. var modelCompId = RuntimeIdHelper.ToRuntimeId(modelComponent); var objectInfoData = new ObjectInfoData((uint)modelCompId, (ushort)meshIndex, (ushort)renderMesh.Mesh.MaterialIndex); objectInfoDataHolder[objectNodeReference] = objectInfoData; #if DEBUG // This is only for debugging purposes, it can be removed. if (_isFirstRun) { System.Diagnostics.Debug.WriteLine($"Entity: {modelComponent.Entity.Name} - modelCompId: {objectInfoData.ModelComponentId} - meshIndex: {objectInfoData.MeshIndex} - matIndex: {objectInfoData.MaterialIndex}"); } #endif } #if DEBUG _isFirstRun = false; #endif }
/// <summary> /// Invalidates the cached identifiers of all the <see cref="EntityComponent"/>s of the specified <see cref="Entity"/>. /// </summary> /// <param name="entity">The entity for which to invalidate the cached identifiers of its components.</param> /// <param name="isRecursive">A value indicating whether to also invalidate the cached identifiers of the components of child entities, recursively.</param> public void UncacheEntity([NotNull] Entity entity, bool isRecursive) { if (entity is null) { throw new ArgumentNullException(nameof(entity)); } foreach (var component in entity.Components) { idToEntity.Remove(RuntimeIdHelper.ToRuntimeId(component)); } if (isRecursive) { foreach (var component in entity.GetChildren().BreadthFirst(x => x.GetChildren()).SelectMany(e => e.Components)) { idToEntity.Remove(RuntimeIdHelper.ToRuntimeId(component)); } } }
/// <summary> /// Cache identifier of all components of the specified <paramref name="entity"/>. /// </summary> /// <param name="entity">The entity which components to cache.</param> /// <param name="isRecursive"><c>true</c> if the components of child entities should also be cached, recursively; otherwise, <c>false</c>.</param> public void CacheEntity([NotNull] Entity entity, bool isRecursive) { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } foreach (var component in entity.Components) { idToEntity[RuntimeIdHelper.ToRuntimeId(component)] = component.Entity; } if (!isRecursive) { return; } foreach (var component in entity.GetChildren().BreadthFirst(x => x.GetChildren()).SelectMany(e => e.Components)) { idToEntity[RuntimeIdHelper.ToRuntimeId(component)] = component.Entity; } }
public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex) { base.Draw(context, renderView, renderViewStage, startIndex, endIndex); BlendStateDescription? previousBlendState = null; DepthStencilStateDescription?previousDepthStencilState = null; EffectInstance previousEffect = null; //TODO string comparison ...? var isPicking = RenderSystem.RenderStages[renderViewStage.Index].Name == "Picking"; var hasBegin = false; for (var index = startIndex; index < endIndex; index++) { var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode; var renderNode = GetRenderNode(renderNodeReference); var spriteState = (RenderSpriteStudio)renderNode.RenderObject; var depthStencilState = DepthStencilStates.DepthRead; foreach (var node in spriteState.SortedNodes) { if (node.Sprite?.Texture == null || node.Sprite.Region.Width <= 0 || node.Sprite.Region.Height <= 0f || node.Hide != 0) { continue; } // Update the sprite batch BlendStateDescription spriteBlending; switch (node.BaseNode.AlphaBlending) { case SpriteStudioBlending.Mix: spriteBlending = BlendStates.AlphaBlend; break; case SpriteStudioBlending.Multiplication: spriteBlending = MultBlendState; break; case SpriteStudioBlending.Addition: spriteBlending = BlendStates.Additive; break; case SpriteStudioBlending.Subtraction: spriteBlending = SubBlendState; break; default: throw new ArgumentOutOfRangeException(); } // TODO: this should probably be moved to Prepare() // Project the position // TODO: This could be done in a SIMD batch, but we need to figure-out how to plugin in with RenderMesh object var worldPosition = new Vector4(spriteState.WorldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref renderView.ViewProjection, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; var blendState = isPicking ? BlendStates.Default : spriteBlending; // TODO: the current impementation to determine if the sprite is selected does not work. This should be fixed later at some point //var currentEffect = isPicking ? GetOrCreatePickingSpriteEffect() : ShadowObject.IsObjectSelected(spriteState.SpriteStudioComponent) ? GetOrCreateSelectedSpriteEffect() : null; var currentEffect = isPicking ? GetOrCreatePickingSpriteEffect() : null; // TODO remove this code when material are available if (previousEffect != currentEffect || blendState != previousBlendState || depthStencilState != previousDepthStencilState) { if (hasBegin) { sprite3DBatch.End(); } sprite3DBatch.Begin(context.GraphicsContext, renderView.ViewProjection, SpriteSortMode.Deferred, blendState, null, depthStencilState, RasterizerStates.CullNone, currentEffect); hasBegin = true; } previousEffect = currentEffect; previousBlendState = blendState; previousDepthStencilState = depthStencilState; var sourceRegion = node.Sprite.Region; var texture = node.Sprite.Texture; // skip the sprite if no texture is set. if (texture == null) { continue; } var color4 = Color4.White; if (isPicking) { // TODO move this code corresponding to picking out of the runtime code. color4 = new Color4(RuntimeIdHelper.ToRuntimeId(spriteState.Source)); } else { if (node.BlendFactor > 0.0f) { switch (node.BlendType) //todo this should be done in a shader { case SpriteStudioBlending.Mix: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Multiplication: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Addition: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Subtraction: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; default: throw new ArgumentOutOfRangeException(); } } else { color4 *= node.FinalTransparency; } } Matrix.Multiply(ref node.ModelTransform, ref spriteState.WorldMatrix, out var worldMatrix); // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(node.Sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - node.Sprite.Center.Y / sourceRegion.Height); if (node.Sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var size = node.Sprite.Size; var centerOffset = Vector2.Modulate(normalizedCenter, size); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; // draw the sprite sprite3DBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref size, ref color4, node.Sprite.Orientation, SwizzleMode.None, projectedZ); } } if (hasBegin) { sprite3DBatch.End(); } }
public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex) { base.Draw(context, renderView, renderViewStage, startIndex, endIndex); Matrix viewInverse; Matrix.Invert(ref renderView.View, out viewInverse); BlendStateDescription? previousBlendState = null; DepthStencilStateDescription?previousDepthStencilState = null; EffectInstance previousEffect = null; //TODO string comparison ...? var isPicking = renderViewStage.RenderStage.Name == "Picking"; bool hasBegin = false; for (var index = startIndex; index < endIndex; index++) { var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode; var renderNode = GetRenderNode(renderNodeReference); var renderSprite = (RenderSprite)renderNode.RenderObject; var spriteComp = renderSprite.SpriteComponent; var transfoComp = renderSprite.TransformComponent; var depthStencilState = renderSprite.SpriteComponent.IgnoreDepth ? DepthStencilStates.None : DepthStencilStates.Default; var sprite = spriteComp.CurrentSprite; if (sprite == null) { continue; } // TODO: this should probably be moved to Prepare() // Project the position // TODO: This could be done in a SIMD batch, but we need to figure-out how to plugin in with RenderMesh object var worldPosition = new Vector4(renderSprite.TransformComponent.WorldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref renderView.ViewProjection, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; // Update the sprite batch var blendState = isPicking ? BlendStates.Default : sprite.IsTransparent ? (spriteComp.PremultipliedAlpha ? BlendStates.AlphaBlend : BlendStates.NonPremultiplied) : BlendStates.Opaque; var currentEffect = isPicking ? GetOrCreatePickingSpriteEffect() : null; // TODO remove this code when material are available if (previousEffect != currentEffect || blendState != previousBlendState || depthStencilState != previousDepthStencilState) { if (hasBegin) { sprite3DBatch.End(); } sprite3DBatch.Begin(context.GraphicsContext, renderView.ViewProjection, SpriteSortMode.Deferred, blendState, null, depthStencilState, RasterizerStates.CullNone, currentEffect); hasBegin = true; } previousEffect = currentEffect; previousBlendState = blendState; previousDepthStencilState = depthStencilState; var sourceRegion = sprite.Region; var texture = sprite.Texture; var color = spriteComp.Color; if (isPicking) // TODO move this code corresponding to picking out of the runtime code. { var compId = RuntimeIdHelper.ToRuntimeId(spriteComp); color = new Color4(compId); } // skip the sprite if no texture is set. if (texture == null) { continue; } // determine the element world matrix depending on the type of sprite var worldMatrix = transfoComp.WorldMatrix; if (spriteComp.SpriteType == SpriteType.Billboard) { worldMatrix = viewInverse; // remove scale of the camera worldMatrix.Row1 /= ((Vector3)viewInverse.Row1).Length(); worldMatrix.Row2 /= ((Vector3)viewInverse.Row2).Length(); // set the scale of the object worldMatrix.Row1 *= ((Vector3)transfoComp.WorldMatrix.Row1).Length(); worldMatrix.Row2 *= ((Vector3)transfoComp.WorldMatrix.Row2).Length(); // set the position worldMatrix.TranslationVector = transfoComp.WorldMatrix.TranslationVector; } // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - sprite.Center.Y / sourceRegion.Height); if (sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var centerOffset = Vector2.Modulate(normalizedCenter, sprite.SizeInternal); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; worldMatrix.M43 -= centerOffset.X * worldMatrix.M13 + centerOffset.Y * worldMatrix.M23; // draw the sprite sprite3DBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref sprite.SizeInternal, ref color, sprite.Orientation, SwizzleMode.None, projectedZ); } if (hasBegin) { sprite3DBatch.End(); } }
public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex) { base.Draw(context, renderView, renderViewStage, startIndex, endIndex); var isMultisample = RenderSystem.RenderStages[renderViewStage.Index].Output.MultisampleCount != MultisampleCount.None; var batchContext = threadContext.Value; Matrix viewInverse; Matrix.Invert(ref renderView.View, out viewInverse); uint previousBatchState = uint.MaxValue; //TODO string comparison ...? var isPicking = RenderSystem.RenderStages[renderViewStage.Index].Name == "Picking"; bool hasBegin = false; for (var index = startIndex; index < endIndex; index++) { var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode; var renderNode = GetRenderNode(renderNodeReference); var renderSprite = (RenderSprite)renderNode.RenderObject; var sprite = renderSprite.Sprite; if (sprite == null) { continue; } // TODO: this should probably be moved to Prepare() // Project the position // TODO: This could be done in a SIMD batch, but we need to figure-out how to plugin in with RenderMesh object var worldPosition = new Vector4(renderSprite.WorldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref renderView.ViewProjection, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; BlendModes blendMode; EffectInstance currentEffect = null; if (isPicking) { blendMode = BlendModes.Default; currentEffect = batchContext.GetOrCreatePickingSpriteEffect(RenderSystem.EffectSystem); } else { var spriteBlend = renderSprite.BlendMode; if (spriteBlend == SpriteBlend.Auto) { spriteBlend = sprite.IsTransparent ? SpriteBlend.AlphaBlend : SpriteBlend.None; } if (spriteBlend == SpriteBlend.AlphaBlend) { blendMode = renderSprite.PremultipliedAlpha ? BlendModes.Alpha : BlendModes.NonPremultiplied; } else { blendMode = spriteBlendToBlendMode[spriteBlend]; } } // Check if the current blend state has changed in any way, if not // Note! It doesn't really matter in what order we build the bitmask, the result is not preserved anywhere except in this method var currentBatchState = (uint)blendMode; currentBatchState = (currentBatchState << 1) + ((uint)renderSprite.DepthMode); currentBatchState = (currentBatchState << 1) + (renderSprite.IsAlphaCutoff ? 1U : 0U); currentBatchState = (currentBatchState << 2) + ((uint)renderSprite.Sampler); if (previousBatchState != currentBatchState) { var blendState = blendModeToDescription[blendMode]; if (renderSprite.IsAlphaCutoff) { currentEffect = batchContext.GetOrCreateAlphaCutoffSpriteEffect(RenderSystem.EffectSystem); } DepthStencilStateDescription depthStencilState; switch (renderSprite.DepthMode) { case RenderSprite.SpriteDepthMode.Ignore: depthStencilState = DepthStencilStates.None; break; case RenderSprite.SpriteDepthMode.ReadOnly: depthStencilState = DepthStencilStates.DepthRead; break; default: depthStencilState = DepthStencilStates.Default; break; case RenderSprite.SpriteDepthMode.WriteOnly: depthStencilState = new DepthStencilStateDescription(false, true); break; } var samplerState = context.GraphicsDevice.SamplerStates.LinearClamp; if (renderSprite.Sampler != SpriteSampler.LinearClamp) { switch (renderSprite.Sampler) { case SpriteSampler.PointClamp: samplerState = context.GraphicsDevice.SamplerStates.PointClamp; break; case SpriteSampler.AnisotropicClamp: samplerState = context.GraphicsDevice.SamplerStates.AnisotropicClamp; break; } } if (hasBegin) { lock (batchEndLocker) { batchContext.SpriteBatch.End(); } } var rasterizerState = RasterizerStates.CullNone; if (isMultisample) { rasterizerState.MultisampleCount = RenderSystem.RenderStages[renderViewStage.Index].Output.MultisampleCount; rasterizerState.MultisampleAntiAliasLine = true; } batchContext.SpriteBatch.Begin(context.GraphicsContext, renderView.ViewProjection, SpriteSortMode.Deferred, blendState, samplerState, depthStencilState, rasterizerState, currentEffect); hasBegin = true; } previousBatchState = currentBatchState; var sourceRegion = sprite.Region; var texture = sprite.Texture; var color = renderSprite.Color; if (isPicking) // TODO move this code corresponding to picking out of the runtime code. { var compId = RuntimeIdHelper.ToRuntimeId(renderSprite.Source); color = new Color4(compId, 0.0f, 0.0f, 0.0f); } // skip the sprite if no texture is set. if (texture == null) { continue; } // determine the element world matrix depending on the type of sprite var worldMatrix = renderSprite.WorldMatrix; if (renderSprite.SpriteType == SpriteType.Billboard) { worldMatrix = viewInverse; var worldMatrixRow1 = worldMatrix.Row1; var worldMatrixRow2 = worldMatrix.Row2; // remove scale of the camera worldMatrixRow1 /= ((Vector3)viewInverse.Row1).Length(); worldMatrixRow2 /= ((Vector3)viewInverse.Row2).Length(); // set the scale of the object worldMatrixRow1 *= ((Vector3)renderSprite.WorldMatrix.Row1).Length(); worldMatrixRow2 *= ((Vector3)renderSprite.WorldMatrix.Row2).Length(); worldMatrix.Row1 = worldMatrixRow1; worldMatrix.Row2 = worldMatrixRow2; // set the position worldMatrix.TranslationVector = renderSprite.WorldMatrix.TranslationVector; // set the rotation var localRotationZ = renderSprite.RotationEulerZ; if (localRotationZ != 0) { worldMatrix = Matrix.RotationZ(localRotationZ) * worldMatrix; } } // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - sprite.Center.Y / sourceRegion.Height); if (sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var centerOffset = Vector2.Modulate(normalizedCenter, sprite.SizeInternal); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; worldMatrix.M43 -= centerOffset.X * worldMatrix.M13 + centerOffset.Y * worldMatrix.M23; // adapt the source region to match what is expected at full resolution if (texture.ViewType == ViewType.Full && texture.ViewWidth != texture.FullQualitySize.Width) { var fullQualitySize = texture.FullQualitySize; var horizontalRatio = texture.ViewWidth / (float)fullQualitySize.Width; var verticalRatio = texture.ViewHeight / (float)fullQualitySize.Height; sourceRegion.X *= horizontalRatio; sourceRegion.Width *= horizontalRatio; sourceRegion.Y *= verticalRatio; sourceRegion.Height *= verticalRatio; } // register resource usage. Context.StreamingManager?.StreamResources(texture); // draw the sprite batchContext.SpriteBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref sprite.SizeInternal, ref color, sprite.Orientation, renderSprite.Swizzle, projectedZ); } if (hasBegin) { lock (batchEndLocker) { batchContext.SpriteBatch.End(); } } }
/// <summary> /// Draw this effect mesh. /// </summary> public void Draw(RenderContext context) { // Retrieve effect parameters var graphicsDevice = context.GraphicsDevice; var mesh = Mesh; var currentRenderData = mesh.Draw; var material = Material; var vao = vertexArrayObject; var drawCount = currentRenderData.DrawCount; var primitiveType = currentRenderData.PrimitiveType; parameters.Set(TransformationKeys.World, WorldMatrix); // TODO: We should clarify exactly how to override rasterizer states. Currently setup here on Context.Parameters to allow Material/ModelComponent overrides, but this is ugly // Apply rasterizer var rasterizer = RasterizerState; if (!ForceRasterizer && Material.CullMode.HasValue && Material.CullMode.Value != RasterizerState.Description.CullMode) { switch (Material.CullMode.Value) { case CullMode.Back: rasterizer = graphicsDevice.RasterizerStates.CullBack; break; case CullMode.Front: rasterizer = graphicsDevice.RasterizerStates.CullFront; break; case CullMode.None: rasterizer = graphicsDevice.RasterizerStates.CullNone; break; } } context.Parameters.Set(Effect.RasterizerStateKey, rasterizer); // Handle picking if (context.IsPicking()) // TODO move this code corresponding to picking outside of the runtime code! { parameters.Set(ModelComponentPickingShaderKeys.ModelComponentId, new Color4(RuntimeIdHelper.ToRuntimeId(RenderModel.ModelComponent))); parameters.Set(ModelComponentPickingShaderKeys.MeshId, new Color4(RenderModel.ModelComponent.Model.Meshes.IndexOf(Mesh))); parameters.Set(ModelComponentPickingShaderKeys.MaterialId, new Color4(Mesh.MaterialIndex)); // Don't use the materials blend state on picking targets parameters.Set(Effect.BlendStateKey, null); } if (material != null && material.TessellationMethod != XenkoTessellationMethod.None) { var tessellationMethod = material.TessellationMethod; // adapt the primitive type and index buffer to the tessellation used if (tessellationMethod.PerformsAdjacentEdgeAverage()) { vao = GetOrCreateVertexArrayObjectAEN(context); drawCount = 12 / 3 * drawCount; } primitiveType = tessellationMethod.GetPrimitiveType(); } //using (Profiler.Begin(ProfilingKeys.PrepareMesh)) { // Order of application of parameters: // - RenderPass.Parameters // - ModelComponent.Parameters // - RenderMesh.Parameters (originally copied from mesh parameters) // The order is based on the granularity level of each element and how shared it can be. Material is heavily shared, a model contains many meshes. An renderMesh is unique. // TODO: really copy mesh parameters into renderMesh instead of just referencing the meshDraw parameters. //var modelComponent = RenderModel.ModelComponent; //var hasModelComponentParams = modelComponent != null && modelComponent.Parameters != null; //var materialParameters = material != null && material.Parameters != null ? material.Parameters : null; parameterCollections.Clear(); parameterCollections.Add(context.Parameters); FillParameterCollections(ref parameterCollections); // Check if we need to recreate the EffectParameterCollectionGroup // TODO: We can improve performance by redesigning FillParameterCollections to avoid ArrayExtensions.ArraysReferenceEqual (or directly check the appropriate parameter collections) // This also happens in another place: DynamicEffectCompiler (we probably want to factorize it when doing additional optimizations) if (parameterCollectionGroup == null || parameterCollectionGroup.Effect != Effect || !ArrayExtensions.ArraysReferenceEqual(ref previousParameterCollections, ref parameterCollections)) { previousParameterCollections.Clear(); previousParameterCollections.AddRange(parameterCollections); parameterCollectionGroup = new EffectParameterCollectionGroup(context.GraphicsDevice, Effect, previousParameterCollections.Count, previousParameterCollections.Items); } Effect.Apply(context.GraphicsDevice, parameterCollectionGroup, true); } //using (Profiler.Begin(ProfilingKeys.RenderMesh)) { if (currentRenderData != null) { graphicsDevice.SetVertexArrayObject(vao); if (currentRenderData.IndexBuffer == null) { graphicsDevice.Draw(primitiveType, drawCount, currentRenderData.StartLocation); } else { graphicsDevice.DrawIndexed(primitiveType, drawCount, currentRenderData.StartLocation); } } } }
public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex) { base.Draw(context, renderView, renderViewStage, startIndex, endIndex); var batchContext = threadContext.Value; Matrix viewInverse; Matrix.Invert(ref renderView.View, out viewInverse); uint previousBatchState = uint.MaxValue; //TODO string comparison ...? var isPicking = renderViewStage.RenderStage.Name == "Picking"; bool hasBegin = false; for (var index = startIndex; index < endIndex; index++) { var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode; var renderNode = GetRenderNode(renderNodeReference); var renderSprite = (RenderSprite)renderNode.RenderObject; var spriteComp = renderSprite.SpriteComponent; var transfoComp = renderSprite.TransformComponent; var sprite = spriteComp.CurrentSprite; if (sprite == null) { continue; } // TODO: this should probably be moved to Prepare() // Project the position // TODO: This could be done in a SIMD batch, but we need to figure-out how to plugin in with RenderMesh object var worldPosition = new Vector4(renderSprite.TransformComponent.WorldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref renderView.ViewProjection, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; // Check if the current blend state has changed in any way, if not // Note! It doesn't really matter in what order we build the bitmask, the result is not preserved anywhere except in this method var currentBatchState = isPicking ? 0U : sprite.IsTransparent ? (spriteComp.PremultipliedAlpha ? 1U : 2U) : 3U; currentBatchState = (currentBatchState << 1) + (renderSprite.SpriteComponent.IgnoreDepth ? 1U : 0U); currentBatchState = (currentBatchState << 2) + ((uint)renderSprite.SpriteComponent.Sampler); if (previousBatchState != currentBatchState) { var blendState = isPicking ? BlendStates.Default : sprite.IsTransparent ? (spriteComp.PremultipliedAlpha ? BlendStates.AlphaBlend : BlendStates.NonPremultiplied) : BlendStates.Opaque; var currentEffect = isPicking ? batchContext.GetOrCreatePickingSpriteEffect(RenderSystem.EffectSystem) : null; // TODO remove this code when material are available var depthStencilState = renderSprite.SpriteComponent.IgnoreDepth ? DepthStencilStates.None : DepthStencilStates.Default; var samplerState = context.GraphicsDevice.SamplerStates.LinearClamp; if (renderSprite.SpriteComponent.Sampler != SpriteComponent.SpriteSampler.LinearClamp) { switch (renderSprite.SpriteComponent.Sampler) { case SpriteComponent.SpriteSampler.PointClamp: samplerState = context.GraphicsDevice.SamplerStates.PointClamp; break; case SpriteComponent.SpriteSampler.AnisotropicClamp: samplerState = context.GraphicsDevice.SamplerStates.AnisotropicClamp; break; } } if (hasBegin) { batchContext.SpriteBatch.End(); } batchContext.SpriteBatch.Begin(context.GraphicsContext, renderView.ViewProjection, SpriteSortMode.Deferred, blendState, samplerState, depthStencilState, RasterizerStates.CullNone, currentEffect); hasBegin = true; } previousBatchState = currentBatchState; var sourceRegion = sprite.Region; var texture = sprite.Texture; var color = spriteComp.Color; if (isPicking) // TODO move this code corresponding to picking out of the runtime code. { var compId = RuntimeIdHelper.ToRuntimeId(spriteComp); color = new Color4(compId, 0.0f, 0.0f, 0.0f); } // skip the sprite if no texture is set. if (texture == null) { continue; } // determine the element world matrix depending on the type of sprite var worldMatrix = transfoComp.WorldMatrix; if (spriteComp.SpriteType == SpriteType.Billboard) { worldMatrix = viewInverse; // remove scale of the camera worldMatrix.Row1 /= ((Vector3)viewInverse.Row1).Length(); worldMatrix.Row2 /= ((Vector3)viewInverse.Row2).Length(); // set the scale of the object worldMatrix.Row1 *= ((Vector3)transfoComp.WorldMatrix.Row1).Length(); worldMatrix.Row2 *= ((Vector3)transfoComp.WorldMatrix.Row2).Length(); // set the position worldMatrix.TranslationVector = transfoComp.WorldMatrix.TranslationVector; } // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - sprite.Center.Y / sourceRegion.Height); if (sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var centerOffset = Vector2.Modulate(normalizedCenter, sprite.SizeInternal); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; worldMatrix.M43 -= centerOffset.X * worldMatrix.M13 + centerOffset.Y * worldMatrix.M23; // draw the sprite batchContext.SpriteBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref sprite.SizeInternal, ref color, sprite.Orientation, spriteComp.Swizzle, projectedZ); } if (hasBegin) { batchContext.SpriteBatch.End(); } }
protected override void DrawCore(RenderContext context, RenderItemCollection renderItems, int fromIndex, int toIndex) { var viewParameters = context.Parameters; var device = context.GraphicsDevice; var viewProjection = viewParameters.Get(TransformationKeys.ViewProjection); BlendState previousBlendState = null; DepthStencilState previousDepthStencilState = null; Effect previousEffect = null; var isPicking = context.IsPicking(); bool hasBegin = false; for (var i = fromIndex; i <= toIndex; i++) { var renderItem = renderItems[i]; var spriteState = (SpriteStudioProcessor.Data)renderItem.DrawContext; var transfoComp = spriteState.TransformComponent; var depthStencilState = device.DepthStencilStates.None; foreach (var node in spriteState.SpriteStudioComponent.SortedNodes) { if (node.Sprite?.Texture == null || node.Sprite.Region.Width <= 0 || node.Sprite.Region.Height <= 0f || node.Hide != 0) { continue; } // Update the sprite batch BlendState spriteBlending; switch (node.BaseNode.AlphaBlending) { case SpriteStudioBlending.Mix: spriteBlending = device.BlendStates.AlphaBlend; break; case SpriteStudioBlending.Multiplication: spriteBlending = MultBlendState; break; case SpriteStudioBlending.Addition: spriteBlending = device.BlendStates.Additive; break; case SpriteStudioBlending.Subtraction: spriteBlending = SubBlendState; break; default: throw new ArgumentOutOfRangeException(); } var blendState = isPicking ? device.BlendStates.Opaque : renderItems.HasTransparency ? spriteBlending : device.BlendStates.Opaque; var currentEffect = isPicking ? GetOrCreatePickingSpriteEffect() : ShadowObject.IsObjectSelected(spriteState.SpriteStudioComponent) ? GetOrCreateSelectedSpriteEffect() : null; // TODO remove this code when material are available if (previousEffect != currentEffect || blendState != previousBlendState || depthStencilState != previousDepthStencilState) { if (hasBegin) { sprite3DBatch.End(); } sprite3DBatch.Begin(viewProjection, SpriteSortMode.Deferred, blendState, null, depthStencilState, device.RasterizerStates.CullNone, currentEffect); hasBegin = true; } previousEffect = currentEffect; previousBlendState = blendState; previousDepthStencilState = depthStencilState; var sourceRegion = node.Sprite.Region; var texture = node.Sprite.Texture; // skip the sprite if no texture is set. if (texture == null) { continue; } var color4 = Color4.White; if (isPicking) { // TODO move this code corresponding to picking out of the runtime code. color4 = new Color4(RuntimeIdHelper.ToRuntimeId(spriteState.SpriteStudioComponent)); } else { if (node.BlendFactor > 0.0f) { switch (node.BlendType) //todo this should be done in a shader { case SpriteStudioBlending.Mix: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Multiplication: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Addition: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; case SpriteStudioBlending.Subtraction: color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency; break; default: throw new ArgumentOutOfRangeException(); } } else { color4 *= node.FinalTransparency; } } var worldMatrix = node.ModelTransform * transfoComp.WorldMatrix; // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(node.Sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - node.Sprite.Center.Y / sourceRegion.Height); if (node.Sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var size = node.Sprite.Size; var centerOffset = Vector2.Modulate(normalizedCenter, size); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; // draw the sprite sprite3DBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref size, ref color4, node.Sprite.Orientation, SwizzleMode.None, renderItem.Depth); } } sprite3DBatch.End(); }
protected override void DrawCore(RenderContext context, RenderItemCollection renderItems, int fromIndex, int toIndex) { var viewParameters = context.Parameters; var device = context.GraphicsDevice; var viewInverse = Matrix.Invert(viewParameters.Get(TransformationKeys.View)); var viewProjection = viewParameters.Get(TransformationKeys.ViewProjection); BlendState previousBlendState = null; DepthStencilState previousDepthStencilState = null; Effect previousEffect = null; var isPicking = context.IsPicking(); bool hasBegin = false; for (var i = fromIndex; i <= toIndex; i++) { var renderItem = renderItems[i]; var spriteState = (SpriteProcessor.SpriteComponentState)renderItem.DrawContext; var spriteComp = spriteState.SpriteComponent; var transfoComp = spriteState.TransformComponent; var depthStencilState = spriteState.SpriteComponent.IgnoreDepth ? device.DepthStencilStates.None : device.DepthStencilStates.Default; var sprite = spriteComp.CurrentSprite; if (sprite == null) { continue; } // Update the sprite batch var blendState = isPicking ? device.BlendStates.Opaque : renderItems.HasTransparency ? (spriteComp.PremultipliedAlpha ? device.BlendStates.AlphaBlend : device.BlendStates.NonPremultiplied) : device.BlendStates.Opaque; var currentEffect = isPicking? GetOrCreatePickingSpriteEffect(): ShadowObject.IsObjectSelected(spriteComp) ? GetOrCreateSelectedSpriteEffect(): null; // TODO remove this code when material are available if (previousEffect != currentEffect || blendState != previousBlendState || depthStencilState != previousDepthStencilState) { if (hasBegin) { sprite3DBatch.End(); } sprite3DBatch.Begin(viewProjection, SpriteSortMode.Deferred, blendState, null, depthStencilState, device.RasterizerStates.CullNone, currentEffect); hasBegin = true; } previousEffect = currentEffect; previousBlendState = blendState; previousDepthStencilState = depthStencilState; var sourceRegion = sprite.Region; var texture = sprite.Texture; var color = spriteComp.Color; if (isPicking) // TODO move this code corresponding to picking out of the runtime code. { color = new Color4(RuntimeIdHelper.ToRuntimeId(spriteComp)); } // skip the sprite if no texture is set. if (texture == null) { continue; } // determine the element world matrix depending on the type of sprite var worldMatrix = transfoComp.WorldMatrix; if (spriteComp.SpriteType == SpriteType.Billboard) { worldMatrix = viewInverse; // remove scale of the camera worldMatrix.Row1 /= ((Vector3)viewInverse.Row1).Length(); worldMatrix.Row2 /= ((Vector3)viewInverse.Row2).Length(); // set the scale of the object worldMatrix.Row1 *= ((Vector3)transfoComp.WorldMatrix.Row1).Length(); worldMatrix.Row2 *= ((Vector3)transfoComp.WorldMatrix.Row2).Length(); // set the position worldMatrix.TranslationVector = transfoComp.WorldMatrix.TranslationVector; } // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image) var normalizedCenter = new Vector2(sprite.Center.X / sourceRegion.Width - 0.5f, 0.5f - sprite.Center.Y / sourceRegion.Height); if (sprite.Orientation == ImageOrientation.Rotated90) { var oldCenterX = normalizedCenter.X; normalizedCenter.X = -normalizedCenter.Y; normalizedCenter.Y = oldCenterX; } // apply the offset due to the center of the sprite var centerOffset = Vector2.Modulate(normalizedCenter, sprite.SizeInternal); worldMatrix.M41 -= centerOffset.X * worldMatrix.M11 + centerOffset.Y * worldMatrix.M21; worldMatrix.M42 -= centerOffset.X * worldMatrix.M12 + centerOffset.Y * worldMatrix.M22; worldMatrix.M43 -= centerOffset.X * worldMatrix.M13 + centerOffset.Y * worldMatrix.M23; // draw the sprite sprite3DBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref sprite.SizeInternal, ref color, sprite.Orientation, SwizzleMode.None, renderItem.Depth); } sprite3DBatch.End(); }