public void CreatesTestScene(int[] randomIndexes) { for (int i = 0; i < randomIndexes.Length; i++) { int index = randomIndexes[i]; var textBlockVisual3D = new TextBlockVisual3D() { Text = "TextBlockVisual3D_" + (index + 1).ToString(), Position = new Point3D(0, 0, -200 + index * 40), PositionType = PositionTypes.Center, Size = new Size(200, 50), Foreground = Brushes.Yellow, RenderBitmapSize = new Size(512, 128), // Rendering text to bitmap will run the code much faster (when rendered by WPF, then by default (RenderBitmapSize is Empty) VisualBrush is used to show the text - this gives more details when text is closer to the camera, but it is much slower and requires much more cpu time; When rendered by DXEngine, then RenderBitmapSize is set to 512x256 by default) IsBackSidedTextFlipped = true }; TargetViewport3D.Children.Add(textBlockVisual3D); } // Adding ambient light can help to prevent darkening the TextBlockVisual3D too much, // but it also reduces the shading effect and when adding too much ambient light // the objects may not appear shaded anymore. // //var ambientLight = new AmbientLight(Color.FromRgb(50, 50, 50)); //TargetViewport3D.Children.Add(ambientLight.CreateModelVisual3D()); }
private void AddMiterLimitsSample(double miterLimit, double zOffset) { var sampleModelVisual3D = new ModelVisual3D(); sampleModelVisual3D.Transform = new TranslateTransform3D(0, 0, zOffset); var positions = CreateSnakePositions(new Point3D(-100, 0, 0), 50, 20, 80); var polyLineVisual3D = new Ab3d.Visuals.PolyLineVisual3D() { Positions = positions, LineColor = Colors.White, LineThickness = 15, MiterLimit = miterLimit }; sampleModelVisual3D.Children.Add(polyLineVisual3D); var textBlockVisual3D = new TextBlockVisual3D() { Position = new Point3D(-120, 0, 0), PositionType = PositionTypes.Right, TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 0, -1), Size = new Size(200, 40), Foreground = Brushes.White, Text = string.Format("MiterLimit = {0}", miterLimit) }; sampleModelVisual3D.Children.Add(textBlockVisual3D); MainViewport.Children.Add(sampleModelVisual3D); }
private void AddAxisLines() { double zPos = -0.5; // Move axis slightly away from camera so other lines will be always on top of axis var axisRootVisual3D = new ContentVisual3D(); var xAxisLineVisual3D = new LineVisual3D() { StartPosition = new Point3D(0, 0, zPos), EndPosition = new Point3D(100, 0, zPos), LineColor = Colors.Red, EndLineCap = LineCap.ArrowAnchor }; axisRootVisual3D.Children.Add(xAxisLineVisual3D); var yAxisLineVisual3D = new LineVisual3D() { StartPosition = new Point3D(0, 0, zPos), EndPosition = new Point3D(0, 100, zPos), LineColor = Colors.Green, EndLineCap = LineCap.ArrowAnchor }; axisRootVisual3D.Children.Add(yAxisLineVisual3D); var xTextBlockVisual3D = new TextBlockVisual3D() { Text = "X", Position = new Point3D(110, 0, zPos), PositionType = PositionTypes.Center, Size = new Size(10, 10), TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 1, 0), Foreground = Brushes.Red, FontWeight = FontWeights.Bold }; axisRootVisual3D.Children.Add(xTextBlockVisual3D); var yTextBlockVisual3D = new TextBlockVisual3D() { Text = "Y", Position = new Point3D(0, 110, zPos), PositionType = PositionTypes.Center, Size = new Size(10, 10), TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 1, 0), Foreground = Brushes.Green, FontWeight = FontWeights.Bold }; axisRootVisual3D.Children.Add(yTextBlockVisual3D); MainViewport.Children.Add(axisRootVisual3D); }
private void AddTextBlockVisual3D(double xOffset, string text) { var textBlockVisual3D = new TextBlockVisual3D() { Position = new Point3D(xOffset, -5, 100), PositionType = PositionTypes.Center, UpDirection = new Vector3D(0, 0.5, -0.5), // text is shown at slight angle Size = new Size(45, 20), // set width to 60; height is automatically calculated //BorderSize = new Size(45, 20), TextPadding = new Thickness(5, 3, 5, 3), BorderBrush = Brushes.Black, BorderThickness = new Thickness(1, 1, 1, 1), Text = text, }; MainViewport.Children.Add(textBlockVisual3D); }
private void AddLinesFan(ModelVisual3D parentModelVisual3D, Point3D startPosition, double lineThickness, double linesLength, string titleText = null) { if (titleText == null) { titleText = string.Format(System.Globalization.CultureInfo.InvariantCulture, "LineThickness {0:0.0}:", lineThickness); } var textBlockVisual3D = new TextBlockVisual3D() { Position = startPosition + new Vector3D(-2, linesLength + 5, 0), PositionType = PositionTypes.Bottom | PositionTypes.Left, FontSize = 11, RenderBitmapSize = new Size(1024, 256), Text = titleText }; parentModelVisual3D.Children.Add(textBlockVisual3D); var linePositions = new Point3DCollection(); for (int a = 0; a <= 90; a += 5) { Point3D endPosition = startPosition + new Vector3D(linesLength * Math.Cos(a / 180.0 * Math.PI), linesLength * Math.Sin(a / 180.0 * Math.PI), 0); linePositions.Add(startPosition); linePositions.Add(endPosition); } var multiLineVisual3D = new MultiLineVisual3D() { Positions = linePositions, LineColor = Colors.Black, LineThickness = lineThickness }; parentModelVisual3D.Children.Add(multiLineVisual3D); }
private void CreateTestSemiTransparentObjects() { if (_disposables != null) { _disposables.Dispose(); } _disposables = new DisposeList(); SemiTransparentRootVisual3D.Children.Clear(); string texturesFolder = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\"); AddSmallForest(new Point3D(0, 20, -100), texturesFolder + @"AlphaTextures\tree0.dds"); AddSmallForest(new Point3D(-100, 20, -100), texturesFolder + "TreeTexture.png"); var wireFenceMaterial = CreateDiffuseMaterial(texturesFolder + @"AlphaTextures\WireFence.dds"); SetMaterialsDXAttributes(wireFenceMaterial); var boxVisual3D = new BoxVisual3D() { CenterPosition = new Point3D(100, 20, -100), Size = new Size3D(40, 40, 40), Material = wireFenceMaterial, BackMaterial = wireFenceMaterial }; SemiTransparentRootVisual3D.Children.Add(boxVisual3D); AddPlaneGroup(new Point3D(0, 30, 70), new Vector3D(0, 0, 20), 5, texturesFolder + "SemiTransparentFrame.png"); var textBlockVisual1 = new TextBlockVisual3D() { Text = "ABCDE\r\nFGHIJ", Position = new Point3D(-100, 20, 0), PositionType = PositionTypes.Bottom, Size = new Size(60, 40), Background = Brushes.Transparent, RenderBitmapSize = new Size(256, 128), TextPadding = new Thickness(5, 0, 5, 0), BorderBrush = Brushes.Yellow, BorderThickness = new Thickness(0, 2, 0, 2) }; SetMaterialsDXAttributes(textBlockVisual1.Material); SetMaterialsDXAttributes(textBlockVisual1.BackMaterial); SemiTransparentRootVisual3D.Children.Add(textBlockVisual1); var textBlockVisual2 = new TextBlockVisual3D() { Text = "12345\r\n67890", Position = new Point3D(100, 20, 0), PositionType = PositionTypes.Bottom, Size = new Size(60, 40), Background = Brushes.Transparent, RenderBitmapSize = new Size(256, 128), TextPadding = new Thickness(5, 0, 5, 0), BorderBrush = Brushes.Yellow, BorderThickness = new Thickness(0, 2, 0, 2) }; SetMaterialsDXAttributes(textBlockVisual2.Material); SetMaterialsDXAttributes(textBlockVisual2.BackMaterial); SemiTransparentRootVisual3D.Children.Add(textBlockVisual2); var gradientBrush = new RadialGradientBrush(Colors.White, Color.FromArgb(0, 255, 255, 255)); // Gradient from White to fully transparent var gradientMaterial = new DiffuseMaterial(gradientBrush); SetMaterialsDXAttributes(gradientMaterial); var planeVisual3D = new PlaneVisual3D() { CenterPosition = new Point3D(0, 60, 0), Size = new Size(80, 80), Normal = new Vector3D(0, 0, 1), HeightDirection = new Vector3D(0, 1, 0), Material = gradientMaterial, BackMaterial = gradientMaterial }; SemiTransparentRootVisual3D.Children.Add(planeVisual3D); AlignBillboards(); _originalObjects = SemiTransparentRootVisual3D.Children.ToList(); // if there was a saved objects order because of previous sorting or randomizing, we apply that same objects order now ApplySavedObjectsOrder(); }
private void AddModel(Model3D originalModel3D, Point3D position, PositionTypes positionType, Size3D size, bool preserveAspectRatio = true) { // Create a new Model3DGroup that will hold the originalModel3D. // This allows us to have different transformation for each originalModel3D (transformation is on Model3DGroup) var model3DGroup = new Model3DGroup(); model3DGroup.Children.Add(originalModel3D); Ab3d.Utilities.ModelUtils.PositionAndScaleModel3D(model3DGroup, position, positionType, size, preserveAspectRatio); // Add the model var modelVisual3D = new ModelVisual3D() { Content = model3DGroup }; SolidObjectsVisual3D.Children.Add(modelVisual3D); // Now add red WireCrossVisual3D at the specified position var wireCrossVisual3D = new Ab3d.Visuals.WireCrossVisual3D() { Position = position, LinesLength = 30, LineColor = Colors.Red }; SolidObjectsVisual3D.Children.Add(wireCrossVisual3D); // Now show a WireBoxVisual3D (box from 3D lines) that would represent the position, positionType and size. // To get the correct CenterPosition of the WireBoxVisual3D, // we start with creating a bounding box (Rect3D) that would be used when CenterPosition would be set to (0, 0, 0): var wireboxInitialBounds = new Point3D(-size.X * 0.5, -size.Y * 0.5, -size.Z * 0.5); // Then we use that bounding box and call GetModelTranslationVector3D method // that will tell us how much we need to move the bounding box so that it will be positioned at position and for positionType: var wireboxCenterOffset = Ab3d.Utilities.ModelUtils.GetModelTranslationVector3D(new Rect3D(wireboxInitialBounds, size), position, positionType); // Now we can use the result wireboxCenterOffset as a CenterPosition or a WireBoxVisual3D var wireBoxVisual3D = new WireBoxVisual3D() { CenterPosition = new Point3D(wireboxCenterOffset.X, wireboxCenterOffset.Y, wireboxCenterOffset.Z), Size = size, LineColor = Colors.Green, LineThickness = 1 }; SolidObjectsVisual3D.Children.Add(wireBoxVisual3D); // Finally we add TextBlockVisual3D to show position and size information for this model // Note that the TextBlockVisual3D is added to the TransparentObjectsVisual3D. // The reason for this is that TextBlockVisual3D uses semi-transparent background. // To correctly show other object through semi-transparent, the semi-transparent must be added to the scene after solid objects. var infoText = string.Format("Position: {0:0}\r\nPositionType: {1}\r\nSize: {2:0}", position, positionType, size); if (!preserveAspectRatio) { infoText += "\r\npreserveAspectRatio: false"; } var textBlockVisual3D = new TextBlockVisual3D() { Position = new Point3D(model3DGroup.Bounds.GetCenterPosition().X, -15, 55), // Show so that X center position is the same as model center position PositionType = PositionTypes.Center, TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 1, -1), // angled at 45 degrees Size = new Size(80, 0), // y size will be calculated automatically based on x size (80) and size of the text Text = infoText, BorderBrush = Brushes.DimGray, BorderThickness = new Thickness(1, 1, 1, 1), Background = new SolidColorBrush(Color.FromArgb(180, 200, 200, 200)), TextPadding = new Thickness(5, 2, 5, 2) }; TransparentObjectsVisual3D.Children.Add(textBlockVisual3D); }
private void AddTextBlockVisuals(Point3D startPosition, string text, string fontFamily = null) { Point3D position = startPosition; Vector3D charAdvancementVector = new Vector3D(30, 0, 0); foreach (var oneChar in text) { var textBlockVisual3D = new TextBlockVisual3D() { Text = oneChar.ToString(), Position = position, PositionType = PositionTypes.Center, TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 1, 0), Size = new Size(20, 40), Foreground = Brushes.LightGray, IsTwoSidedText = true, IsBackSidedTextFlipped = false }; if (!string.IsNullOrEmpty(fontFamily)) { textBlockVisual3D.FontFamily = new FontFamily(fontFamily); } MainViewport.Children.Add(textBlockVisual3D); var visualEventSource3D = new VisualEventSource3D(textBlockVisual3D); visualEventSource3D.MouseEnter += delegate(object sender, Mouse3DEventArgs e) { var hitTextBlockVisual3D = e.HitObject as TextBlockVisual3D; if (hitTextBlockVisual3D != null && hitTextBlockVisual3D.Foreground != Brushes.Red) { hitTextBlockVisual3D.Foreground = Brushes.Orange; } Mouse.OverrideCursor = Cursors.Hand; }; visualEventSource3D.MouseLeave += delegate(object sender, Mouse3DEventArgs e) { var hitTextBlockVisual3D = e.HitObject as TextBlockVisual3D; if (hitTextBlockVisual3D != null && hitTextBlockVisual3D.Foreground != Brushes.Red) { hitTextBlockVisual3D.Foreground = Brushes.LightGray; } Mouse.OverrideCursor = null; }; visualEventSource3D.MouseClick += delegate(object sender, MouseButton3DEventArgs e) { var hitTextBlockVisual3D = e.HitObject as TextBlockVisual3D; if (hitTextBlockVisual3D != null) { if (hitTextBlockVisual3D.Foreground != Brushes.Red) { hitTextBlockVisual3D.Foreground = Brushes.Red; } else { hitTextBlockVisual3D.Foreground = Brushes.Orange; } } }; _eventManager3D.RegisterEventSource3D(visualEventSource3D); position += charAdvancementVector; } }
private void CreateSimpleDemoScene() { // When using Ab3d.PowerToys and Ab3d.DXEngine, then the standard way to render text is to use TextBlockVisual3D. // It provides many options and easy positioning with Position / PositionType properties. See sample in the Ab3d.PowerToys samples project for more info. // // Here we also show how to setup the alpha-clip threshold that can be used when rendering with Ab3d.DXEngine. // This way we do not need to sort the TextBlockVisual3D by their distance to render them correctly. var textBlockVisual3D = new TextBlockVisual3D() { Text = "TextBlockVisual3D", Position = new Point3D(-190, -50, 0), PositionType = PositionTypes.BottomLeft, TextDirection = new Vector3D(1, 0, 0), UpDirection = new Vector3D(0, 1, 0), Size = new Size(80, 40), Background = Brushes.Transparent, RenderBitmapSize = new Size(256, 128), TextPadding = new Thickness(5, 0, 5, 0), BorderBrush = Brushes.Yellow, BorderThickness = new Thickness(0, 2, 0, 2), IsBackSidedTextFlipped = true }; // Because TextBlockVisual3D usually use transparent background the rules and limitations of rendering transparent objects apply when using multiple TextBlockVisual3D objects (see Alpha clipping sample for more info). // With Ab3d.DXEngine a very fast transparent objects sorting can be enabled by: // MainDXViewportView.DXScene.IsTransparencySortingEnabled // // But instead of using transparency sorting, we enable alpha-clipping (see Alpha clipping sample for more info): textBlockVisual3D.SetDXAttribute(DXAttributeType.Texture_AlphaClipThreshold, 0.15f); MainViewport.Children.Add(textBlockVisual3D); // It is also possible to show 3D text with using TextVisual3D. // This objects renders text with using 3D lines. // Its disadvantage is that it supports only an old style font that was used for plotters and cannot render all the characters. // TextBlockVisual3D renders the text with first rendering the text with the specified border to a texture. // This this texture is rendered into a plane 3D object. // This works very well when there are not a lot of texts. // But if many texts need to be rendered, then the it takes long to render all texts to textures and also they take a lot of memory. // // In this case the InstancedTextNode can be used because it only rendered individual characters and then reuses // the textures with rendered characters to show the texts. To make rendering even more efficient object instancing is used. // // // The first set in using the InstancedTextNode is to create its instance. // // There we define the FontFamily and FontWeight that will be the same for all added texts. // // We also define the size of the texture that will be used to render the characters. // It is recommended to use size that is power of 2 - for example 64, 128, 256, etc. // By default the fontBitmapSize is set to 128 that rendered characters to 128 x 128 texture. // // We can also set the useDynamicInstanceBuffer to true. This would create dynamic instance buffer. // This is recommended when the text data is changed very ofter (color, position or visibility is changed in each frame or similar). // Here we change data only occasionally, so preserve the useDynamicInstanceBuffer in its default value (this is better for GPU access to the buffer) _instancedTextNode = new InstancedTextNode(fontFamily: new FontFamily("Arial"), fontWeight: FontWeights.Normal, fontBitmapSize: 128, useDynamicInstanceBuffer: false); // Than we can call AddText to add individual text to the InstancedTextNode. // Note that to be able to show text from the back side, we need to set hasBackSide to true (this rendered twice as many objects). // AddText method returns an instance of InstancedText object that can be used to change the color, position, show or hide text. _instancedText = _instancedTextNode.AddText("Ab3d.DXEngine", Colors.Orange, new Point3D(-190, 0, 0), size: 25, hasBackSide: true); // To change direction and orientation of text, we can call the SetTextDirection method. _instancedTextNode.SetTextDirection(textDirection: new Vector3D(1, 0, 0), upDirection: new Vector3D(0, 0, -1)); // All characters from font are supported. Also new line is supported. _instancedTextNode.AddText("All chars:\n@üßščžç☯", Colors.Black, new Point3D(-100, 1, 50), size: 30, hasBackSide: true); _instancedTextNode.SetTextDirection(textDirection: new Vector3D(1, 0, 0), upDirection: new Vector3D(0, 1, 0)); _instancedTextNode.AddText("Right->", Colors.Red, new Point3D(0, 30, 0), size: 30, hasBackSide: true); _instancedTextNode.SetTextDirection(textDirection: new Vector3D(0, 0, -1), upDirection: new Vector3D(0, 1, 0)); _instancedTextNode.AddText("Forward->", Colors.Blue, new Point3D(0, 30, 0), size: 30, hasBackSide: true); _instancedTextNode.SetTextDirection(textDirection: new Vector3D(0, 1, 0), upDirection: new Vector3D(-1, 0, 0)); _instancedTextNode.AddText("UP->", Colors.Green, new Point3D(0, 70, 0), size: 30, hasBackSide: true); // Sample on how to create text that is flipped on the back side (so it can be correctly read from the back side) var startPosition = new Point3D(10, 0, 0); _instancedTextNode.SetTextDirection(textDirection: new Vector3D(1, 0, 0), upDirection: new Vector3D(0, 1, 0)); var flippedFrontFaceInstancedText = _instancedTextNode.AddText("FlippedBackSideText", Colors.Green, startPosition, size: 25, hasBackSide: false); // Based on size of previous text (instancedText.TextBounds), we can calculate the start position for the flipped back face text: var backFaceStartPosition = new Point3D(startPosition.X + flippedFrontFaceInstancedText.TextBounds.SizeX, startPosition.Y, startPosition.Z); _instancedTextNode.SetTextDirection(textDirection: new Vector3D(-1, 0, 0), upDirection: new Vector3D(0, 1, 0)); // flip textDirection, preserve upDirection var flippedBackFaceInstancedText = _instancedTextNode.AddText(flippedFrontFaceInstancedText.Text, flippedFrontFaceInstancedText.Color, backFaceStartPosition, size: 25, hasBackSide: false); // It is also possible to call AddText that takes transformation matrix instead of position and scale. // This method does not use current text direction that is set with SetTextDirection: var transform3DGroup = new Transform3DGroup(); transform3DGroup.Children.Add(new ScaleTransform3D(20, 70, 20)); // Note that initially the font size is 1, so we need to scale it !!! transform3DGroup.Children.Add(new TranslateTransform3D(-120, -120, 0)); _instancedTextNode.AddText("Custom transform", Colors.Gray, transform3DGroup.Value, hasBackSide: true); // If we want to immediately create character textures, we can call the InitializeResources method. // Note that MainDXViewportView.DXScene must not be null (this can be called in MainDXViewportView.DXSceneDeviceCreated or MainDXViewportView.DXSceneInitialized event handler) //_instancedTextNode.InitializeResources(MainDXViewportView.DXScene); // Show the InstancedTextNode as any other DXEngine's SceneNode: var sceneNodeVisual1 = new SceneNodeVisual3D(_instancedTextNode); MainViewport.Children.Add(sceneNodeVisual1); // To show text with other font or with other font weight, we need to create another InstancedTextNode _instancedTextNode2 = new InstancedTextNode(new FontFamily("Times New Roman"), FontWeights.Bold, fontBitmapSize: 128); _instancedTextNode2.AddText("Text with any font", Colors.Gold, new Point3D(100, -100, 0), 30, true); var sceneNodeVisual2 = new SceneNodeVisual3D(_instancedTextNode2); MainViewport.Children.Add(sceneNodeVisual2); SetupDemoSceneButtons(isDemoSceneShown: true); UpdateCharactersCountInfo(); }