public void ShowModel(Model3D model3D, bool updateCamera) { try { ContentVisual.SetCurrentValue(ModelVisual3D.ContentProperty, model3D); // NOTE: // We could show both solid model and wireframe in WireframeVisual3D (ContentWireframeVisual) with using WireframeWithOriginalSolidModel for WireframeType. // But in this sample we show solid frame is separate ModelVisual3D and therefore we show only wireframe in WireframeVisual3D. ContentWireframeVisual.BeginInit(); ContentWireframeVisual.SetCurrentValue(Ab3d.Visuals.WireframeVisual3D.ShowPolygonLinesProperty, ReadPolygonIndicesCheckBox.IsChecked ?? false); ContentWireframeVisual.SetCurrentValue(Ab3d.Visuals.WireframeVisual3D.OriginalModelProperty, model3D); ContentWireframeVisual.EndInit(); if (AddLineDepthBiasCheckBox.IsChecked ?? false) { // To specify line depth bias to the Ab3d.PowerToys line Visual3D objects, // we use SetDXAttribute extension method and use LineDepthBias as DXAttributeType // NOTE: This can be used only before the Visual3D is created by DXEngine. // If you want to change the line bias after the object has been rendered, use the SetDepthBias method (see OnAddLineDepthBiasCheckBoxCheckedChanged) // // See DXEngineVisuals/LineDepthBiasSample for more info. ContentWireframeVisual.SetDXAttribute(DXAttributeType.LineDepthBias, model3D.Bounds.GetDiagonalLength() * 0.001); } // Calculate the center of the model and its size // This will be used to position the camera if (updateCamera) { var bounds = model3D.Bounds; var modelCenter = new Point3D(bounds.X + bounds.SizeX / 2, bounds.Y + bounds.SizeY / 2, bounds.Z + bounds.SizeZ / 2); var modelSize = Math.Sqrt(bounds.SizeX * bounds.SizeX + bounds.SizeY * bounds.SizeY + bounds.SizeZ * bounds.SizeZ); Camera1.TargetPosition = modelCenter; Camera1.SetCurrentValue(Ab3d.Cameras.BaseTargetPositionCamera.DistanceProperty, modelSize * 2); } // If the read model already define some lights, then do not show the Camera's light if (ModelUtils.HasAnyLight(model3D)) { Camera1.SetCurrentValue(Ab3d.Cameras.BaseCamera.ShowCameraLightProperty, ShowCameraLightType.Never); } else { Camera1.SetCurrentValue(Ab3d.Cameras.BaseCamera.ShowCameraLightProperty, ShowCameraLightType.Always); } ShowInfoButton.SetCurrentValue(IsEnabledProperty, true); } catch { } }
private void ShowModel(Model3D model3D, bool updateCamera) { ContentVisual.Content = model3D; if (model3D == null) { return; } // NOTE: // We could show both solid model and wireframe in WireframeVisual3D (ContentWireframeVisual) with using WireframeWithOriginalSolidModel for WireframeType. // But in this sample we show solid frame is separate ModelVisual3D and therefore we show only wireframe in WireframeVisual3D. ContentWireframeVisual.BeginInit(); ContentWireframeVisual.ShowPolygonLines = ReadPolygonIndicesCheckBox.IsChecked ?? false; ContentWireframeVisual.OriginalModel = model3D; ContentWireframeVisual.EndInit(); // Calculate the center of the model and its size // This will be used to position the camera if (updateCamera) { var bounds = model3D.Bounds; var modelCenter = new Point3D(bounds.X + bounds.SizeX / 2, bounds.Y + bounds.SizeY / 2, bounds.Z + bounds.SizeZ / 2); var modelSize = Math.Sqrt(bounds.SizeX * bounds.SizeX + bounds.SizeY * bounds.SizeY + bounds.SizeZ * bounds.SizeZ); Camera1.TargetPosition = modelCenter; Camera1.Distance = modelSize * 2; } // If the read model already define some lights, then do not show the Camera's light if (ModelUtils.HasAnyLight(model3D)) { Camera1.ShowCameraLight = ShowCameraLightType.Never; } else { Camera1.ShowCameraLight = ShowCameraLightType.Always; } ShowInfoButton.IsEnabled = true; }
public void OnShowWireframeCheckBoxCheckedChanged(object sender, RoutedEventArgs e) { if (!this.IsLoaded) { return; } if (ShowWireframeCheckBox.IsChecked ?? false) { ContentWireframeVisual.SetCurrentValue(WireframeVisual3D.WireframeTypeProperty, WireframeVisual3D.WireframeTypes.Wireframe); } else { ContentWireframeVisual.SetCurrentValue(WireframeVisual3D.WireframeTypeProperty, WireframeVisual3D.WireframeTypes.None); } }
private void LoadObj(string fileName) { if (_isLoading) { return; } bool isNewFile = (_fileName != fileName); _fileName = fileName; _wpf3DModel = null; // Set ReadPolygonIndices in UI thread // When true ReaderObj will read PolygonIndices collection that can be used to show polygons instead of triangles _readerObj.ReadPolygonIndices = ReadPolygonIndicesCheckBox.IsChecked ?? false; // To read polygon indices from the read models, // iterating through GeometryModel3D objects (children of Model3DGroup) and // then converting its Geometry object into MeshGeometry3D. // The MeshGeometry3D defines the Positions, TriangleIndices, Normals and TextureCoordinates collections. // // On top of that the Ab3d.PowerToys and ReaderObj can also add the polygon indices collection. // It is set to MeshGeometry3D as a PolygonIndices dependency property (defined in Ab3d.Utilities.MeshUtils). // You can read the PolygonIndices directly with the following: // // var polygonIndices = meshGeometry3D.GetValue(Ab3d.Utilities.MeshUtils.PolygonIndicesProperty) as Int32Collection; // // You can also use the GetPolygonIndices, SetPolygonIndices and GetPolygonPositions extension methods on MeshGeometry3D, // or use GetPolygonPositions from MeshUtils. // // See also the following to get know how the polygon positions are organized: // https://www.ab4d.com/help/PowerToys/html/F_Ab3d_Utilities_MeshUtils_PolygonIndicesProperty.htm // UH: The following code is much much easier with async and await from .net 4.5 // But here we want to use only .net 4.0 // Read obj file and convert to wpf 3d objects in background thread var readObjFileTask = new Task(() => { _lastException = null; try { string texturesPath = Path.GetDirectoryName(_fileName); // Read obj file from _fileName // We read the object names into objectNames dictionary (Dictionary<string, Model3D>) // This allows us to quickly get objects from their name. // ReaderObj also set name to all objects and materials with using SetName extension method // This way you can get object name or material name with GetName method. // For example: // _wpf3DModel.GetName(); var defaultMaterial = new DiffuseMaterial(Brushes.Silver); _wpf3DModel = _readerObj.ReadModel3D(_fileName, texturesPath, defaultMaterial); if (_wpf3DModel != null) { // We need to freeze the model because it was created on another thread. // After the model is forzen, it cannot be changed any more. // if you want to change the model, than Convert method must be called on UI thread // or you must clone the read object in the UI thread _wpf3DModel.Freeze(); } // NOTE: // If we wanted more control over reading obj files, we could read them in two steps // 1) Read obj file into ObjFileData object // 2) Convert read ObjFileData object into WPF Model3D // The following code does that (uncomment it to see it in action): //var objFileData = _readerObj.ReadFile(_fileName); ////var objFileData = _readerObj.ReadStream(fileStream); //var objMeshToWpfModel3DConverter = new ObjFileToWpfModel3DConverter(); //objMeshToWpfModel3DConverter.InvertYTextureCoordinate = false; //objMeshToWpfModel3DConverter.TexturesDirectory = Path.GetDirectoryName(_fileName); //// DefaultMaterial is a Material that is used when no other material is specified. //// Actually with leaving DefaultMaterial as null, the same Silver material would be used (setting it here only to show the user what properties exist) //objMeshToWpfModel3DConverter.DefaultMaterial = new DiffuseMaterial(Brushes.Silver); //// ReuseMaterials specified if one material instance defined in obj file can be reused for all Model3D objects that use that material. //// If false than a new material instance will be created for each usage //// This is useful when user wants to change material on individual objects without applying the change to all other objects that use that material. //// Default value is true. //objMeshToWpfModel3DConverter.ReuseMaterials = true; //_wpf3DModel = _objMeshToWpfModel3DConverter.Convert(objFileData); } catch (Exception ex) { _lastException = ex; } }); // After reading the obj file and converting it to WPF 3D objects we need to show the objects or errors - this needs to be done on UI thread var showObjectTask = readObjFileTask.ContinueWith(_ => { try { if (_lastException != null) { ResultTextBlock.Text = "ERROR reading obj file:\r\n" + _lastException.Message; } else if (_wpf3DModel != null) { string objectsDescriptions = Ab3d.Utilities.Dumper.GetObjectHierarchyString(_wpf3DModel); if (_readerObj.Errors != null && _readerObj.Errors.Count > 0) { var sb = new StringBuilder(); sb.AppendLine("Obj file errors:"); foreach (var error in _readerObj.Errors) { sb.AppendLine(error); } ResultTextBlock.Text = objectsDescriptions + Environment.NewLine + Environment.NewLine + sb.ToString(); } else { ResultTextBlock.Text = objectsDescriptions; } //ResultTextBlock.Text = Ab3d.Utilities.Dumper.GetMeshInitializationCode(model); //ResultTextBlock.Text += "\r\n\n" + Ab3d.Utilities.Dumper.Dump(model); ContentVisual.Content = _wpf3DModel; // NOTE: // We could show both solid model and wireframe in WireframeVisual3D (ContentWireframeVisual) with using WireframeWithOriginalSolidModel for WireframeType. // But in this sample we show solid frame is separate ModelVisual3D and therefore we show only wireframe in WireframeVisual3D. ContentWireframeVisual.BeginInit(); ContentWireframeVisual.ShowPolygonLines = ReadPolygonIndicesCheckBox.IsChecked ?? false; ContentWireframeVisual.OriginalModel = _wpf3DModel; ContentWireframeVisual.EndInit(); // Calculate the center of the model and its size // This will be used to position the camera if (isNewFile) // If we just reloaded the previous file, we preserve the current camera TargetPosition and Distance { var bounds = _wpf3DModel.Bounds; var modelCenter = new Point3D(bounds.X + bounds.SizeX / 2, bounds.Y + bounds.SizeY / 2, bounds.Z + bounds.SizeZ / 2); var modelSize = Math.Sqrt(bounds.SizeX * bounds.SizeX + bounds.SizeY * bounds.SizeY + bounds.SizeZ * bounds.SizeZ); Camera1.TargetPosition = modelCenter; Camera1.Distance = modelSize * 1.5; } // If the read model already define some lights, then do not show the Camera's light if (ModelUtils.HasAnyLight(_wpf3DModel)) { Camera1.ShowCameraLight = ShowCameraLightType.Never; } else { Camera1.ShowCameraLight = ShowCameraLightType.Always; } } _isLoading = false; } finally { Mouse.OverrideCursor = null; } }, TaskScheduler.FromCurrentSynchronizationContext()); // Run on UI thread _isLoading = true; Mouse.OverrideCursor = Cursors.Wait; // Start tasks readObjFileTask.Start(); }