private static void CalculateMinMaxDistance(ref Plane plane, ref BoundingBoxExt boundingBox, ref float minDistance, ref float maxDistance) { var nearCorner = boundingBox.Minimum; var farCorner = boundingBox.Maximum; if (plane.Normal.X < 0) { Utilities.Swap(ref nearCorner.X, ref farCorner.X); } if (plane.Normal.Y < 0) { Utilities.Swap(ref nearCorner.Y, ref farCorner.Y); } if (plane.Normal.Z < 0) { Utilities.Swap(ref nearCorner.Z, ref farCorner.Z); } float oldDistance; // Interlocked exchange if lower var distance = CollisionHelper.DistancePlanePoint(ref plane, ref nearCorner); while ((oldDistance = minDistance) > distance && Interlocked.CompareExchange(ref minDistance, distance, oldDistance) != oldDistance) { } // Interlocked exchange if greater distance = CollisionHelper.DistancePlanePoint(ref plane, ref farCorner); while ((oldDistance = maxDistance) < distance && Interlocked.CompareExchange(ref maxDistance, distance, oldDistance) != oldDistance) { } }
private static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt, bool ignoreDepthPlanes) { unsafe { fixed(Plane *planeStart = &frustum.LeftPlane) { var plane = planeStart; for (int i = 0; i < 6; ++i) { if (ignoreDepthPlanes && i > 3) { continue; } // Previous code: if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) <= -plane->D) { return(false); } plane++; } } return(true); } }
// TODO: Find a way to replug this /// <summary> /// Adds a default frustum culling for rendering only meshes that are only inside the frustum/ /// </summary> /// <param name="modelRenderer">The model renderer.</param> /// <returns>ModelRenderer.</returns> //public static ModelComponentRenderer AddDefaultFrustumCulling(this ModelComponentRenderer modelRenderer) //{ // modelRenderer.UpdateMeshes = FrustumCulling; // return modelRenderer; //} private static void FrustumCulling(RenderContext context, FastList <RenderMesh> meshes) { Matrix viewProjection, mat1, mat2; // Compute view * projection context.Parameters.Get(TransformationKeys.View, out mat1); context.Parameters.Get(TransformationKeys.Projection, out mat2); Matrix.Multiply(ref mat1, ref mat2, out viewProjection); var frustum = new BoundingFrustum(ref viewProjection); for (var i = 0; i < meshes.Count; ++i) { var renderMesh = meshes[i]; // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Get world matrix renderMesh.Mesh.Parameters.Get(TransformationKeys.World, out mat1); // Compute transformed AABB (by world) var boundingBoxExt = new BoundingBoxExt(renderMesh.Mesh.BoundingBox); boundingBoxExt.Transform(mat1); // Perform frustum culling if (!frustum.Contains(ref boundingBoxExt)) { meshes.SwapRemoveAt(i--); } } }
internal void CalculateBoundingBox() { // update the sprite bounding box var halfSpriteSize = Sprite?.Size / 2 ?? Vector2.Zero; // Only calculate if we've changed... if (lastWorldMatrix != WorldMatrix || lastHalfSpriteSize != halfSpriteSize || lastSpriteType != SpriteType) { Vector3 halfBoxSize; var boxWorldPosition = WorldMatrix.TranslationVector; if (SpriteType == SpriteType.Billboard) { // Make a gross estimation here as we don't have access to the camera view matrix // TODO: move this code or grant camera view matrix access to this processor var maxScale = Math.Max(WorldMatrix.Row1.Length(), Math.Max(WorldMatrix.Row2.Length(), WorldMatrix.Row3.Length())); halfBoxSize = maxScale * halfSpriteSize.Length() * Vector3.One; } else { halfBoxSize = new Vector3( Math.Abs(WorldMatrix.M11 * halfSpriteSize.X) + Math.Abs(WorldMatrix.M21 * halfSpriteSize.Y), Math.Abs(WorldMatrix.M12 * halfSpriteSize.X) + Math.Abs(WorldMatrix.M22 * halfSpriteSize.Y), Math.Abs(WorldMatrix.M13 * halfSpriteSize.X) + Math.Abs(WorldMatrix.M23 * halfSpriteSize.Y)); } // Update bounding box BoundingBox = new BoundingBoxExt(boxWorldPosition - halfBoxSize, boxWorldPosition + halfBoxSize); // Save current state for next check lastWorldMatrix = WorldMatrix; lastHalfSpriteSize = halfSpriteSize; lastSpriteType = SpriteType; } }
public void Build(RenderDrawContext context) { if (Mesh == null && TryGetHeightMapImageData(context.CommandList, out var data)) { Data = new GeometryBuilder(data).BuildTerrainData(Size, MaxHeight, UvScale); MeshBoundingBox = Utils.FromPoints(Data.Vertices); MeshBoundingSphere = BoundingSphere.FromBox(MeshBoundingBox); BoundingBox = new BoundingBoxExt(MeshBoundingBox); var vertexBuffer = Buffer.Vertex.New(context.GraphicsDevice, Data.Vertices, GraphicsResourceUsage.Dynamic); var indexBuffer = Buffer.Index.New(context.GraphicsDevice, Data.Indices); var vertexBufferBinding = new VertexBufferBinding(vertexBuffer, VertexPositionNormalTexture.Layout, vertexBuffer.ElementCount); var indexBufferBinding = new IndexBufferBinding(indexBuffer, true, indexBuffer.ElementCount); MeshDraw = new MeshDraw { StartLocation = 0, PrimitiveType = PrimitiveType.TriangleList, VertexBuffers = new[] { vertexBufferBinding }, IndexBuffer = indexBufferBinding, DrawCount = indexBuffer.ElementCount }; Mesh = new Mesh(MeshDraw, new ParameterCollection()); } }
// TODO: Find a way to replug this /// <summary> /// Adds a default frustum culling for rendering only meshes that are only inside the frustum/ /// </summary> /// <param name="modelRenderer">The model renderer.</param> /// <returns>ModelRenderer.</returns> //public static ModelComponentRenderer AddDefaultFrustumCulling(this ModelComponentRenderer modelRenderer) //{ // modelRenderer.UpdateMeshes = FrustumCulling; // return modelRenderer; //} private static void FrustumCulling(RenderContext context, FastList<RenderMesh> meshes) { Matrix viewProjection, mat1, mat2; // Compute view * projection context.Parameters.Get(TransformationKeys.View, out mat1); context.Parameters.Get(TransformationKeys.Projection, out mat2); Matrix.Multiply(ref mat1, ref mat2, out viewProjection); var frustum = new BoundingFrustum(ref viewProjection); for (var i = 0; i < meshes.Count; ++i) { var renderMesh = meshes[i]; // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Get world matrix renderMesh.Mesh.Parameters.Get(TransformationKeys.World, out mat1); // Compute transformed AABB (by world) var boundingBoxExt = new BoundingBoxExt(renderMesh.Mesh.BoundingBox); boundingBoxExt.Transform(mat1); // Perform frustum culling if (!frustum.Contains(ref boundingBoxExt)) { meshes.SwapRemoveAt(i--); } } }
private static void CalculateMinMaxDistance(ref Plane plane, ref BoundingBoxExt boundingBox, ref float minDistance, ref float maxDistance) { var nearCorner = boundingBox.Minimum; var farCorner = boundingBox.Maximum; if (plane.Normal.X < 0) { Utilities.Swap(ref nearCorner.X, ref farCorner.X); } if (plane.Normal.Y < 0) { Utilities.Swap(ref nearCorner.Y, ref farCorner.Y); } if (plane.Normal.Z < 0) { Utilities.Swap(ref nearCorner.Z, ref farCorner.Z); } var distance = CollisionHelper.DistancePlanePoint(ref plane, ref nearCorner); if (minDistance > distance) { minDistance = distance; } distance = CollisionHelper.DistancePlanePoint(ref plane, ref farCorner); if (maxDistance < distance) { maxDistance = distance; } }
public static void Update(ModelViewHierarchyUpdater hierarchy, RenderModel renderModel, int slot) { var boneMatrices = staticBoneMatrices; var meshes = renderModel.RenderMeshesList[slot]; { if (meshes == null) { return; } foreach (var renderMesh in meshes) { var mesh = renderMesh.Mesh; var skinning = mesh.Skinning; if (skinning == null) { // For unskinned meshes, use the original bounding box var boundingBoxExt = (BoundingBoxExt)mesh.BoundingBox; boundingBoxExt.Transform(renderMesh.WorldMatrix); renderMesh.BoundingBox = boundingBoxExt; continue; } var bones = skinning.Bones; // Make sure there is enough spaces in boneMatrices if (boneMatrices == null || bones.Length > boneMatrices.Length) { staticBoneMatrices = boneMatrices = new Matrix[bones.Length]; } var bindPoseBoundingBox = new BoundingBoxExt(renderMesh.Mesh.BoundingBox); renderMesh.BoundingBox = BoundingBoxExt.Empty; for (int index = 0; index < bones.Length; index++) { var nodeIndex = bones[index].NodeIndex; // Compute bone matrix Matrix.Multiply(ref bones[index].LinkToMeshMatrix, ref hierarchy.NodeTransformations[nodeIndex].WorldMatrix, out boneMatrices[index]); // Calculate and extend bounding box for each bone // TODO: Move runtime bounding box into ModelViewHierarchyUpdater? // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Compute transformed AABB (by world) var boundingBoxExt = bindPoseBoundingBox; boundingBoxExt.Transform(boneMatrices[index]); BoundingBoxExt.Merge(ref renderMesh.BoundingBox, ref boundingBoxExt, out renderMesh.BoundingBox); } // Upload bones renderMesh.Parameters.Set(TransformationSkinningKeys.BlendMatrixArray, boneMatrices, 0, bones.Length); } } }
internal void Update(TransformComponent transformComponent, ref Matrix worldMatrix) { if (!Enabled || ModelViewHierarchy == null || model == null) { return; } // Check if scaling is negative bool isScalingNegative = false; { Vector3 scale, translation; Matrix rotation; if (worldMatrix.Decompose(out scale, out rotation, out translation)) { isScalingNegative = scale.X * scale.Y * scale.Z < 0.0f; } } // Update model view hierarchy node matrices modelViewHierarchy.NodeTransformations[0].LocalMatrix = worldMatrix; modelViewHierarchy.NodeTransformations[0].IsScalingNegative = isScalingNegative; modelViewHierarchy.UpdateMatrices(); // Update the bounding sphere / bounding box in world space var meshes = Model.Meshes; var modelBoundingSphere = BoundingSphere.Empty; var modelBoundingBox = BoundingBox.Empty; bool hasBoundingBox = false; Matrix world; foreach (var mesh in meshes) { var meshBoundingSphere = mesh.BoundingSphere; modelViewHierarchy.GetWorldMatrix(mesh.NodeIndex, out world); Vector3.TransformCoordinate(ref meshBoundingSphere.Center, ref world, out meshBoundingSphere.Center); BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere); var boxExt = new BoundingBoxExt(mesh.BoundingBox); boxExt.Transform(world); var meshBox = (BoundingBox)boxExt; if (hasBoundingBox) { BoundingBox.Merge(ref modelBoundingBox, ref meshBox, out modelBoundingBox); } else { modelBoundingBox = meshBox; } hasBoundingBox = true; } // Update the bounds BoundingBox = modelBoundingBox; BoundingSphere = modelBoundingSphere; }
private static void BoundingBoxIgnoreWorld(InstancingData instancingData, IInstancing instancing, ModelComponent.MeshInfo meshInfo, Mesh mesh) { // We need to remove the world transformation component if (instancingData.ModelComponent.Skeleton != null) { var ibb = instancing.BoundingBox; var mbb = new BoundingBoxExt(meshInfo.BoundingBox); Matrix.Invert(ref instancingData.ModelComponent.Skeleton.NodeTransformations[0].LocalMatrix, out var invWorld);
public static void Update(ModelViewHierarchyUpdater hierarchy, RenderModel renderModel, int slot) { var boneMatrices = staticBoneMatrices; var meshes = renderModel.RenderMeshesList[slot]; { if (meshes == null) { return; } foreach (var renderMesh in meshes) { var mesh = renderMesh.Mesh; var skinning = mesh.Skinning; if (skinning == null) { // For unskinned meshes, use the original bounding box var boundingBoxExt = (BoundingBoxExt)mesh.BoundingBox; boundingBoxExt.Transform(renderMesh.WorldMatrix); renderMesh.BoundingBox = boundingBoxExt; continue; } var bones = skinning.Bones; // Make sure there is enough spaces in boneMatrices if (boneMatrices == null || bones.Length > boneMatrices.Length) staticBoneMatrices = boneMatrices = new Matrix[bones.Length]; var bindPoseBoundingBox = new BoundingBoxExt(renderMesh.Mesh.BoundingBox); renderMesh.BoundingBox = BoundingBoxExt.Empty; for (int index = 0; index < bones.Length; index++) { var nodeIndex = bones[index].NodeIndex; // Compute bone matrix Matrix.Multiply(ref bones[index].LinkToMeshMatrix, ref hierarchy.NodeTransformations[nodeIndex].WorldMatrix, out boneMatrices[index]); // Calculate and extend bounding box for each bone // TODO: Move runtime bounding box into ModelViewHierarchyUpdater? // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Compute transformed AABB (by world) var boundingBoxExt = bindPoseBoundingBox; boundingBoxExt.Transform(boneMatrices[index]); BoundingBoxExt.Merge(ref renderMesh.BoundingBox, ref boundingBoxExt, out renderMesh.BoundingBox); } // Upload bones renderMesh.Parameters.Set(TransformationSkinningKeys.BlendMatrixArray, boneMatrices, 0, bones.Length); } } }
/// <summary> /// Updates this instance( <see cref="Position"/>, <see cref="Direction"/>, <see cref="HasBoundingBox"/>, <see cref="BoundingBox"/>, <see cref="BoundingBoxExt"/> /// </summary> /// <param name="colorSpace"></param> internal void UpdateBoundingBox() { // Compute bounding boxes HasBoundingBox = false; BoundingBox = new BoundingBox(); BoundingBoxExt = new BoundingBoxExt(); var directLight = Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox) { // Computes the bounding boxes BoundingBox = directLight.ComputeBounds(Position, Direction); BoundingBoxExt = new BoundingBoxExt(BoundingBox); } }
protected virtual void CalculateBounds(ref ShortPoint location, out BoundingBoxExt bounds, out Vector2 origin) { var blockSize = BlockSize; int left, top, right, bottom; GridBlock.CalculateEdges(ref blockSize, ref location, out left, out top, out right, out bottom); var size = CellSize; var topLeft = new Vector3(left * size.X, top * size.Y, 0); var bottomRight = new Vector3(right * size.X, bottom * size.Y, 0); Vector3 min, max; Vector3.Min(ref topLeft, ref bottomRight, out min); Vector3.Max(ref topLeft, ref bottomRight, out max); bounds = new BoundingBoxExt(min, max); origin = topLeft.XY(); }
private void SetupLight(RenderDrawContext context, RenderLightShaft lightShaft, LightShadowMapTexture shadowMapTexture, ParameterCollection lightParameterCollection) { BoundingBoxExt box = new BoundingBoxExt(new Vector3(-float.MaxValue), new Vector3(float.MaxValue)); // TODO LightShaftRenderData data; if (!renderData.TryGetValue(lightShaft.Light2, out data)) { data = new LightShaftRenderData(); renderData.Add(lightShaft.Light2, data); UpdateRenderData(context, data, lightShaft, shadowMapTexture); } if (shadowMapTexture != null && data.ShadowMapRenderer != null) { // Detect changed shadow map renderer or type if (data.ShadowMapRenderer != shadowMapTexture.Renderer || data.ShadowType != shadowMapTexture.ShadowType) { UpdateRenderData(context, data, lightShaft, shadowMapTexture); } } else if (shadowMapTexture?.Renderer != data.ShadowMapRenderer) // Change from no shadows to shadows { UpdateRenderData(context, data, lightShaft, shadowMapTexture); } data.RenderViews[0] = context.RenderContext.RenderView; data.ShaderGroup.Reset(); data.ShaderGroup.SetViews(data.RenderViews); data.ShaderGroup.AddView(0, context.RenderContext.RenderView, 1); data.ShaderGroup.AddLight(lightShaft.Light, shadowMapTexture); data.ShaderGroup.UpdateLayout("lightGroup"); lightParameterCollection.Set(LightShaftsEffectKeys.LightGroup, data.ShaderGroup.ShaderSource); // Update the effect here so the layout is correct lightShaftsEffectShader.EffectInstance.UpdateEffect(GraphicsDevice); data.ShaderGroup.ApplyViewParameters(context, 0, lightParameterCollection); data.ShaderGroup.ApplyDrawParameters(context, 0, lightParameterCollection, ref box); data.UsageCounter = usageCounter; }
internal void Update(ref Matrix worldMatrix, bool isScalingNegative) { // Update model view hierarchy node matrices modelViewHierarchy.NodeTransformations[0].LocalMatrix = worldMatrix; modelViewHierarchy.NodeTransformations[0].IsScalingNegative = isScalingNegative; modelViewHierarchy.UpdateMatrices(); // Update the bounding sphere / bounding box in world space var meshes = Model.Meshes; var modelBoundingSphere = BoundingSphere.Empty; var modelBoundingBox = BoundingBox.Empty; bool hasBoundingBox = false; Matrix world; foreach (var mesh in meshes) { var meshBoundingSphere = mesh.BoundingSphere; modelViewHierarchy.GetWorldMatrix(mesh.NodeIndex, out world); Vector3.TransformCoordinate(ref meshBoundingSphere.Center, ref world, out meshBoundingSphere.Center); BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere); var boxExt = new BoundingBoxExt(mesh.BoundingBox); boxExt.Transform(world); var meshBox = (BoundingBox)boxExt; if (hasBoundingBox) { BoundingBox.Merge(ref modelBoundingBox, ref meshBox, out modelBoundingBox); } else { modelBoundingBox = meshBox; } hasBoundingBox = true; } // Update the bounds BoundingBox = modelBoundingBox; BoundingSphere = modelBoundingSphere; }
public override void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { // TODO THREADING: Make CurrentLights and lightData (thread-) local lock (applyLock) { CurrentLights.Clear(); var lightRange = LightRanges[viewIndex]; for (int i = lightRange.Start; i < lightRange.End; ++i) CurrentLights.Add(Lights[i]); base.ApplyDrawParameters(context, viewIndex, parameters, ref boundingBox); // TODO: Octree structure to select best lights quicker var boundingBox2 = (BoundingBox)boundingBox; for (int i = 0; i < CurrentLights.Count; i++) { var light = CurrentLights[i].Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var spotLight = (LightSpot)light.Type; lightsData.Add(new SpotLightData { PositionWS = light.Position, DirectionWS = light.Direction, AngleOffsetAndInvSquareRadius = new Vector3(spotLight.LightAngleScale, spotLight.LightAngleOffset, spotLight.InvSquareRange), Color = light.Color, }); // Did we reach max number of simultaneous lights? // TODO: Still collect everything but sort by importance and remove the rest? if (lightsData.Count >= LightCurrentCount) break; } } parameters.Set(countKey, lightsData.Count); parameters.Set(lightsKey, lightsData.Count, ref lightsData.Items[0]); lightsData.Clear(); } }
/// <summary> /// Updates this instance( <see cref="Position"/>, <see cref="Direction"/>, <see cref="HasBoundingBox"/>, <see cref="BoundingBox"/>, <see cref="BoundingBoxExt"/> /// </summary> public bool Update() { if (Type == null || !Enabled || !Type.Update(this)) { return(false); } // Compute light direction and position Vector3 lightDirection; var lightDir = DefaultDirection; Vector3.TransformNormal(ref lightDir, ref Entity.Transform.WorldMatrix, out lightDirection); lightDirection.Normalize(); Position = Entity.Transform.WorldMatrix.TranslationVector; Direction = lightDirection; // Color var colorLight = Type as IColorLight; Color = (colorLight != null) ? colorLight.ComputeColor(Intensity) : new Color3(); // Compute bounding boxes HasBoundingBox = false; BoundingBox = new BoundingBox(); BoundingBoxExt = new BoundingBoxExt(); var directLight = Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox) { // Computes the bounding boxes BoundingBox = directLight.ComputeBounds(Position, Direction); BoundingBoxExt = new BoundingBoxExt(BoundingBox); } return(true); }
private void UpdateRenderModel(RenderModel renderModel) { if (renderModel.ModelComponent.Model == null) { return; } var modelComponent = renderModel.ModelComponent; var modelViewHierarchy = modelComponent.Skeleton; var nodeTransformations = modelViewHierarchy.NodeTransformations; // TODO GRAPHICS REFACTOR compute bounding box either by Mesh, or switch to future VisibilityObject system to deal with complete models) var boundingBox = new BoundingBoxExt(modelComponent.BoundingBox); var modelComponentMaterials = modelComponent.Materials; var modelMaterials = renderModel.ModelComponent.Model.Materials; foreach (var renderMesh in renderModel.Meshes) { var mesh = renderMesh.Mesh; renderMesh.Enabled = modelComponent.Enabled; if (renderMesh.Enabled) { // Update material var materialIndex = mesh.MaterialIndex; var materialOverride = modelComponentMaterials.GetItemOrNull(materialIndex); var modelMaterialInstance = modelMaterials.GetItemOrNull(materialIndex); UpdateMaterial(renderMesh, materialOverride, modelMaterialInstance, modelComponent); // Copy world matrix var nodeIndex = mesh.NodeIndex; renderMesh.World = nodeTransformations[nodeIndex].WorldMatrix; renderMesh.BoundingBox = boundingBox; renderMesh.RenderGroup = modelComponent.Entity.Group; } } }
public override void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { CurrentLights.Clear(); var lightRange = LightRanges[viewIndex]; for (int i = lightRange.Start; i < lightRange.End; ++i) CurrentLights.Add(Lights[i]); base.ApplyDrawParameters(context, viewIndex, parameters, ref boundingBox); // TODO: Since we cull per object, we could maintain a higher number of allowed light than the shader support (i.e. 4 lights active per object even though the scene has many more of them) // TODO: Octree structure to select best lights quicker var boundingBox2 = (BoundingBox)boundingBox; foreach (var lightEntry in CurrentLights) { var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var pointLight = (LightPoint)light.Type; lightsData.Add(new PointLightData { PositionWS = light.Position, InvSquareRadius = pointLight.InvSquareRadius, Color = light.Color, }); // Did we reach max number of simultaneous lights? // TODO: Still collect everything but sort by importance and remove the rest? if (lightsData.Count >= LightCurrentCount) break; } } parameters.Set(countKey, lightsData.Count); parameters.Set(lightsKey, lightsData.Count, ref lightsData.Items[0]); lightsData.Clear(); }
/// <summary> /// Applies PerDraw lighting parameters. /// </summary> /// <param name="context"></param> /// <param name="viewIndex"></param> /// <param name="parameters"></param> /// <param name="boundingBox"></param> public virtual void ApplyDrawParameters(FastListStruct <LightDynamicEntry>?lightList, RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { }
/// <inheritdoc/> public override void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { base.ApplyDrawParameters(context, viewIndex, parameters, ref boundingBox); ShadowGroup?.ApplyDrawParameters(context, parameters, CurrentLights, ref boundingBox); }
public override void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBox2 = (BoundingBox)boundingBox; bool shadowMapCreated = false; int lightIndex = 0; for (int i = 0; i < currentLights.Count; ++i) { var lightEntry = currentLights[i]; if (lightEntry.Light.BoundingBox.Intersects(ref boundingBox2)) { var shaderData = (ShaderData)lightEntry.ShadowMapTexture.ShaderData; // Copy per-face data for (int j = 0; j < 6; j++) { worldToShadow[lightIndex * 6 + j] = shaderData.WorldToShadow[j]; inverseWorldToShadow[lightIndex * 6 + j] = Matrix.Invert(shaderData.WorldToShadow[j]); } depthBiases[lightIndex] = shaderData.DepthBias; offsetScales[lightIndex] = shaderData.OffsetScale; depthParameters[lightIndex] = shaderData.DepthParameters; lightIndex++; // TODO: should be setup just once at creation time if (!shadowMapCreated) { shadowMapTexture = shaderData.Texture; if (shadowMapTexture != null) { shadowMapTextureSize = new Vector2(shadowMapTexture.Width, shadowMapTexture.Height); shadowMapTextureTexelSize = 1.0f / shadowMapTextureSize; } shadowMapCreated = true; } } } parameters.Set(shadowMapTextureKey, shadowMapTexture); parameters.Set(shadowMapTextureSizeKey, shadowMapTextureSize); parameters.Set(shadowMapTextureTexelSizeKey, shadowMapTextureTexelSize); parameters.Set(worldToShadowKey, worldToShadow); parameters.Set(inverseWorldToShadowKey, inverseWorldToShadow); parameters.Set(depthParametersKey, depthParameters); parameters.Set(depthBiasesKey, depthBiases); parameters.Set(offsetScalesKey, offsetScales); }
private static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt, bool ignoreDepthPlanes) { unsafe { fixed (Plane* planeStart = &frustum.LeftPlane) { var plane = planeStart; for (int i = 0; i < 6; ++i) { if (ignoreDepthPlanes && i > 3) continue; // Previous code: if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) <= -plane->D) return false; plane++; } } return true; } }
private static void CalculateMinMaxDistance(RenderView view, ref Plane plane, ref BoundingBoxExt boundingBox) { // TODO GRAPHICS REFACTOR: Optimize per-view: Only two corners need checking, depending on view direction. Also, currently unnecessary for shadow views. var minimum = boundingBox.Minimum; var maximum = boundingBox.Maximum; var point = minimum; var distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.X = maximum.X; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.Y = maximum.Y; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.X = minimum.X; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.Z = maximum.Z; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.Y = minimum.Y; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.X = maximum.X; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); point.Y = maximum.Y; distance = CollisionHelper.DistancePlanePoint(ref plane, ref point); MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance); }
public override void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBox2 = (BoundingBox)boundingBox; bool shadowMapCreated = false; int lightIndex = 0; for (int i = 0; i < currentLights.Count; ++i) { var lightEntry = currentLights[i]; var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var singleLightData = (LightSpotShadowMapShaderData)lightEntry.ShadowMapTexture.ShaderData; worldToShadowCascadeUV[lightIndex] = singleLightData.WorldToShadowCascadeUV; depthBiases[lightIndex] = singleLightData.DepthBias; offsetScales[lightIndex] = singleLightData.OffsetScale; lightIndex++; if (!shadowMapCreated) { shadowMapTexture = singleLightData.Texture; if (shadowMapTexture != null) { shadowMapTextureSize = new Vector2(shadowMapTexture.Width, shadowMapTexture.Height); shadowMapTextureTexelSize = 1.0f / shadowMapTextureSize; } shadowMapCreated = true; } } } parameters.Set(shadowMapTextureKey, shadowMapTexture); parameters.Set(shadowMapTextureSizeKey, shadowMapTextureSize); parameters.Set(shadowMapTextureTexelSizeKey, shadowMapTextureTexelSize); parameters.Set(worldToShadowCascadeUVsKey, worldToShadowCascadeUV); parameters.Set(depthBiasesKey, depthBiases); parameters.Set(offsetScalesKey, offsetScales); }
internal void Update(ref Matrix worldMatrix) { // Update model view hierarchy node matrices modelViewHierarchy.NodeTransformations[0].LocalMatrix = worldMatrix; modelViewHierarchy.UpdateMatrices(); // Update the bounding sphere / bounding box in world space var meshes = Model.Meshes; var modelBoundingSphere = BoundingSphere.Empty; var modelBoundingBox = BoundingBox.Empty; bool hasBoundingBox = false; Matrix world; foreach (var mesh in meshes) { var meshBoundingSphere = mesh.BoundingSphere; modelViewHierarchy.GetWorldMatrix(mesh.NodeIndex, out world); Vector3.TransformCoordinate(ref meshBoundingSphere.Center, ref world, out meshBoundingSphere.Center); BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere); var boxExt = new BoundingBoxExt(mesh.BoundingBox); boxExt.Transform(world); var meshBox = (BoundingBox)boxExt; if (hasBoundingBox) { BoundingBox.Merge(ref modelBoundingBox, ref meshBox, out modelBoundingBox); } else { modelBoundingBox = meshBox; } hasBoundingBox = true; } // Update the bounds BoundingBox = modelBoundingBox; BoundingSphere = modelBoundingSphere; }
/// <summary> /// Determines whether a <see cref="BoundingFrustum" /> intersects or contains an AABB determined by its center and extent. /// Faster variant specific for frustum culling. /// </summary> /// <param name="frustum">The frustum.</param> /// <param name="boundingBoxExt">The bounding box ext.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt) { unsafe { fixed (Plane* planeStart = &frustum.LeftPlane) { var plane = planeStart; for (int i = 0; i < 6; ++i) { // Previous code: if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) <= -plane->D) return false; plane++; } } return true; } /* unsafe { fixed (Plane* planeStart = &frustum.LeftPlane) fixed (Vector3* pExtent = &boundingBoxExt.Extent) { var plane = planeStart; for (int i = 0; i < 6; ++i) { // Previous code: //if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) // + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) // + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) // + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) // <= -plane->D) // Optimized version (only 1 dot and cheaper Math.Abs) // https://fgiesen.wordpress.com/2010/10/17/view-frustum-culling/ // return dot3(center, plane) + dot3(extent, absPlane) <= -plane.w; // or // vector4 signFlip = componentwise_and(plane, 0x80000000); // vector3 centerOffset = xor(extent, signFlip) // dot3(center + centerOffset, plane) <= -plane.w; uint val = (((uint*)&plane->Normal)[0] & 0x80000000) ^ ((uint*)pExtent)[0]; var dist = plane->Normal.X * ((*(float*)(&val)) + boundingBoxExt.Center.X); val = (((uint*)&plane->Normal)[1] & 0x80000000) ^ ((uint*)pExtent)[1]; dist += plane->Normal.Y * ((*(float*)(&val)) + boundingBoxExt.Center.Y); val = (((uint*)&plane->Normal)[2] & 0x80000000) ^ ((uint*)pExtent)[2]; dist += plane->Normal.Z * ((*(float*)(&val)) + boundingBoxExt.Center.Z); if (dist <= -plane->D) return false; plane++; } } return true; } */ }
/// <summary> /// Calculate the bounding sphere of the entity's models. /// </summary> /// <param name="entity">The entity to measure</param> /// <param name="isRecursive">Indicate the child entities bounding spheres should be merged</param> /// <param name="meshSelector">Selects which meshes are considered for bounding box calculation.</param> /// <returns>The bounding sphere (world matrix included)</returns> public static BoundingSphere CalculateBoundSphere(this Entity entity, bool isRecursive = true, Func <Model, IEnumerable <Mesh> > meshSelector = null) { entity.Transform.UpdateWorldMatrix(); var worldMatrix = entity.Transform.WorldMatrix; var boundingSphere = BoundingSphere.Empty; // calculate the bounding sphere of the model if any var modelComponent = entity.Get <ModelComponent>(); var hasModel = modelComponent?.Model != null; if (hasModel) { var hierarchy = modelComponent.Skeleton; var nodeTransforms = new Matrix[hierarchy.Nodes.Length]; // Calculate node transforms here, since there might not be a ModelProcessor running for (int i = 0; i < nodeTransforms.Length; i++) { if (hierarchy.Nodes[i].ParentIndex == -1) { nodeTransforms[i] = worldMatrix; } else { Matrix localMatrix; Matrix.Transformation( ref hierarchy.Nodes[i].Transform.Scale, ref hierarchy.Nodes[i].Transform.Rotation, ref hierarchy.Nodes[i].Transform.Position, out localMatrix); Matrix.Multiply(ref localMatrix, ref nodeTransforms[hierarchy.Nodes[i].ParentIndex], out nodeTransforms[i]); } } // calculate the bounding sphere var boundingBox = BoundingBoxExt.Empty; var meshes = modelComponent.Model.Meshes; var filteredMeshes = meshSelector == null ? meshes : meshSelector(modelComponent.Model); // Calculate skinned bounding boxes. // TODO: Cloned from ModelSkinningUpdater. Consolidate. foreach (var mesh in filteredMeshes) { var skinning = mesh.Skinning; if (skinning == null) { // For unskinned meshes, use the original bounding box var boundingBoxExt = (BoundingBoxExt)mesh.BoundingBox; boundingBoxExt.Transform(nodeTransforms[mesh.NodeIndex]); BoundingBoxExt.Merge(ref boundingBox, ref boundingBoxExt, out boundingBox); } else { var bones = skinning.Bones; var bindPoseBoundingBox = new BoundingBoxExt(mesh.BoundingBox); for (var index = 0; index < bones.Length; index++) { var nodeIndex = bones[index].NodeIndex; Matrix boneMatrix; // Compute bone matrix Matrix.Multiply(ref bones[index].LinkToMeshMatrix, ref nodeTransforms[nodeIndex], out boneMatrix); // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Compute transformed AABB (by world) var boundingBoxExt = bindPoseBoundingBox; boundingBoxExt.Transform(boneMatrix); BoundingBoxExt.Merge(ref boundingBox, ref boundingBoxExt, out boundingBox); } } } var halfSize = boundingBox.Extent; var maxHalfSize = Math.Max(halfSize.X, Math.Max(halfSize.Y, halfSize.Z)); boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(boundingBox.Center, maxHalfSize)); } // Calculate the bounding sphere for the sprite component if any and merge the result var spriteComponent = entity.Get <SpriteComponent>(); var hasSprite = spriteComponent?.CurrentSprite != null; if (hasSprite && !(hasModel && meshSelector != null)) { var spriteSize = spriteComponent.CurrentSprite.Size; var spriteDiagonalSize = (float)Math.Sqrt(spriteSize.X * spriteSize.X + spriteSize.Y * spriteSize.Y); // Note: this is probably wrong, need to unify with SpriteComponentRenderer var center = worldMatrix.TranslationVector; var scales = new Vector3(worldMatrix.Row1.Length(), worldMatrix.Row2.Length(), worldMatrix.Row3.Length()); var maxScale = Math.Max(scales.X, Math.Max(scales.Y, scales.Z)); boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(center, maxScale * spriteDiagonalSize / 2f)); } var spriteStudioComponent = entity.Get <SpriteStudioComponent>(); if (spriteStudioComponent != null) { // Make sure nodes are prepared if (!SpriteStudioProcessor.PrepareNodes(spriteStudioComponent)) { return(new BoundingSphere()); } // Update root nodes foreach (var node in spriteStudioComponent.Nodes) { node.UpdateTransformation(); } // Compute bounding sphere for each node foreach (var node in spriteStudioComponent.Nodes.SelectDeep(x => x.ChildrenNodes)) { if (node.Sprite == null || node.Hide != 0) { continue; } var nodeMatrix = node.ModelTransform * worldMatrix; var spriteSize = node.Sprite.Size; var spriteDiagonalSize = (float)Math.Sqrt(spriteSize.X * spriteSize.X + spriteSize.Y * spriteSize.Y); Vector3 pos, scale; nodeMatrix.Decompose(out scale, out pos); var center = pos; var maxScale = Math.Max(scale.X, scale.Y); //2d ignore Z boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(center, maxScale * (spriteDiagonalSize / 2f))); } } var particleComponent = entity.Get <ParticleSystemComponent>(); if (particleComponent != null) { var center = worldMatrix.TranslationVector; var sphere = particleComponent.ParticleSystem?.BoundingShape != null?BoundingSphere.FromBox(particleComponent.ParticleSystem.BoundingShape.GetAABB(center, Quaternion.Identity, 1.0f)) : new BoundingSphere(center, 2.0f); boundingSphere = BoundingSphere.Merge(boundingSphere, sphere); } var boundingBoxComponent = entity.Get <NavigationBoundingBoxComponent>(); if (boundingBoxComponent != null) { var center = worldMatrix.TranslationVector; var scales = new Vector3(worldMatrix.Row1.Length(), worldMatrix.Row2.Length(), worldMatrix.Row3.Length()) * boundingBoxComponent.Size; boundingSphere = BoundingSphere.FromBox(new BoundingBox(-scales + center, scales + center)); } // Extend the bounding sphere to include the children if (isRecursive) { foreach (var child in entity.GetChildren()) { boundingSphere = BoundingSphere.Merge(boundingSphere, child.CalculateBoundSphere(true, meshSelector)); } } // If the entity does not contain any components having an impact on the bounding sphere, create an empty bounding sphere centered on the entity position. if (boundingSphere == BoundingSphere.Empty) { boundingSphere = new BoundingSphere(worldMatrix.TranslationVector, 0); } return(boundingSphere); }
public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBoxCasted = (BoundingBox)boundingBox; int lightIndex = 0; for (int i = 0; i < currentLights.Count; ++i) { var lightEntry = currentLights[i]; var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBoxCasted)) { var spotLight = (LightSpot)light.Type; /* * // TODO: Just save the shaderdata struct directly within "LightDynamicEntry"? * var singleLightData = (LightSpotTextureProjectionShaderData)lightEntry.ShadowMapTexture.ShaderData; // TODO: This must not depend on the shadow map texture! * * worldToTextureUV[lightIndex] = singleLightData.WorldToTextureUV; * projectionTextureMipMapLevels[lightIndex] = singleLightData.ProjectiveTextureMipMapLevel; * projectiveTexture = singleLightData.ProjectiveTexture; */ // TODO: Move this to "Collect()" and use "LightSpotTextureProjectionShaderData", but IDK how! worldToTextureUV[lightIndex] = ComputeWorldToTextureUVMatrix(light); projectorPlaneMatrices[lightIndex] = ComputeProjectorPlaneMatrix(light); // We use the maximum number of mips instead of the actual number, // so things like video textures behave more consistently when changing the number of mip maps to generate. int maxMipMapCount = Texture.CountMips(lightParameters.ProjectionTexture.Width, lightParameters.ProjectionTexture.Height); float projectiveTextureMipMapLevel = (float)(maxMipMapCount - 1) * spotLight.MipMapScale; // "- 1" because the lowest mip level is 0, not 1. projectionTextureMipMapLevels[lightIndex] = projectiveTextureMipMapLevel; transitionAreas[lightIndex] = Math.Max(spotLight.TransitionArea, 0.001f); // Keep the value just above zero. This is to prevent some issues with the "smoothstep()" function on OpenGL and OpenGL ES. ++lightIndex; } } // TODO: Why is this set if it's already in the collection? // TODO: Does this get set once per group or something? parameters.Set(projectiveTextureKey, lightParameters.ProjectionTexture); parameters.Set(uvScale, lightParameters.UVScale); parameters.Set(uvOffset, lightParameters.UVOffset); parameters.Set(worldToProjectiveTextureUVsKey, worldToTextureUV); parameters.Set(projectorPlaneMatricesKey, projectorPlaneMatrices); parameters.Set(projectionTextureMipMapLevelsKey, projectionTextureMipMapLevels); parameters.Set(transitionAreasKey, transitionAreas); }
public override void ApplyDrawParameters(FastListStruct <LightDynamicEntry>?lightList, RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { FastListStruct <SpotLightData> lightsData = new FastListStruct <SpotLightData>(8); if (lightList == null) { lightList = new FastListStruct <LightDynamicEntry>(8); } FastListStruct <LightDynamicEntry> currentLights = lightList.Value; var lightRange = lightRanges[viewIndex]; for (int i = lightRange.Start; i < lightRange.End; ++i) { currentLights.Add(lights[i]); } base.ApplyDrawParameters(currentLights, context, viewIndex, parameters, ref boundingBox); // TODO: Octree structure to select best lights quicker var boundingBox2 = (BoundingBox)boundingBox; for (int i = 0; i < currentLights.Count; i++) { var light = currentLights[i].Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var spotLight = (LightSpot)light.Type; lightsData.Add(new SpotLightData { PositionWS = light.Position, DirectionWS = light.Direction, AngleOffsetAndInvSquareRadius = new Vector3(spotLight.LightAngleScale, spotLight.LightAngleOffset, spotLight.InvSquareRange), Color = light.Color, }); // Did we reach max number of simultaneous lights? // TODO: Still collect everything but sort by importance and remove the rest? if (lightsData.Count >= LightCurrentCount) { break; } } } parameters.Set(countKey, lightsData.Count); parameters.Set(lightsKey, lightsData.Count, ref lightsData.Items[0]); TextureProjectionShaderGroupData?.ApplyDrawParameters(context, parameters, currentLights, ref boundingBox); }
private bool DrawBoundingVolumes(RenderDrawContext context, IReadOnlyList <RenderLightShaftBoundingVolume> boundingVolumes, Matrix viewProjection) { var commandList = context.CommandList; bool effectUpdated = minmaxVolumeEffectShader.UpdateEffect(GraphicsDevice); if (minmaxVolumeEffectShader.Effect == null) { return(false); } var needEffectUpdate = effectUpdated || previousMinmaxEffectBytecode != minmaxVolumeEffectShader.Effect.Bytecode; bool visibleMeshes = false; for (int pass = 0; pass < 2; ++pass) { var minmaxPipelineState = minmaxPipelineStates[pass]; bool pipelineDirty = false; if (needEffectUpdate) { // The EffectInstance might have been updated from outside previousMinmaxEffectBytecode = minmaxVolumeEffectShader.Effect.Bytecode; minmaxPipelineState.State.RootSignature = minmaxVolumeEffectShader.RootSignature; minmaxPipelineState.State.EffectBytecode = minmaxVolumeEffectShader.Effect.Bytecode; minmaxPipelineState.State.Output.RenderTargetCount = 1; minmaxPipelineState.State.Output.RenderTargetFormat0 = commandList.RenderTarget.Format; pipelineDirty = true; } MeshDraw currentDraw = null; var frustum = new BoundingFrustum(ref viewProjection); foreach (var volume in boundingVolumes) { if (volume.Model == null) { continue; } // Update parameters for the minmax shader Matrix worldViewProjection = Matrix.Multiply(volume.World, viewProjection); minmaxVolumeEffectShader.Parameters.Set(VolumeMinMaxShaderKeys.WorldViewProjection, worldViewProjection); foreach (var mesh in volume.Model.Meshes) { // Frustum culling BoundingBox meshBoundingBox; Matrix world = volume.World; BoundingBox.Transform(ref mesh.BoundingBox, ref world, out meshBoundingBox); var boundingBoxExt = new BoundingBoxExt(meshBoundingBox); if (boundingBoxExt.Extent != Vector3.Zero && !VisibilityGroup.FrustumContainsBox(ref frustum, ref boundingBoxExt, true)) { continue; } visibleMeshes = true; var draw = mesh.Draw; if (currentDraw != draw) { if (minmaxPipelineState.State.PrimitiveType != draw.PrimitiveType) { minmaxPipelineState.State.PrimitiveType = draw.PrimitiveType; pipelineDirty = true; } var inputElements = draw.VertexBuffers.CreateInputElements(); if (inputElements.ComputeHash() != minmaxPipelineState.State.InputElements.ComputeHash()) { minmaxPipelineState.State.InputElements = inputElements; pipelineDirty = true; } // Update mesh for (int i = 0; i < draw.VertexBuffers.Length; i++) { var vertexBuffer = draw.VertexBuffers[i]; commandList.SetVertexBuffer(i, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride); } if (draw.IndexBuffer != null) { commandList.SetIndexBuffer(draw.IndexBuffer.Buffer, draw.IndexBuffer.Offset, draw.IndexBuffer.Is32Bit); } currentDraw = draw; } if (pipelineDirty) { minmaxPipelineState.Update(); pipelineDirty = false; } context.CommandList.SetPipelineState(minmaxPipelineState.CurrentState); minmaxVolumeEffectShader.Apply(context.GraphicsContext); // Draw if (currentDraw.IndexBuffer == null) { commandList.Draw(currentDraw.DrawCount, currentDraw.StartLocation); } else { commandList.DrawIndexed(currentDraw.DrawCount, currentDraw.StartLocation); } } } } return(visibleMeshes); }
internal void Update(TransformComponent transformComponent, ref Matrix worldMatrix) { if (!Enabled || model == null) return; // Check if scaling is negative bool isScalingNegative = false; { Vector3 scale, translation; Matrix rotation; if (worldMatrix.Decompose(out scale, out rotation, out translation)) isScalingNegative = scale.X*scale.Y*scale.Z < 0.0f; } // Make sure skeleton is up to date CheckSkeleton(); if (skeleton != null) { // Update model view hierarchy node matrices skeleton.NodeTransformations[0].LocalMatrix = worldMatrix; skeleton.NodeTransformations[0].IsScalingNegative = isScalingNegative; skeleton.UpdateMatrices(); } // Update the bounding sphere / bounding box in world space var meshes = Model.Meshes; var modelBoundingSphere = BoundingSphere.Empty; var modelBoundingBox = BoundingBox.Empty; bool hasBoundingBox = false; Matrix world; foreach (var mesh in meshes) { var meshBoundingSphere = mesh.BoundingSphere; if (skeleton != null) skeleton.GetWorldMatrix(mesh.NodeIndex, out world); else world = worldMatrix; Vector3.TransformCoordinate(ref meshBoundingSphere.Center, ref world, out meshBoundingSphere.Center); BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere); var boxExt = new BoundingBoxExt(mesh.BoundingBox); boxExt.Transform(world); var meshBox = (BoundingBox)boxExt; if (hasBoundingBox) { BoundingBox.Merge(ref modelBoundingBox, ref meshBox, out modelBoundingBox); } else { modelBoundingBox = meshBox; } hasBoundingBox = true; } // Update the bounds BoundingBox = modelBoundingBox; BoundingSphere = modelBoundingSphere; }
/// <summary> /// Updates this instance( <see cref="Position"/>, <see cref="Direction"/>, <see cref="HasBoundingBox"/>, <see cref="BoundingBox"/>, <see cref="BoundingBoxExt"/> /// </summary> public bool Update() { if (Type == null || !Enabled || !Type.Update(this)) { return false; } // Compute light direction and position Vector3 lightDirection; var lightDir = DefaultDirection; Vector3.TransformNormal(ref lightDir, ref Entity.Transform.WorldMatrix, out lightDirection); lightDirection.Normalize(); Position = Entity.Transform.Position; Direction = lightDirection; // Color var colorLight = Type as IColorLight; Color = (colorLight != null) ? colorLight.ComputeColor(Intensity) : new Color3(); // Compute bounding boxes HasBoundingBox = false; BoundingBox = new BoundingBox(); BoundingBoxExt = new BoundingBoxExt(); var directLight = Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox) { // Computes the bounding boxes BoundingBox = directLight.ComputeBounds(Position, Direction); BoundingBoxExt = new BoundingBoxExt(BoundingBox); } return true; }
public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct<LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { }
/// <summary> /// Applies PerDraw lighting parameters. /// </summary> /// <param name="context"></param> /// <param name="viewIndex"></param> /// <param name="parameters"></param> /// <param name="boundingBox"></param> public virtual void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { }
public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct<LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBox2 = (BoundingBox)boundingBox; bool shadowMapCreated = false; int lightIndex = 0; for (int i = 0; i < currentLights.Count; ++i) { var lightEntry = currentLights[i]; var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var singleLightData = (LightSpotShadowMapShaderData)lightEntry.ShadowMapTexture.ShaderData; worldToShadowCascadeUV[lightIndex] = singleLightData.WorldToShadowCascadeUV; depthBiases[lightIndex] = singleLightData.DepthBias; offsetScales[lightIndex] = singleLightData.OffsetScale; lightIndex++; if (!shadowMapCreated) { shadowMapTexture = singleLightData.Texture; if (shadowMapTexture != null) { shadowMapTextureSize = new Vector2(shadowMapTexture.Width, shadowMapTexture.Height); shadowMapTextureTexelSize = 1.0f / shadowMapTextureSize; } shadowMapCreated = true; } } } parameters.Set(shadowMapTextureKey, shadowMapTexture); parameters.Set(shadowMapTextureSizeKey, shadowMapTextureSize); parameters.Set(shadowMapTextureTexelSizeKey, shadowMapTextureTexelSize); parameters.Set(worldToShadowCascadeUVsKey, worldToShadowCascadeUV); parameters.Set(depthBiasesKey, depthBiases); parameters.Set(offsetScalesKey, offsetScales); }
public bool Contains(ref BoundingBoxExt boundingBoxExt) { return Collision.FrustumContainsBox(ref this, ref boundingBoxExt); }
public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { }
private void PrepareModels(RenderContext context, List <RenderModel> renderModels, RenderItemCollection opaqueList, RenderItemCollection transparentList) { // If no camera, early exit var camera = context.GetCurrentCamera(); if (camera == null) { return; } var viewProjectionMatrix = camera.ViewProjectionMatrix; var preRenderModel = Callbacks.PreRenderModel; var sceneCameraRenderer = context.Tags.Get(SceneCameraRenderer.Current); var cullingMode = sceneCameraRenderer != null ? sceneCameraRenderer.CullingMode : CullingMode.None; var frustum = new BoundingFrustum(ref viewProjectionMatrix); var cameraRenderMode = sceneCameraRenderer != null ? sceneCameraRenderer.Mode : null; foreach (var renderModel in renderModels) { // If Model is null, then skip it if (renderModel.Model == null) { continue; } if (preRenderModel != null) { if (!preRenderModel(context, renderModel)) { continue; } } // Always prepare the slot for the render meshes even if they are not used. EnsureRenderMeshes(renderModel); var meshes = PrepareModelForRendering(context, renderModel); foreach (var renderMesh in meshes) { if (!renderMesh.Enabled) { continue; } var worldMatrix = renderMesh.WorldMatrix; // Perform frustum culling if (cullingMode == CullingMode.Frustum) { // Always render meshes with unspecified bounds // TODO: This should not be necessary. Add proper bounding boxes to gizmos etc. var boundingBox = renderMesh.Mesh.BoundingBox; if (boundingBox.Extent != Vector3.Zero) { // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Compute transformed AABB (by world) var boundingBoxExt = new BoundingBoxExt(boundingBox); boundingBoxExt.Transform(worldMatrix); if (!frustum.Contains(ref boundingBoxExt)) { continue; } } } // 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(worldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref viewProjectionMatrix, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; // TODO: Should this be set somewhere else? var rasterizerState = cameraRenderMode != null?cameraRenderMode.GetDefaultRasterizerState(renderMesh.RenderModel.IsGeometryInverted) : null; renderMesh.RasterizerState = RasterizerState ?? rasterizerState; renderMesh.UpdateMaterial(); var list = renderMesh.HasTransparency ? transparentList : opaqueList; list.Add(new RenderItem(this, renderMesh, projectedZ)); } } }
public override void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { CurrentLights.Clear(); var lightRange = LightRanges[viewIndex]; for (int i = lightRange.Start; i < lightRange.End; ++i) { CurrentLights.Add(Lights[i]); } base.ApplyDrawParameters(context, viewIndex, parameters, ref boundingBox); // TODO: Since we cull per object, we could maintain a higher number of allowed light than the shader support (i.e. 4 lights active per object even though the scene has many more of them) // TODO: Octree structure to select best lights quicker var boundingBox2 = (BoundingBox)boundingBox; foreach (var lightEntry in CurrentLights) { var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var pointLight = (LightPoint)light.Type; lightsData.Add(new PointLightData { PositionWS = light.Position, InvSquareRadius = pointLight.InvSquareRadius, Color = light.Color, }); // Did we reach max number of simultaneous lights? // TODO: Still collect everything but sort by importance and remove the rest? if (lightsData.Count >= LightCurrentCount) { break; } } } parameters.Set(countKey, lightsData.Count); parameters.Set(lightsKey, lightsData.Count, ref lightsData.Items[0]); lightsData.Clear(); }
public override void ApplyDrawParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters, ref BoundingBoxExt boundingBox) { CurrentLights.Clear(); var lightRange = LightRanges[viewIndex]; for (int i = lightRange.Start; i < lightRange.End; ++i) { CurrentLights.Add(Lights[i]); } base.ApplyDrawParameters(context, viewIndex, parameters, ref boundingBox); // TODO: Octree structure to select best lights quicker var boundingBox2 = (BoundingBox)boundingBox; foreach (var lightEntry in CurrentLights) { var light = lightEntry.Light; if (light.BoundingBox.Intersects(ref boundingBox2)) { var spotLight = (LightSpot)light.Type; lightsData.Add(new SpotLightData { PositionWS = light.Position, DirectionWS = light.Direction, AngleOffsetAndInvSquareRadius = new Vector3(spotLight.LightAngleScale, spotLight.LightAngleOffset, spotLight.InvSquareRange), Color = light.Color, }); // Did we reach max number of simultaneous lights? // TODO: Still collect everything but sort by importance and remove the rest? if (lightsData.Count >= LightCurrentCount) { break; } } } parameters.Set(countKey, lightsData.Count); parameters.Set(lightsKey, lightsData.Count, ref lightsData.Items[0]); lightsData.Clear(); }
public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct<LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { for (int lightIndex = 0; lightIndex < currentLights.Count; ++lightIndex) { var lightEntry = currentLights[lightIndex]; var singleLightData = (LightSpotShadowMapShaderData)lightEntry.ShadowMapTexture.ShaderData; worldToShadowCascadeUV[lightIndex] = singleLightData.WorldToShadowCascadeUV; depthBiases[lightIndex] = singleLightData.DepthBias; offsetScales[lightIndex] = singleLightData.OffsetScale; // TODO: should be setup just once at creation time if (lightIndex == 0) { shadowMapTexture = singleLightData.Texture; if (shadowMapTexture != null) { shadowMapTextureSize = new Vector2(shadowMapTexture.Width, shadowMapTexture.Height); shadowMapTextureTexelSize = 1.0f / shadowMapTextureSize; } } } parameters.Set(shadowMapTextureKey, shadowMapTexture); parameters.Set(shadowMapTextureSizeKey, shadowMapTextureSize); parameters.Set(shadowMapTextureTexelSizeKey, shadowMapTextureTexelSize); parameters.Set(worldToShadowCascadeUVsKey, worldToShadowCascadeUV); parameters.Set(depthBiasesKey, depthBiases); parameters.Set(offsetScalesKey, offsetScales); }
public override void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBox2 = (BoundingBox)boundingBox; bool shadowMapCreated = false; int lightIndex = 0; lock (locker) { for (int i = 0; i < currentLights.Count; ++i) { var lightEntry = currentLights[i]; if (lightEntry.Light.BoundingBox.Intersects(ref boundingBox2)) { var shaderData = (ShaderData)lightEntry.ShadowMapTexture.ShaderData; offsets[lightIndex] = shaderData.Offset; backfaceOffsets[lightIndex] = shaderData.BackfaceOffset; faceSizes[lightIndex] = shaderData.FaceSize; depthParameters[lightIndex] = shaderData.DepthParameters; depthBiases[lightIndex] = shaderData.DepthBias; viewMatrices[lightIndex] = shaderData.View; lightIndex++; // TODO: should be setup just once at creation time if (!shadowMapCreated) { shadowMapTexture = shaderData.Texture; if (shadowMapTexture != null) { shadowMapTextureSize = new Vector2(shadowMapTexture.Width, shadowMapTexture.Height); shadowMapTextureTexelSize = 1.0f / shadowMapTextureSize; } shadowMapCreated = true; } } } parameters.Set(shadowMapTextureKey, shadowMapTexture); parameters.Set(shadowMapTextureSizeKey, shadowMapTextureSize); parameters.Set(shadowMapTextureTexelSizeKey, shadowMapTextureTexelSize); parameters.Set(viewKey, viewMatrices); parameters.Set(offsetsKey, offsets); parameters.Set(backfaceOffsetsKey, backfaceOffsets); parameters.Set(faceSizesKey, faceSizes); parameters.Set(depthParametersKey, depthParameters); parameters.Set(depthBiasesKey, depthBiases); } }
private static void CalculateMinMaxDistance(ref Plane plane, ref BoundingBoxExt boundingBox, ref float minDistance, ref float maxDistance) { var nearCorner = boundingBox.Minimum; var farCorner = boundingBox.Maximum; if (plane.Normal.X < 0) Utilities.Swap(ref nearCorner.X, ref farCorner.X); if (plane.Normal.Y < 0) Utilities.Swap(ref nearCorner.Y, ref farCorner.Y); if (plane.Normal.Z < 0) Utilities.Swap(ref nearCorner.Z, ref farCorner.Z); float oldDistance; // Interlocked exchange if lower var distance = CollisionHelper.DistancePlanePoint(ref plane, ref nearCorner); while ((oldDistance = minDistance) > distance && Interlocked.CompareExchange(ref minDistance, distance, oldDistance) != oldDistance) { } // Interlocked exchange if greater distance = CollisionHelper.DistancePlanePoint(ref plane, ref farCorner); while ((oldDistance = maxDistance) < distance && Interlocked.CompareExchange(ref maxDistance, distance, oldDistance) != oldDistance) { } }