/// <summary> /// Adds a new <see cref="TerrainRendererMesh"/>. /// </summary> /// <param name="mesh">The terrain mesh.</param> /// <remarks> /// <para> /// The <see cref="TerrainRenderer"/> takes ownership of the <see cref="TerrainRendererMesh"/> /// instance. When the <see cref="TerrainRenderer"/> is disposed of, all internally stored /// <see cref="TerrainRendererMesh"/>es are disposed of. /// </para> /// <para> /// If the terrain renderer already has a mesh with the same settings /// (<see cref="TerrainRendererMesh.NumberOfLevels"/> and /// <see cref="TerrainRendererMesh.CellsPerLevel"/>), the old mesh is disposed of and replaced. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public void SetMesh(TerrainRendererMesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } if (mesh.IsDisposed) { throw new ArgumentException("Mesh is disposed.", "mesh"); } var key = new Pair <int, int>(mesh.NumberOfLevels, mesh.CellsPerLevel); TerrainRendererMesh oldMesh; if (_meshes.TryGetValue(key, out oldMesh) && mesh != oldMesh) { oldMesh.Dispose(); } _meshes[key] = mesh; }
public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { ThrowIfDisposed(); if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } context.ThrowIfCameraMissing(); context.ThrowIfRenderPassMissing(); var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; var cameraNode = context.CameraNode; // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; cameraNode.LastFrame = frame; var savedRenderState = new RenderStateSnapshot(graphicsDevice); for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i] as TerrainNode; if (node == null) { continue; } // Node is visible in current frame. node.LastFrame = frame; if (!node.MaterialInstance.Contains(context.RenderPass)) { continue; } TerrainRendererMesh mesh; var meshKey = new Pair <int, int>(node.BaseClipmap.NumberOfLevels, node.BaseClipmap.CellsPerLevel); bool meshExists = _meshes.TryGetValue(meshKey, out mesh); if (!meshExists || mesh.IsDisposed) { // Warning: This may take a few seconds! mesh = new TerrainRendererMesh(graphicsDevice, node.BaseClipmap.NumberOfLevels, node.BaseClipmap.CellsPerLevel); _meshes[meshKey] = mesh; } // Get the EffectBindings and the Effect for the current render pass. EffectBinding materialInstanceBinding = node.MaterialInstance[context.RenderPass]; EffectBinding materialBinding = node.Material[context.RenderPass]; Effect effect = materialBinding.Effect; // Add this info to the render context. This info will be needed by parameter bindings. context.SceneNode = node; context.MaterialInstanceBinding = materialInstanceBinding; context.MaterialBinding = materialBinding; // Update and apply global effect parameter bindings - these bindings set the // effect parameter values for "global" parameters. For example, if an effect uses // a "ViewProjection" parameter, then a binding will compute this matrix from the // current CameraInstance in the render context and update the effect parameter. foreach (var binding in effect.GetParameterBindings()) { if (binding.Description.Hint == EffectParameterHint.Global) { binding.Update(context); binding.Apply(context); } } // Update and apply material bindings - these are usually constant parameter values, // like textures or colors, that are defined in the fbx file or material description // (XML files). //SetHoleParameter(materialBinding, context); foreach (var binding in materialBinding.ParameterBindings) { binding.Update(context); binding.Apply(context); } // Update and apply local, per-instance, and per-pass bindings - these are bindings // for parameters, like the "World" matrix or lighting parameters. foreach (var binding in materialInstanceBinding.ParameterBindings) { if (binding.Description.Hint != EffectParameterHint.PerPass) { binding.Update(context); binding.Apply(context); } } // Select and apply technique. var techniqueBinding = materialInstanceBinding.TechniqueBinding; techniqueBinding.Update(context); var technique = techniqueBinding.GetTechnique(effect, context); effect.CurrentTechnique = technique; var passBinding = techniqueBinding.GetPassBinding(technique, context); foreach (var pass in passBinding) { // Update and apply per-pass bindings. foreach (var binding in materialInstanceBinding.ParameterBindings) { if (binding.Description.Hint == EffectParameterHint.PerPass) { binding.Update(context); binding.Apply(context); } } pass.Apply(); // The WireFrame pass is only rendered if DrawWireFrame is set. if ((!string.Equals(pass.Name, "WireFrame", StringComparison.OrdinalIgnoreCase) || DrawWireFrame) && !string.Equals(pass.Name, "Restore", StringComparison.OrdinalIgnoreCase)) { mesh.Submesh.Draw(); } } // Reset texture effect parameter: // This seems to be necessary because the vertex textures are not automatically // removed from the texture stage which causes exceptions later. ResetParameter(effect, "TerrainBaseClipmap0"); ResetParameter(effect, "TerrainBaseClipmap1"); ResetParameter(effect, "TerrainBaseClipmap2"); ResetParameter(effect, "TerrainBaseClipmap3"); foreach (var pass in passBinding) { pass.Apply(); break; } } context.SceneNode = null; context.MaterialBinding = null; context.MaterialInstanceBinding = null; savedRenderState.Restore(); }
/// <summary> /// Adds a new <see cref="TerrainRendererMesh"/>. /// </summary> /// <param name="mesh">The terrain mesh.</param> /// <remarks> /// <para> /// The <see cref="TerrainRenderer"/> takes ownership of the <see cref="TerrainRendererMesh"/> /// instance. When the <see cref="TerrainRenderer"/> is disposed of, all internally stored /// <see cref="TerrainRendererMesh"/>es are disposed of. /// </para> /// <para> /// If the terrain renderer already has a mesh with the same settings /// (<see cref="TerrainRendererMesh.NumberOfLevels"/> and /// <see cref="TerrainRendererMesh.CellsPerLevel"/>), the old mesh is disposed of and replaced. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public void SetMesh(TerrainRendererMesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); if (mesh.IsDisposed) throw new ArgumentException("Mesh is disposed.", "mesh"); var key = new Pair<int, int>(mesh.NumberOfLevels, mesh.CellsPerLevel); TerrainRendererMesh oldMesh; if (_meshes.TryGetValue(key, out oldMesh) && mesh != oldMesh) oldMesh.Dispose(); _meshes[key] = mesh; }
public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order) { ThrowIfDisposed(); if (nodes == null) throw new ArgumentNullException("nodes"); if (context == null) throw new ArgumentNullException("context"); int numberOfNodes = nodes.Count; if (numberOfNodes == 0) return; context.ThrowIfCameraMissing(); context.ThrowIfRenderPassMissing(); var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; var cameraNode = context.CameraNode; // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; cameraNode.LastFrame = frame; var savedRenderState = new RenderStateSnapshot(graphicsDevice); for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i] as TerrainNode; if (node == null) continue; // Node is visible in current frame. node.LastFrame = frame; if (!node.MaterialInstance.Contains(context.RenderPass)) continue; TerrainRendererMesh mesh; var meshKey = new Pair<int, int>(node.BaseClipmap.NumberOfLevels, node.BaseClipmap.CellsPerLevel); bool meshExists = _meshes.TryGetValue(meshKey, out mesh); if (!meshExists || mesh.IsDisposed) { // Warning: This may take a few seconds! mesh = new TerrainRendererMesh(graphicsDevice, node.BaseClipmap.NumberOfLevels, node.BaseClipmap.CellsPerLevel); _meshes[meshKey] = mesh; } // Get the EffectBindings and the Effect for the current render pass. EffectBinding materialInstanceBinding = node.MaterialInstance[context.RenderPass]; EffectBinding materialBinding = node.Material[context.RenderPass]; Effect effect = materialBinding.Effect; // Add this info to the render context. This info will be needed by parameter bindings. context.SceneNode = node; context.MaterialInstanceBinding = materialInstanceBinding; context.MaterialBinding = materialBinding; // Update and apply global effect parameter bindings - these bindings set the // effect parameter values for "global" parameters. For example, if an effect uses // a "ViewProjection" parameter, then a binding will compute this matrix from the // current CameraInstance in the render context and update the effect parameter. foreach (var binding in effect.GetParameterBindings()) { if (binding.Description.Hint == EffectParameterHint.Global) { binding.Update(context); binding.Apply(context); } } // Update and apply material bindings - these are usually constant parameter values, // like textures or colors, that are defined in the fbx file or material description // (XML files). //SetHoleParameter(materialBinding, context); foreach (var binding in materialBinding.ParameterBindings) { binding.Update(context); binding.Apply(context); } // Update and apply local, per-instance, and per-pass bindings - these are bindings // for parameters, like the "World" matrix or lighting parameters. foreach (var binding in materialInstanceBinding.ParameterBindings) { if (binding.Description.Hint != EffectParameterHint.PerPass) { binding.Update(context); binding.Apply(context); } } // Select and apply technique. var techniqueBinding = materialInstanceBinding.TechniqueBinding; techniqueBinding.Update(context); var technique = techniqueBinding.GetTechnique(effect, context); effect.CurrentTechnique = technique; var passBinding = techniqueBinding.GetPassBinding(technique, context); foreach (var pass in passBinding) { // Update and apply per-pass bindings. foreach (var binding in materialInstanceBinding.ParameterBindings) { if (binding.Description.Hint == EffectParameterHint.PerPass) { binding.Update(context); binding.Apply(context); } } pass.Apply(); // The WireFrame pass is only rendered if DrawWireFrame is set. if ((!string.Equals(pass.Name, "WireFrame", StringComparison.OrdinalIgnoreCase) || DrawWireFrame) && !string.Equals(pass.Name, "Restore", StringComparison.OrdinalIgnoreCase)) { mesh.Submesh.Draw(); } } // Reset texture effect parameter: // This seems to be necessary because the vertex textures are not automatically // removed from the texture stage which causes exceptions later. ResetParameter(effect, "TerrainBaseClipmap0"); ResetParameter(effect, "TerrainBaseClipmap1"); ResetParameter(effect, "TerrainBaseClipmap2"); ResetParameter(effect, "TerrainBaseClipmap3"); foreach (var pass in passBinding) { pass.Apply(); break; } } context.SceneNode = null; context.MaterialBinding = null; context.MaterialInstanceBinding = null; savedRenderState.Restore(); }