private void SetRandomColorAndAlpha(ModelNode model) { var randomColor3F = RandomHelper.Random.NextVector3F(0, 4); // Desaturate random color to avoid eye cancer. ;-) float luminance = Vector3F.Dot(randomColor3F, GraphicsHelper.LuminanceWeights); var randomColor = (Vector3)InterpolationHelper.Lerp(new Vector3F(luminance), randomColor3F, 0.5f); var randomAlpha = MathHelper.Clamp(RandomHelper.Random.NextFloat(0, 5), 0, 1); // Change the values of all effect parameters "InstanceColor" and "InstanceAlpha": foreach (MeshNode meshNode in model.GetDescendants().OfType <MeshNode>()) { foreach (MaterialInstance materialInstance in meshNode.MaterialInstances) { foreach (EffectBinding effectBinding in materialInstance.EffectBindings) { if (effectBinding.ParameterBindings.Contains("InstanceColor")) { effectBinding.Set("InstanceColor", randomColor); } if (effectBinding.ParameterBindings.Contains("InstanceAlpha")) { effectBinding.Set("InstanceAlpha", randomAlpha); } } } } }
public void PlayAnimation(string name) { if (CurrentAnimation == name) { return; } StopAnimation(); CurrentAnimation = name; // Start selected animation. var meshNode = ModelNode.GetDescendants().OfType <MeshNode>().First(); var mesh = meshNode.Mesh; var animation = mesh.Animations[name]; var loopingAnimation = new TimelineClip(animation) { Duration = TimeSpan.MaxValue, LoopBehavior = LoopBehavior.Cycle, }; _animationController = _animationService.StartAnimation(loopingAnimation, (IAnimatableProperty)meshNode.SkeletonPose); // Update view model IsPlaying flags. foreach (var animationPropertyViewModel in _animationPropertyViewModels) { if (animationPropertyViewModel.Name == CurrentAnimation) { animationPropertyViewModel.IsPlaying = true; } } }
// OnLoad() is called when the GameObject is added to the IGameObjectService. protected override void OnLoad() { var contentManager = _services.GetInstance <ContentManager>(); // A rusty barrel with multiple levels of detail (LODs). _rigidBody = new RigidBody(new CylinderShape(0.35f, 1)); _modelNode0 = contentManager.Load <ModelNode>("Barrel/Barrel").Clone(); SampleHelper.EnablePerPixelLighting(_modelNode0); // Mark the LOD nodes with UserFlags = 1. _modelNode0.GetDescendants() .OfType <LodGroupNode>() .SelectMany(lodGroupNode => lodGroupNode.Levels) .Select(level => level.Node) .ForEach(node => { node.UserFlags = 1; }); // Add a second model where each LOD has a different color. _modelNode1 = contentManager.Load <ModelNode>("Barrel/Barrel_Colored").Clone(); SampleHelper.EnablePerPixelLighting(_modelNode1); // Mark the LOD nodes with UserFlags = 2. _modelNode1.GetDescendants() .OfType <LodGroupNode>() .SelectMany(lodGroupNode => lodGroupNode.Levels) .Select(level => level.Node) .ForEach(node => { node.UserFlags = 2; }); // Set a random pose. var randomPosition = new Vector3F( RandomHelper.Random.NextFloat(-10, 10), RandomHelper.Random.NextFloat(2, 5), RandomHelper.Random.NextFloat(-10, 0)); _rigidBody.Pose = new Pose(randomPosition, RandomHelper.Random.NextQuaternionF()); _modelNode0.PoseWorld = _rigidBody.Pose; _modelNode1.PoseWorld = _rigidBody.Pose; // Add rigid body to physics simulation and models to scene. var simulation = _services.GetInstance <Simulation>(); simulation.RigidBodies.Add(_rigidBody); var scene = _services.GetInstance <IScene>(); scene.Children.Add(_modelNode0); scene.Children.Add(_modelNode1); }
private void InitializeModel() { Debug.Assert(GraphicsScreens.Count != 0, "Initialize graphics screens before calling InitializeModel()."); if (Document.ModelNode != null) { ModelNode = Document.ModelNode; // The first view model can use the original model node. All other view models // use clones because we cannot put one model node into several scenes. if (Document.ViewModels[0] != this) { ModelNode = ModelNode.Clone(); } if (Document.HasAnimations) { // We want to animate all clones together. --> Make all clones use the skeleton // pose instance of the original mesh node. var originalMeshNode = Document.ModelNode.GetDescendants().OfType <MeshNode>().FirstOrDefault(); var clonedMeshNode = ModelNode.GetDescendants().OfType <MeshNode>().FirstOrDefault(); if (originalMeshNode != null && clonedMeshNode != null) { clonedMeshNode.SkeletonPose = originalMeshNode.SkeletonPose; } HasSkeleton = true; } else { HasSkeleton = false; } var scene = UseDeferredLighting ? ((DeferredGraphicsScreen)GraphicsScreens[0]).Scene : ((BasicGraphicsScreen)GraphicsScreens[0]).Scene; scene.Children.Add(ModelNode); } else if (Document.Model != null) { HasSkeleton = false; ((BasicGraphicsScreen)GraphicsScreens[0]).Model = Document.Model; } }
public void StopAnimation() { if (CurrentAnimation == null) { return; } CurrentAnimation = null; // Stop currently running animation. _animationController.Stop(); _animationController.Recycle(); // Set bind pose. var meshNode = ModelNode.GetDescendants().OfType <MeshNode>().First(); meshNode.SkeletonPose.ResetBoneTransforms(); // Update view model IsPlaying flags. foreach (var animationPropertyViewModel in _animationPropertyViewModels) { animationPropertyViewModel.IsPlaying = false; } }
private void UpdateProperties() { if (_modelPropertySource == null) { _modelPropertySource = new PropertySource(); } _currentPropertySource = _modelPropertySource; _modelPropertySource.Name = this.GetName(); _modelPropertySource.TypeName = "3D Model"; _modelPropertySource.Properties.Clear(); if (State != ModelDocumentState.Loaded) { return; } // Try to load drmdl file. string drmdlFile = Path.ChangeExtension(Uri.LocalPath, "drmdl"); if (UseDigitalRuneGraphics && File.Exists(drmdlFile)) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Model description", Value = drmdlFile, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); } int numberOfVertices = 0; int numberOfPrimitives = 0; if (UseDigitalRuneGraphics) { foreach (var mesh in ModelNode.GetDescendants().OfType <MeshNode>().Select(mn => mn.Mesh)) { foreach (var submesh in mesh.Submeshes) { numberOfVertices += submesh.VertexCount; numberOfPrimitives += submesh.PrimitiveCount; } } } else { foreach (var meshPart in Model.Meshes.SelectMany(m => m.MeshParts)) { numberOfVertices += meshPart.NumVertices; numberOfPrimitives += meshPart.PrimitiveCount; } } _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Triangles", Value = numberOfPrimitives, //Description = , PropertyType = typeof(int), DataTemplateKey = TextBlockKey, CanReset = false, IsReadOnly = true, }); _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Vertices", Value = numberOfVertices, //Description = , PropertyType = typeof(int), DataTemplateKey = TextBlockKey, CanReset = false, IsReadOnly = true, }); // Try to load drmat files. if (UseDigitalRuneGraphics) { string drmdlFolder = Path.GetDirectoryName(drmdlFile); var drmatFiles = new HashSet <string>(); var textures = new HashSet <string>(); var xDocument = XDocument.Load(drmdlFile); foreach (var submeshElement in xDocument.Descendants("Submesh")) { var materialAttribute = submeshElement.Attributes("Material").FirstOrDefault(); if (materialAttribute == null) { continue; } string drmatFile = GetAbsolutePath(drmdlFolder, materialAttribute.Value); if (!File.Exists(drmatFile)) { continue; } drmatFiles.Add(drmatFile); string drmatFolder = Path.GetDirectoryName(drmatFile); // Collect all referenced texture filenames. var drmatXDocument = XDocument.Load(drmatFile); foreach (var textureElement in drmatXDocument.Descendants("Texture")) { var fileAttribute = textureElement.Attributes("File").FirstOrDefault(); if (fileAttribute == null) { continue; } string textureFile = GetAbsolutePath(drmatFolder, fileAttribute.Value); textures.Add(textureFile); } } int i = 0; foreach (var drmatFile in drmatFiles) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "Materials", Name = Invariant($"Material {i}"), Value = drmatFile, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); i++; } i = 0; foreach (var texture in textures) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "Textures", Name = Invariant($"Texture {i}"), Value = texture, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); i++; } _animationPropertyViewModels.Clear(); if (HasAnimations) { var mesh = ModelNode.GetDescendants().OfType <MeshNode>().First().Mesh; foreach (var entry in mesh.Animations) { var animationPropertyViewModel = new AnimationPropertyViewModel(this) { Name = entry.Key, Animation = entry.Value, }; _animationPropertyViewModels.Add(animationPropertyViewModel); _modelPropertySource.Properties.Add(new CustomProperty { Category = "Animations", Name = "\"" + entry.Key + "\"", Value = animationPropertyViewModel, //Description = , PropertyType = typeof(AnimationPropertyViewModel), CanReset = false, IsReadOnly = true, }); } } } if (_documentService.ActiveDocument == this) { // This document is active and can control tool windows. if (_propertiesService != null) { _propertiesService.PropertySource = _currentPropertySource; } } }
public ConstraintVehicleObject(IServiceLocator services) { Name = "Vehicle"; _services = services; _inputService = services.GetInstance <IInputService>(); _simulation = services.GetInstance <Simulation>(); // Load models for rendering. var contentManager = services.GetInstance <ContentManager>(); _vehicleModelNode = contentManager.Load <ModelNode>("Car/Car").Clone(); _wheelModelNodes = new ModelNode[4]; _wheelModelNodes[0] = contentManager.Load <ModelNode>("Car/Wheel").Clone(); _wheelModelNodes[1] = _wheelModelNodes[0].Clone(); _wheelModelNodes[2] = _wheelModelNodes[0].Clone(); _wheelModelNodes[3] = _wheelModelNodes[0].Clone(); // Add wheels under the car model node. _vehicleModelNode.Children.Add(_wheelModelNodes[0]); _vehicleModelNode.Children.Add(_wheelModelNodes[1]); _vehicleModelNode.Children.Add(_wheelModelNodes[2]); _vehicleModelNode.Children.Add(_wheelModelNodes[3]); // ----- Create the chassis of the car. // The Vehicle needs a rigid body that represents the chassis. This can be any shape (e.g. // a simple BoxShape). In this example we will build a convex polyhedron from the car model. // 1. Extract the vertices from the car model. // The car model has ~10,000 vertices. It consists of a MeshNode for the glass // parts and a MeshNode "Car" for the chassis. var meshNode = _vehicleModelNode.GetDescendants() .OfType <MeshNode>() .First(mn => mn.Name == "Car"); var mesh = MeshHelper.ToTriangleMesh(meshNode.Mesh); // Apply the transformation of the mesh node. mesh.Transform(meshNode.PoseWorld * Matrix.CreateScale(meshNode.ScaleWorld)); // 2. (Optional) Create simplified convex hull from mesh. // We could also skip this step and directly create a convex polyhedron from the mesh using // var chassisShape = new ConvexPolyhedron(mesh.Vertices); // However, the convex polyhedron would still have 500-600 vertices. // We can reduce the number of vertices by using the GeometryHelper. // Create a convex hull for mesh with max. 64 vertices. Additional, shrink the hull by 4 cm. var convexHull = GeometryHelper.CreateConvexHull(mesh.Vertices, 64, -0.04f); // 3. Create convex polyhedron shape using the vertices of the convex hull. var chassisShape = new ConvexPolyhedron(convexHull.Vertices.Select(v => v.Position)); // (Note: Building convex hulls and convex polyhedra are time-consuming. To save loading time // we should build the shape in the XNA content pipeline. See other DigitalRune Physics // Samples.) // The mass properties of the car. We use a mass of 800 kg. var mass = MassFrame.FromShapeAndMass(chassisShape, Vector3.One, 800, 0.1f, 1); // Trick: We artificially modify the center of mass of the rigid body. Lowering the center // of mass makes the car more stable against rolling in tight curves. // We could also modify mass.Inertia for other effects. var pose = mass.Pose; pose.Position.Y -= 0.5f; // Lower the center of mass. pose.Position.Z = -0.5f; // The center should be below the driver. // (Note: The car model is not exactly centered.) mass.Pose = pose; // Material for the chassis. var material = new UniformMaterial { Restitution = 0.1f, StaticFriction = 0.2f, DynamicFriction = 0.2f }; var chassis = new RigidBody(chassisShape, mass, material) { Pose = new Pose(new Vector3(0, 2, 0)), // Start position UserData = "NoDraw", // (Remove this line to render the collision model.) }; // ----- Create the vehicle. Vehicle = new ConstraintVehicle(_simulation, chassis); // Add 4 wheels. Vehicle.Wheels.Add(new ConstraintWheel { Offset = new Vector3(-0.9f, 0.6f, -2.0f), Radius = 0.36f, SuspensionRestLength = 0.55f, MinSuspensionLength = 0.25f, Friction = 2 }); // Front left Vehicle.Wheels.Add(new ConstraintWheel { Offset = new Vector3(0.9f, 0.6f, -2.0f), Radius = 0.36f, SuspensionRestLength = 0.55f, MinSuspensionLength = 0.25f, Friction = 2 }); // Front right Vehicle.Wheels.Add(new ConstraintWheel { Offset = new Vector3(-0.9f, 0.6f, 0.98f), Radius = 0.36f, SuspensionRestLength = 0.55f, MinSuspensionLength = 0.25f, Friction = 1.8f }); // Back left Vehicle.Wheels.Add(new ConstraintWheel { Offset = new Vector3(0.9f, 0.6f, 0.98f), Radius = 0.36f, SuspensionRestLength = 0.55f, MinSuspensionLength = 0.25f, Friction = 1.8f }); // Back right // Vehicles are disabled per default. This way we can create the vehicle and the simulation // objects are only added when needed. Vehicle.Enabled = false; }
private void UpdateDebugDrawing() { if (GraphicsScreens.Count == 0) { return; } var debugRenderer = UseDeferredLighting ? ((DeferredGraphicsScreen)GraphicsScreens[0]).DebugRenderer : ((BasicGraphicsScreen)GraphicsScreens[0]).DebugRenderer; debugRenderer.Clear(); debugRenderer.DrawAxes(Pose.Identity, 1, true); // Draw status message. if (ModelNode == null && Document.Model == null) { string text = null; if (Document.State == ModelDocumentState.Loading) { text = "Loading..."; } else if (Document.State == ModelDocumentState.Error) { text = "Error. See Output window and Errors window for more information."; } if (text != null) { debugRenderer.DrawText(text, ModelCenter, new Vector2F(0.5f), Color.White, true); } return; } // Draw AABB. if (ShowBoundingShapes) { if (ModelNode != null) { foreach (var meshNode in ModelNode.GetDescendants().OfType <MeshNode>()) { debugRenderer.DrawObject(meshNode, Color.Orange, true, false); } } else if (Document.Model != null) { Matrix[] boneTransforms = new Matrix[Document.Model.Bones.Count]; Document.Model.CopyAbsoluteBoneTransformsTo(boneTransforms); foreach (var mesh in Document.Model.Meshes) { var sphere = mesh.BoundingSphere; sphere = sphere.Transform(boneTransforms[mesh.ParentBone.Index]); debugRenderer.DrawSphere(sphere.Radius, new Pose((Vector3)sphere.Center), Color.Orange, true, false); } } } // Draw skeleton. if (HasSkeleton && ShowSkeleton) { foreach (var meshNode in ModelNode.GetDescendants().OfType <MeshNode>()) { debugRenderer.DrawSkeleton(meshNode, meshNode.Aabb.Extent.Length / 20, Color.Orange, true); } } // Visualize intermediate render targets. if (Document.UseDigitalRuneGraphics && UseDeferredLighting) { ((DeferredGraphicsScreen)GraphicsScreens[0]).VisualizeIntermediateRenderTargets = ShowIntermediateRenderTargets; } // Draw selected outline items. Color highlightColor = Color.LightBlue; var selectedItems = Document.Outline?.SelectedItems; if (selectedItems?.Count > 0 && Document.CurrentAnimation == null) // DebugRenderer does not support animations. { foreach (var item in selectedItems) { // Skip if any parent is selected. var parent = item.Parent; bool skip = false; while (parent != null) { if (parent.IsSelected) { skip = true; break; } parent = parent.Parent; } if (skip) { continue; } if (item.UserData is SceneNode) { var sceneNode = (SceneNode)item.UserData; debugRenderer.DrawModel(sceneNode, highlightColor, true, true); } else if (item.UserData is Mesh) { var mesh = (Mesh)item.UserData; var sceneNode = (SceneNode)item.Parent.UserData; debugRenderer.DrawMesh(mesh, sceneNode.PoseWorld, sceneNode.ScaleWorld, highlightColor, true, true); } else if (item.UserData is Submesh) { var submesh = (Submesh)item.UserData; var sceneNode = (SceneNode)item.Parent.Parent.UserData; debugRenderer.DrawMesh(submesh, sceneNode.PoseWorld, sceneNode.ScaleWorld, highlightColor, true, true); } else if (item.UserData is Material) { var material = (Material)item.UserData; var mesh = (Mesh)item.Parent.UserData; var materialIndex = mesh.Materials.IndexOf(material); var sceneNode = (SceneNode)item.Parent.Parent.UserData; foreach (var sm in mesh.Submeshes) { if (sm.MaterialIndex == materialIndex) { debugRenderer.DrawMesh(sm, sceneNode.PoseWorld, sceneNode.ScaleWorld, highlightColor, true, true); } } } } } }
/// <summary> /// Checks the model for common mistakes and writes warnings. /// </summary> private void ValidateModelNode() { if (ModelNode == null) { return; } bool missingMesh = true; bool tooSmall = false; bool tooBig = false; bool tooManyBones = false; bool missingPositions = false; bool missingTexture = false; bool missingTextureCoordinates = false; bool missingNormals = false; bool missingTangentFrames = false; bool wrongAlpha = false; bool missingBoneIndices = false; bool missingBoneWeights = false; foreach (var meshNode in ModelNode.GetDescendants().OfType <MeshNode>()) { missingMesh = false; var size = meshNode.Aabb.Extent.Length; if (size < 0.01) { tooSmall = true; } else if (size > 100) { tooBig = true; } if (meshNode.SkeletonPose != null && meshNode.SkeletonPose.Skeleton.NumberOfBones > 72) { tooManyBones = true; } foreach (var submesh in meshNode.Mesh.Submeshes) { var vertexElements = submesh.VertexBuffer.VertexDeclaration.GetVertexElements(); if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.Position)) { missingPositions = true; } if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.TextureCoordinate)) { missingTextureCoordinates = true; } if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.Normal)) { missingNormals = true; } if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.Tangent)) { missingTangentFrames = true; } if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.Binormal)) { missingTangentFrames = true; } if (meshNode.SkeletonPose != null) { if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.BlendIndices)) { missingBoneIndices = true; } if (!vertexElements.Any(ve => ve.VertexElementUsage == VertexElementUsage.BlendWeight)) { missingBoneWeights = true; } } } foreach (var material in meshNode.Mesh.Materials) { foreach (var effectBinding in material.EffectBindings) { if (effectBinding.ParameterBindings.Contains("Diffuse")) { var alphaBinding = effectBinding.ParameterBindings["Diffuse"] as ConstParameterBinding <Vector4>; if (alphaBinding != null && alphaBinding.Value.W < 0.001f) { wrongAlpha = true; } } else if (effectBinding.ParameterBindings.Contains("DiffuseColor")) { var alphaBinding = effectBinding.ParameterBindings["DiffuseColor"] as ConstParameterBinding <Vector4>; if (alphaBinding != null && alphaBinding.Value.W < 0.001f) { wrongAlpha = true; } } else if (effectBinding.ParameterBindings.Contains("Alpha")) { var alphaBinding = effectBinding.ParameterBindings["Alpha"] as ConstParameterBinding <float>; if (alphaBinding != null && alphaBinding.Value < 0.001f) { wrongAlpha = true; } } if (effectBinding.ParameterBindings.Contains("Texture")) { var textureBinding = effectBinding.ParameterBindings["Texture"] as ConstParameterBinding <Texture>; if (textureBinding != null && textureBinding.Value == null) { missingTexture = true; } var texture2DBinding = effectBinding.ParameterBindings["Texture"] as ConstParameterBinding <Texture2D>; if (texture2DBinding != null && texture2DBinding.Value == null) { missingTexture = true; } } if (effectBinding.ParameterBindings.Contains("DiffuseTexture")) { var textureBinding = effectBinding.ParameterBindings["DiffuseTexture"] as ConstParameterBinding <Texture>; if (textureBinding != null && textureBinding.Value == null) { missingTexture = true; } var texture2DBinding = effectBinding.ParameterBindings["DiffuseTexture"] as ConstParameterBinding <Texture2D>; if (texture2DBinding != null && texture2DBinding.Value == null) { missingTexture = true; } } } } } if (missingMesh) { AddWarning("Model does not contain any meshes."); } if (tooSmall) { AddWarning("Model is very small. Scale it up in a 3D modeling tool or set a scale factor in DRMDL file!"); } else if (tooBig) { AddWarning("Model is very big. Scale it down in a 3D modeling tool or set a scale factor in DRMDL file!"); } if (tooManyBones) { AddWarning("Model uses too many bones for mesh skinning. Reduce number of bones to ≤ 72 in a 3D modeling tool!"); } if (missingPositions) { AddWarning("Missing vertex positions. Correct the model in a 3D modeling tool!"); } if (missingNormals) { AddWarning("Missing vertex normals. Correct the model in a 3D modeling tool!"); } if (missingTexture) { AddWarning("Missing diffuse texture."); } if (missingTextureCoordinates) { AddWarning("Missing texture coordinates. Add texture coordinates in a 3D modeling tool!"); } if (missingTangentFrames) { AddWarning("Missing tangent frames. Set GenerateTangentFrames in DRMDL file to true! (Optional for XNA BasicEffect. Required for advanced effects.)"); } if (missingBoneIndices || missingBoneWeights) { AddWarning("Missing blend indices or blend weights for mesh skinning. Add blend indices and blend weights in a 3D modeling tool!"); } if (wrongAlpha) { AddWarning("Alpha of mesh is 0 or almost 0."); } }
public VegetationSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; _graphicsScreen = new DeferredGraphicsScreen(Services); _graphicsScreen.DrawReticle = true; GraphicsService.Screens.Insert(0, _graphicsScreen); GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services)); Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer); var scene = _graphicsScreen.Scene; Services.Register(typeof(IScene), null, scene); // Add gravity and damping to the physics simulation. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a custom game object which controls the camera. var cameraGameObject = new CameraObject(Services); GameObjectService.Objects.Add(cameraGameObject); _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode; // Add standard game objects. GameObjectService.Objects.Add(new GrabObject(Services)); GameObjectService.Objects.Add(new DynamicSkyObject(Services, true, false, true)); GameObjectService.Objects.Add(new GroundObject(Services)); GameObjectService.Objects.Add(new ObjectCreatorObject(Services)); GameObjectService.Objects.Add(new LavaBallsObject(Services)); // Add a new game object which controls the wind velocity and "Wind" parameters in effects. GameObjectService.Objects.Add(new WindObject(Services)); // The vegetation effects use an effect parameter named "LodDistances". By default all // effect parameters are shared per "Material". However, we want to change the parameter // per instance. Therefore, the effect declares the parameter like this: // float3 LodDistances < string Hint = "PerInstance"; >; // However, MonoGame does not yet support effect annotations. Therefore, we tell the // graphics service that the "LodDistances" parameter should be stored per instance by // adding a new entry to the default effect interpreter. var defaultEffectInterpreter = GraphicsService.EffectInterpreters.OfType<DefaultEffectInterpreter>().First(); if (!defaultEffectInterpreter.ParameterDescriptions.ContainsKey("LodDistances")) defaultEffectInterpreter.ParameterDescriptions.Add( "LodDistances", (parameter, i) => new EffectParameterDescription(parameter, "LodDistances", i, EffectParameterHint.PerInstance)); // Load three different plant models. // The palm tree consists of a single mesh. It uses the *Vegetation.fx effects. ModelNode palmModelNode = ContentManager.Load<ModelNode>("Vegetation/PalmTree/palm_tree"); Mesh palmMesh = ((MeshNode)palmModelNode.Children[0]).Mesh; // The bird's nest plant consists of 2 LODs. It uses the *Vegetation.fx effects. ModelNode plantModelNode = ContentManager.Load<ModelNode>("Vegetation/BirdnestPlant/BirdnestPlant"); LodGroupNode plantLodGroupNode = plantModelNode.GetDescendants().OfType<LodGroupNode>().First().Clone(); // The grass model consists of one mesh. It uses the *Grass.fx effects. ModelNode grassModelNode = ContentManager.Load<ModelNode>("Vegetation/Grass/grass"); Mesh grassMesh = ((MeshNode)grassModelNode.Children[0]).Mesh; // Store all used meshes in a list for use in UpdateMaterialEffectParameters. _meshes.Add(palmMesh); foreach (var meshNode in plantLodGroupNode.Levels.Select(lodEntry => lodEntry.Node).OfType<MeshNode>()) _meshes.Add(meshNode.Mesh); _meshes.Add(grassMesh); // We can add individual plant instances to the scene like this: // (However, this is inefficient for large amounts of plants.) _graphicsScreen.Scene.Children.Add(new MeshNode(palmMesh) { PoseLocal = new Pose(new Vector3(-2, 0, 0)) }); plantLodGroupNode.PoseLocal = Pose.Identity; _graphicsScreen.Scene.Children.Add(plantLodGroupNode); _graphicsScreen.Scene.Children.Add(new MeshNode(grassMesh) { PoseLocal = new Pose(new Vector3(2, 0, 0)) }); int numberOfInstancesPerCell = 100; #else int numberOfInstancesPerCell = 10; // It is more efficient to group instances in batches and render them using mesh instancing. // This is handled by the VegetationObject class. GameObjectService.Objects.Add(new VegetationObject(Services, palmMesh, numberOfInstancesPerCell, 20, 10, 10, 1) { Name = "PalmTrees" }); // The bird's nest plant has 2 LODs. We create two VegetationObjects. One displays the // detailed meshes near the camera. The second displays the low-poly meshes in the distance. var plantMeshLod0 = ((MeshNode)plantLodGroupNode.Levels[0].Node).Mesh; _meshes.Add(plantMeshLod0); GameObjectService.Objects.Add(new VegetationObject(Services, plantMeshLod0, numberOfInstancesPerCell, 20, 10, 10, 2) { Name = "PlantLOD0", MaxDistance = plantLodGroupNode.Levels[1].Distance, }); var plantMeshLod1 = ((MeshNode)plantLodGroupNode.Levels[1].Node).Mesh; _meshes.Add(plantMeshLod1); GameObjectService.Objects.Add(new VegetationObject(Services, plantMeshLod1, numberOfInstancesPerCell, 20, 10, 10, 2) { Name = "PlantLOD1", MinDistance = plantLodGroupNode.Levels[1].Distance, MaxDistance = plantLodGroupNode.MaxDistance, CastsShadows = false, // No shadows in the distance. }); // Grass, lots of it... GameObjectService.Objects.Add(new VegetationObject(Services, grassMesh, numberOfInstancesPerCell * 10, 10, 20, 20, 3) { Name = "Grass", MaxDistance = 30, CastsShadows = false, }); CreateGuiControls(); }
private async Task LoadAsync(string fileName, bool recreateModelAndMaterialFiles) { Debug.Assert(_tempDirectoryHelper == null); Debug.Assert(_monoGameContent == null); Debug.Assert(ModelNode == null); Debug.Assert(Model == null); Debug.Assert(_assimpScene == null); Debug.Assert(State == ModelDocumentState.Loading); string extension = Path.GetExtension(fileName); IsXnb = string.Compare(extension, ".XNB", StringComparison.OrdinalIgnoreCase) == 0; // ----- Build XNB string directoryName; try { if (IsXnb) { // Get the folder that contains the XNB. directoryName = Path.GetDirectoryName(fileName); } else if (GameContentBuilder.IsSupportedModelFileExtension(extension)) { // Build the XNB and get the output folder. var buildResult = await Task.Run(() => BuildXnb(Editor.Services, Editor.ApplicationName, fileName, UseDigitalRuneGraphics, recreateModelAndMaterialFiles)); directoryName = buildResult.Item1; _tempDirectoryHelper = buildResult.Item2; } else { throw new EditorException(Invariant($"Unsupported 3D model file format (file extension \"{extension}\").")); } } catch (Exception) { if (IsDisposed) { // Document was closed during loading. Reset(); return; } State = ModelDocumentState.Error; UpdateProperties(); UpdateOutline(); // The GameContentBuilder logs to the output service. _outputService.Show(); throw; } if (IsDisposed) { // Document was closed during loading. Reset(); return; } Debug.Assert(directoryName != null); // ----- Load XNB try { // Get asset name for use with ContentManager. XNBs and unprocessed models // use different folder hierarchies. string assetFileName; if (IsXnb) { // Use absolute path. assetFileName = fileName; } else { // The asset is built relative to the root folder (e.g. "C:\"). The folder // hierarchy (from root to asset) is rebuilt in the temporary output folder. // Make file name relative to root. assetFileName = DRPath.GetRelativePath(Path.GetPathRoot(fileName), fileName); // Get absolute file name relative to temporary output folder. assetFileName = Path.Combine(directoryName, assetFileName); // Change extension. .fbx --> .xnb assetFileName = Path.ChangeExtension(assetFileName, "xnb"); } _monoGameContent = await Task.Run(() => _monoGameService.LoadXnb(directoryName, assetFileName, cacheResult: false)); if (_monoGameContent.Asset is ModelNode) { ModelNode = (ModelNode)_monoGameContent.Asset; UseDigitalRuneGraphics = true; HasAnimations = ModelNode.GetDescendants() .OfType <MeshNode>() .FirstOrDefault()? .Mesh? .Animations? .Count > 0; } else if (_monoGameContent.Asset is Model) { Model = (Model)_monoGameContent.Asset; UseDigitalRuneGraphics = false; HasAnimations = false; // Enable default lighting. var effects = Model.Meshes .SelectMany(m => m.Effects) .OfType <IEffectLights>(); foreach (var effect in effects) { effect.EnableDefaultLighting(); } } else { throw new EditorException("XNB does not contain ModelNode or Model."); } } catch (Exception exception) { Reset(); if (IsDisposed) { return; } State = ModelDocumentState.Error; Logger.Error(exception, "XNB could not be loaded."); // Let LoadAsync return and fail, then show message box. WindowsHelper.BeginInvokeOnUI(() => { var message = Invariant($"XNB could not be loaded:\n\n\"{exception.Message}\""); MessageBox.Show(message, Editor.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Error); }); throw; } if (IsDisposed) { // Document was closed during loading. Reset(); return; } // ----- Load Assimp scene try { if (!IsXnb) { _assimpScene = await Task.Run(() => LoadAssimp(fileName)); } } catch (Exception exception) { Logger.Warn(exception, "Assimp could not read model file."); } if (IsDisposed) { // Document was closed during loading. Reset(); return; } State = ModelDocumentState.Loaded; // ----- Validate model ValidateModelNode(); // TODO: Validate MonoGame Model. // If there are errors or warnings, show Output and Errors window. // (Drawback: This steals focus from the document.) //if (_errors.Count > 0) //{ // _outputService?.Show(); // _errorService?.Show(); //} // ----- Update outline and properties UpdateOutline(); UpdateProperties(); }
// OnLoad() is called when the GameObject is added to the IGameObjectService. protected override void OnLoad() { var contentManager = _services.GetInstance <ContentManager>(); if (_type == 1) { // A simple cube. _rigidBody = new RigidBody(new BoxShape(1, 1, 1)); _modelNode = contentManager.Load <ModelNode>("RustyCube/RustyCube").Clone(); } else if (_type == 2) { // Another simple cube. _rigidBody = new RigidBody(new BoxShape(1, 1, 1)); _modelNode = contentManager.Load <ModelNode>("MetalGrateBox/MetalGrateBox").Clone(); } else if (_type == 3) { // A TV-like box. _rigidBody = new RigidBody(new BoxShape(1, 0.6f, 0.8f)) { UserData = "TV" }; _modelNode = contentManager.Load <ModelNode>("TVBox/TVBox"); if (_modelNode.Children.OfType <LightNode>().Count() == 0) { // This is the first time the "TVBox" is loaded. // Add a projector light to the model that projects the TV screen. The // TV screen is the emissive part of the TV mesh. var meshNode = _modelNode.Children.OfType <MeshNode>().First(); var material = meshNode.Mesh.Materials.First(m => m.Name == "TestCard"); // Get texture from material. // Note: In XNA the effect parameter type is Texture. In MonoGame it is Texture2D. Texture2D texture; EffectParameterBinding parameterBinding = material["Material"].ParameterBindings["EmissiveTexture"]; if (parameterBinding is EffectParameterBinding <Texture> ) { texture = (Texture2D)((EffectParameterBinding <Texture>)parameterBinding).Value; } else { texture = ((EffectParameterBinding <Texture2D>)parameterBinding).Value; } var projection = new PerspectiveProjection(); projection.Near = 0.55f; projection.Far = 3.0f; projection.SetFieldOfView(MathHelper.ToRadians(60), 0.76f / 0.56f); var projectorLight = new ProjectorLight(texture, projection); projectorLight.Attenuation = 4; var projectorLightNode = new LightNode(projectorLight); projectorLightNode.LookAt(new Vector3F(0, 0.2f, 0), Vector3F.Zero, Vector3F.UnitZ); // Attach the projector light to the model. _modelNode.Children.Add(projectorLightNode); } _modelNode = _modelNode.Clone(); } else if (_type == 4) { // A "magic" sphere with a colored point light. _rigidBody = new RigidBody(new SphereShape(0.25f)); _modelNode = contentManager.Load <ModelNode>("MagicSphere/MagicSphere"); if (_modelNode.Children.OfType <LightNode>().Count() == 0) { // This is the first time the "MagicSphere" is loaded. // Change the size of the sphere. var meshNode = _modelNode.Children.OfType <MeshNode>().First(); meshNode.ScaleLocal = new Vector3F(0.5f); // Disable shadows. (The sphere acts as a light source.) meshNode.CastsShadows = false; // Add a point light. var pointLight = new PointLight { Color = new Vector3F(1, 1, 1), DiffuseIntensity = 4, SpecularIntensity = 4, Range = 3, Attenuation = 1, Texture = contentManager.Load <TextureCube>("MagicSphere/ColorCube"), }; var pointLightNode = new LightNode(pointLight) { // The point light uses shadow mapping to cast an omnidirectional shadow. Shadow = new CubeMapShadow { PreferredSize = 64, DepthBiasScale = 0.9f, DepthBiasOffset = -0.01f, } }; _modelNode.Children.Add(pointLightNode); } _modelNode = _modelNode.Clone(); } else if (_type == 5) { // A sphere of glass (or "bubble"). _rigidBody = new RigidBody(new SphereShape(0.3f)); _modelNode = contentManager.Load <ModelNode>("Bubble/Bubble").Clone(); _modelNode.GetDescendants().OfType <MeshNode>().First().ScaleLocal = new Vector3F(0.3f); } else if (_type == 6) { // A rusty barrel with multiple levels of detail (LODs). _rigidBody = new RigidBody(new CylinderShape(0.35f, 1)); _modelNode = contentManager.Load <ModelNode>("Barrel/Barrel").Clone(); } else { // A cube consisting of a frame and transparent sides. _rigidBody = new RigidBody(new BoxShape(1, 1, 1)); _modelNode = contentManager.Load <ModelNode>("GlassBox/GlassBox").Clone(); } SampleHelper.EnablePerPixelLighting(_modelNode); // Set a random pose. var randomPosition = new Vector3F( RandomHelper.Random.NextFloat(-10, 10), RandomHelper.Random.NextFloat(2, 5), RandomHelper.Random.NextFloat(-20, 0)); _rigidBody.Pose = new Pose(randomPosition, RandomHelper.Random.NextQuaternionF()); _modelNode.PoseWorld = _rigidBody.Pose; // Add rigid body to physics simulation and model to scene. var simulation = _services.GetInstance <Simulation>(); simulation.RigidBodies.Add(_rigidBody); var scene = _services.GetInstance <IScene>(); scene.Children.Add(_modelNode); }