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> /// Initializes a new instance of the <see cref="RenderMesh" /> class. /// </summary> /// <param name="renderModel">The render model.</param> /// <param name="mesh">The mesh data.</param> /// <exception cref="System.ArgumentNullException">mesh</exception> public RenderMesh(RenderModel renderModel, Mesh mesh) { if (renderModel == null) throw new ArgumentNullException("renderModel"); if (mesh == null) throw new ArgumentNullException("mesh"); RenderModel = renderModel; Mesh = mesh; Enabled = true; UpdateMaterial(); // A RenderMesh is inheriting values from Mesh.Parameters // We are considering that Mesh.Parameters is not updated frequently (should be almost immutable) parameters = new ParameterCollection(); if (mesh.Parameters != null) { parameters.AddSources(mesh.Parameters); } }
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) 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]; 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]); } // Upload bones renderMesh.Parameters.Set(TransformationSkinningKeys.BlendMatrixArray, boneMatrices, 0, bones.Length); } } }
private bool PrepareRenderModelForRendering(RenderContext context, RenderModel model) { var shaderKeyIdBuilder = new ObjectIdSimpleBuilder(); var parametersKeyIdBuilder = new ObjectIdSimpleBuilder(); //var idBuilder = new ObjectIdBuilder(); var modelComponent = model.ModelComponent; var group = modelComponent.Entity.Group; var modelBoundingBox = modelComponent.BoundingBox; directLightsPerModel.Clear(); directLightShaderGroupEntryKeys.Clear(); directLightShaderGroupEntryKeysNoShadows.Clear(); environmentLightsPerModel.Clear(); environmentLightShaderGroupEntryKeys.Clear(); // This loop is looking for visible lights per render model and calculate a ShaderId and ParametersId // TODO: Part of this loop could be processed outisde of the PrepareRenderModelForRendering // For example: Environment lights or directional lights are always active, so we could pregenerate part of the // id and groups outside this loop. Also considering that each light renderer has a maximum of lights // we could pre foreach (var activeRenderer in activeRenderers) { var lightRenderer = activeRenderer.LightRenderer; var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); var lightCount = lightCollection == null ? 0 : lightCollection.Count; int lightMaxCount = Math.Min(lightCount, lightRenderer.LightMaxCount); var lightRendererId = lightRenderer.LightRendererId; var allocCountForNewLightType = lightRenderer.AllocateLightMaxCount ? (byte)lightRenderer.LightMaxCount : (byte)1; var currentShaderKey = new LightForwardShaderEntryKey(); // Path for environment lights if (lightRenderer.IsEnvironmentLight) { // The loop is simpler for environment lights (single group per light, no shadow maps, no bounding box...etc) for(int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, 0, allocCountForNewLightType); unsafe { shaderKeyIdBuilder.Write(*(uint*)¤tShaderKey); } parametersKeyIdBuilder.Write(light.Id); environmentLightsPerModel.Add(new LightEntry(environmentLightShaderGroupEntryKeys.Count, 0, light, null)); environmentLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, null)); } } else { ILightShadowMapRenderer currentShadowRenderer = null; for (int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; var directLight = (IDirectLight)light.Type; // If the light does not intersects the model, we can skip it if (directLight.HasBoundingBox && !light.BoundingBox.Intersects(ref modelBoundingBox)) { continue; } LightShadowMapTexture shadowTexture = null; LightShadowType shadowType = 0; ILightShadowMapRenderer newShadowRenderer = null; if (shadowMapRenderer != null && shadowMapRenderer.LightComponentsWithShadows.TryGetValue(light, out shadowTexture)) { shadowType = shadowTexture.ShadowType; newShadowRenderer = shadowTexture.Renderer; } if (i == 0) { currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } else { if (currentShaderKey.LightRendererId == lightRendererId && currentShaderKey.ShadowType == shadowType) { if (!lightRenderer.AllocateLightMaxCount) { currentShaderKey.LightCount++; } } else { unsafe { shaderKeyIdBuilder.Write(*(uint*)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, currentShadowRenderer)); currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } } parametersKeyIdBuilder.Write(light.Id); directLightsPerModel.Add(new LightEntry(directLightShaderGroupEntryKeys.Count, directLightShaderGroupEntryKeysNoShadows.Count, light, shadowTexture)); } if (directLightsPerModel.Count > 0) { directLightShaderGroupEntryKeysNoShadows.Add(new LightForwardShaderFullEntryKey(new LightForwardShaderEntryKey(lightRendererId, 0, (byte)directLightsPerModel.Count), lightRenderer, null)); unsafe { shaderKeyIdBuilder.Write(*(uint*)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, currentShadowRenderer)); } } } // Find or create an existing shaders/parameters permutation // Build the keys for Shaders and Parameters permutations ObjectId shaderKeyId; ObjectId parametersKeyId; shaderKeyIdBuilder.ComputeHash(out shaderKeyId); parametersKeyIdBuilder.ComputeHash(out parametersKeyId); // Calculate the shader parameters just once // If we don't have already this permutation, use it LightShaderPermutationEntry newLightShaderPermutationEntry; if (!shaderEntries.TryGetValue(shaderKeyId, out newLightShaderPermutationEntry)) { newLightShaderPermutationEntry = CreateShaderPermutationEntry(); shaderEntries.Add(shaderKeyId, newLightShaderPermutationEntry); } LightParametersPermutationEntry newShaderEntryParameters; // Calculate the shader parameters just once per light combination and for this rendering pass if (!lightParameterEntries.TryGetValue(parametersKeyId, out newShaderEntryParameters)) { newShaderEntryParameters = CreateParametersPermutationEntry(newLightShaderPermutationEntry); lightParameterEntries.Add(parametersKeyId, newShaderEntryParameters); } modelToLights.Add(model, new RenderModelLights(newLightShaderPermutationEntry, newShaderEntryParameters)); return true; }
/// <summary> /// Updates previously computed world matrices to TransformationKeys.World for each <see cref="Mesh"/>. /// </summary> /// <param name="renderModel">The render model.</param> /// <param name="slot">The slot.</param> internal void UpdateToRenderModel(RenderModel renderModel, int slot) { var nodeTransformationsLocal = this.nodeTransformations; // Set World matrices in mesh parameters var meshes = renderModel.RenderMeshesList[slot]; if (meshes == null) { return; } foreach (var renderMesh in meshes) { var nodeIndex = renderMesh.Mesh.NodeIndex; var enabled = nodeTransformationsLocal[nodeIndex].RenderingEnabledRecursive; renderMesh.Enabled = enabled; if (enabled) { renderMesh.WorldMatrix = nodeTransformationsLocal[nodeIndex].WorldMatrix; renderMesh.IsGeometryInverted = nodeTransformationsLocal[nodeIndex].IsScalingNegative; } } }
private void PrepareRenderMeshes(RenderModel renderModel, List<Mesh> meshes, ref FastListStruct<RenderMesh> renderMeshes, RenderItemCollection opaqueList, RenderItemCollection transparentList) { // Add new render meshes for (int i = renderMeshes.Count; i < meshes.Count; i++) { var renderMesh = new RenderMesh(renderModel, meshes[i]); renderMeshes.Add(renderMesh); } // Create the bounding frustum locally on the stack, so that frustum.Contains is performed with boundingBox that is also on the stack var frustum = new BoundingFrustum(ref ViewProjectionMatrix); for (int i = 0; i < renderMeshes.Count; i++) { var renderMesh = renderMeshes[i]; // Update the model hierarchy var modelViewHierarchy = renderModel.ModelComponent.ModelViewHierarchy; modelViewHierarchy.UpdateRenderMesh(renderMesh); if (!renderMesh.Enabled) { continue; } // Upload skinning blend matrices BoundingBoxExt boundingBox; skinningUpdater.Update(modelViewHierarchy, renderMesh, out boundingBox); // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ // Compute transformed AABB (by world) // TODO: CullingMode should be pluggable // TODO: This should not be necessary. Add proper bounding boxes to gizmos etc. if (CullingMode == CullingMode.Frustum && boundingBox.Extent != Vector3.Zero && !frustum.Contains(ref boundingBox)) { 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(renderMesh.WorldMatrix.TranslationVector, 1.0f); Vector4 projectedPosition; Vector4.Transform(ref worldPosition, ref ViewProjectionMatrix, out projectedPosition); var projectedZ = projectedPosition.Z / projectedPosition.W; renderMesh.RasterizerState = renderMesh.IsGeometryInverted ? RasterizerStateForInvertedGeometry : RasterizerState; renderMesh.UpdateMaterial(); var list = renderMesh.HasTransparency ? transparentList : opaqueList; list.Add(new RenderItem(this, renderMesh, projectedZ)); } }