protected override void OnUnload() { // Remove scene nodes from scene and dispose them. foreach (var node in _nodes) { if (node.Parent != null) { node.Parent.Children.Remove(node); } node.Dispose(false); } _nodes = null; }
private void CreateScene() { var random = new Random(1234567); // 3 empty scene nodes act as the root nodes for 3 different scene graphs. _originalMeshNodes = new SceneNode { Children = new SceneNodeCollection() }; _staticInstancingNodes = new SceneNode { Children = new SceneNodeCollection() }; _staticBatchingNodes = new SceneNode { Children = new SceneNodeCollection() }; // Load several model nodes. Each of these models contains only one mesh node. // Store the mesh nodes in a list. MeshNode[] meshNodePrototypes = { (MeshNode)ContentManager.Load<ModelNode>("Grass/Grass").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Parviflora/Parviflora").Children[0], (MeshNode)ContentManager.Load<ModelNode>("PalmTree/palm_tree").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Rock/rock_05").Children[0], (MeshNode)ContentManager.Load<ModelNode>("GlassBox/GlassBox").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Building/wall_concrete_1").Children[0], }; for (int meshIndex = 0; meshIndex < meshNodePrototypes.Length; meshIndex++) { var meshNode = meshNodePrototypes[meshIndex]; var mesh = meshNode.Mesh; int numberOfInstances = (meshIndex == 0) ? 5000 : 200; #else int numberOfInstances = (meshIndex == 0) ? 1000 : 50; int extent = (meshIndex < 2) ? 20 : 100; // Create a list of random scales and poses. var scales = new Vector3[numberOfInstances]; var poses = new Pose[numberOfInstances]; for (int i = 0; i < numberOfInstances; i++) { // Combine a random scale with the original scale of the mesh. scales[i] = new Vector3(random.NextFloat(0.5f, 1.2f)) * meshNode.ScaleWorld; // Combine a random pose with the original pose of the mesh. Vector3 position = new Vector3(random.NextFloat(-extent, extent), 0, random.NextFloat(-extent, extent)); Matrix orientation = Matrix.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi)); poses[i] = new Pose(position, orientation) * meshNode.PoseLocal; } // Strategy A or B: Create a MeshNode for each mesh instance. for (int i = 0; i < numberOfInstances; i++) { var clone = meshNode.Clone(); clone.ScaleLocal = scales[i]; clone.PoseLocal = poses[i]; _originalMeshNodes.Children.Add(clone); } // Strategy C: Create one MeshInstancingNode which contains all instances for one mesh. var instances = new InstanceData[numberOfInstances]; for (int i = 0; i < numberOfInstances; i++) instances[i] = new InstanceData(scales[i], poses[i], new Vector4(1)); // Create MeshInstancingNode. var instancingMeshNode = new MeshInstancingNode<InstanceData>(mesh, instances) { // It is recommended to manually set a suitable pose and shape, so that // the bounding shape contains all instances. PoseLocal = new Pose(new Vector3(0, 2, 0)), Shape = new BoxShape(2 * extent, 4, 2 * extent), }; _staticInstancingNodes.Children.Add(instancingMeshNode); // Strategy D: We merge all instances of the mesh into one huge static mesh. var mergedMesh = MeshHelper.Merge(mesh, scales, poses); _staticBatchingNodes.Children.Add(new MeshNode(mergedMesh)); } // For static batching, instead of creating one merged mesh per mesh type, // we could also merge all mesh instances into a single huge static mesh. //var mergedMesh = MeshHelper.Merge(_originalMeshNodes.Children); //_staticBatchingNodes = new MeshNode(mergedMesh); }
protected override void OnUnload() { // Remove scene nodes from scene and dispose them. foreach (var node in _nodes) { if (node.Parent != null) node.Parent.Children.Remove(node); node.Dispose(false); } _nodes = null; }
protected override void OnLoad() { _scene = _services.GetInstance<IScene>(); // Get a bounding shape for the cells. We use a box with the cell size and // make it bigger by some arbitrary values. The boxes must be bigger because // mesh instances can be placed on the cell boundary and when they are animated // tree branches can sway outside the cell bounds. var meshAabb = _mesh.BoundingShape.GetAabb(); float meshWidth = new Vector2F(meshAabb.Extent.X, meshAabb.Extent.Z).Length * 1.5f; float meshHeight = meshAabb.Extent.Y * 1.7f; var boxShape = new BoxShape(_cellSize + meshWidth, meshHeight, _cellSize + meshWidth); // Create one MeshInstancingNode per cell and add random instances. _nodes = new MeshInstancingNode<InstanceData>[_numberOfCellsX, _numberOfCellsZ]; float xOrigin = -(_numberOfCellsX * _cellSize) / 2; float zOrigin = -(_numberOfCellsZ * _cellSize) / 2; var random = new Random(_randomSeed); for (int x = 0; x < _numberOfCellsX; x++) { for (int z = 0; z < _numberOfCellsZ; z++) { var instances = new InstanceData[_numberOfInstancesPerCell]; for (int i = 0; i < instances.Length; i++) { Vector3F scale = new Vector3F(random.NextFloat(0.5f, 1.5f)); Pose pose = new Pose(new Vector3F(xOrigin + x * _cellSize + random.NextFloat(0, _cellSize), 0, zOrigin + z * _cellSize + random.NextFloat(0, _cellSize)), Matrix33F.CreateRotationY(random.NextFloat(0, 10))); Vector4F color = new Vector4F(1); instances[i] = new InstanceData(scale, pose, color); } _nodes[x, z] = new MeshInstancingNode<InstanceData>(_mesh, instances) { PoseLocal = new Pose(new Vector3F(xOrigin + (0.5f + x) * _cellSize, boxShape.WidthY / 2, zOrigin + (0.5f + z) * _cellSize)), Shape = boxShape, CastsShadows = _castsShadows, }; _scene.Children.Add(_nodes[x, z]); } } UpdateLodDistances(); // ----- Add GUI controls to the Options window. var sampleFramework = _services.GetInstance<SampleFramework>(); var optionsPanel = sampleFramework.AddOptions("Game Objects"); var panel = SampleHelper.AddGroupBox(optionsPanel, "VegetationObject " + Name); SampleHelper.AddSlider( panel, "Min distance", "F2", 0, 100, MinDistance, value => MinDistance = value); SampleHelper.AddSlider( panel, "Max distance", "F2", 0, 100, MaxDistance, value => MaxDistance = value); }
protected override void OnLoad() { _scene = _services.GetInstance <IScene>(); // Get a bounding shape for the cells. We use a box with the cell size and // make it bigger by some arbitrary values. The boxes must be bigger because // mesh instances can be placed on the cell boundary and when they are animated // tree branches can sway outside the cell bounds. var meshAabb = _mesh.BoundingShape.GetAabb(); float meshWidth = new Vector2F(meshAabb.Extent.X, meshAabb.Extent.Z).Length * 1.5f; float meshHeight = meshAabb.Extent.Y * 1.7f; var boxShape = new BoxShape(_cellSize + meshWidth, meshHeight, _cellSize + meshWidth); // Create one MeshInstancingNode per cell and add random instances. _nodes = new MeshInstancingNode <InstanceData> [_numberOfCellsX, _numberOfCellsZ]; float xOrigin = -(_numberOfCellsX * _cellSize) / 2; float zOrigin = -(_numberOfCellsZ * _cellSize) / 2; var random = new Random(_randomSeed); for (int x = 0; x < _numberOfCellsX; x++) { for (int z = 0; z < _numberOfCellsZ; z++) { var instances = new InstanceData[_numberOfInstancesPerCell]; for (int i = 0; i < instances.Length; i++) { Vector3F scale = new Vector3F(random.NextFloat(0.5f, 1.5f)); Pose pose = new Pose(new Vector3F(xOrigin + x * _cellSize + random.NextFloat(0, _cellSize), 0, zOrigin + z * _cellSize + random.NextFloat(0, _cellSize)), Matrix33F.CreateRotationY(random.NextFloat(0, 10))); Vector4F color = new Vector4F(1); instances[i] = new InstanceData(scale, pose, color); } _nodes[x, z] = new MeshInstancingNode <InstanceData>(_mesh, instances) { PoseLocal = new Pose(new Vector3F(xOrigin + (0.5f + x) * _cellSize, boxShape.WidthY / 2, zOrigin + (0.5f + z) * _cellSize)), Shape = boxShape, CastsShadows = _castsShadows, }; _scene.Children.Add(_nodes[x, z]); } } UpdateLodDistances(); // ----- Add GUI controls to the Options window. var sampleFramework = _services.GetInstance <SampleFramework>(); var optionsPanel = sampleFramework.AddOptions("Game Objects"); var panel = SampleHelper.AddGroupBox(optionsPanel, "VegetationObject " + Name); SampleHelper.AddSlider( panel, "Min distance", "F2", 0, 100, MinDistance, value => MinDistance = value); SampleHelper.AddSlider( panel, "Max distance", "F2", 0, 100, MaxDistance, value => MaxDistance = value); }
private void CreateScene() { var random = new Random(1234567); // 3 empty scene nodes act as the root nodes for 3 different scene graphs. _originalMeshNodes = new SceneNode { Children = new SceneNodeCollection() }; _staticInstancingNodes = new SceneNode { Children = new SceneNodeCollection() }; _staticBatchingNodes = new SceneNode { Children = new SceneNodeCollection() }; // Load several model nodes. Each of these models contains only one mesh node. // Store the mesh nodes in a list. MeshNode[] meshNodePrototypes = { (MeshNode)ContentManager.Load<ModelNode>("Grass/Grass").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Parviflora/Parviflora").Children[0], (MeshNode)ContentManager.Load<ModelNode>("PalmTree/palm_tree").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Rock/rock_05").Children[0], (MeshNode)ContentManager.Load<ModelNode>("GlassBox/GlassBox").Children[0], (MeshNode)ContentManager.Load<ModelNode>("Building/wall_concrete_1").Children[0], }; for (int meshIndex = 0; meshIndex < meshNodePrototypes.Length; meshIndex++) { var meshNode = meshNodePrototypes[meshIndex]; var mesh = meshNode.Mesh; #if WINDOWS int numberOfInstances = (meshIndex == 0) ? 5000 : 200; #else int numberOfInstances = (meshIndex == 0) ? 1000 : 50; #endif int extent = (meshIndex < 2) ? 20 : 100; // Create a list of random scales and poses. var scales = new Vector3F[numberOfInstances]; var poses = new Pose[numberOfInstances]; for (int i = 0; i < numberOfInstances; i++) { // Combine a random scale with the original scale of the mesh. scales[i] = new Vector3F(random.NextFloat(0.5f, 1.2f)) * meshNode.ScaleWorld; // Combine a random pose with the original pose of the mesh. Vector3F position = new Vector3F(random.NextFloat(-extent, extent), 0, random.NextFloat(-extent, extent)); Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi)); poses[i] = new Pose(position, orientation) * meshNode.PoseLocal; } // Strategy A or B: Create a MeshNode for each mesh instance. for (int i = 0; i < numberOfInstances; i++) { var clone = meshNode.Clone(); clone.ScaleLocal = scales[i]; clone.PoseLocal = poses[i]; _originalMeshNodes.Children.Add(clone); } // Strategy C: Create one MeshInstancingNode which contains all instances for one mesh. var instances = new InstanceData[numberOfInstances]; for (int i = 0; i < numberOfInstances; i++) instances[i] = new InstanceData(scales[i], poses[i], new Vector4F(1)); // Create MeshInstancingNode. var instancingMeshNode = new MeshInstancingNode<InstanceData>(mesh, instances) { // It is recommended to manually set a suitable pose and shape, so that // the bounding shape contains all instances. PoseLocal = new Pose(new Vector3F(0, 2, 0)), Shape = new BoxShape(2 * extent, 4, 2 * extent), }; _staticInstancingNodes.Children.Add(instancingMeshNode); // Strategy D: We merge all instances of the mesh into one huge static mesh. var mergedMesh = MeshHelper.Merge(mesh, scales, poses); _staticBatchingNodes.Children.Add(new MeshNode(mergedMesh)); } // For static batching, instead of creating one merged mesh per mesh type, // we could also merge all mesh instances into a single huge static mesh. //var mergedMesh = MeshHelper.Merge(_originalMeshNodes.Children); //_staticBatchingNodes = new MeshNode(mergedMesh); }