AB4D Forum

Full Version: Fit
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
i've a 3d scene,she was created dynamically, and I would scale automatically the zoom (fit to the panel)
I center my scene with CenterPosition
how I can do it
Thank you
jean louis
Because this is quite a common use case I have decided that I will prepare a new sample that will demonstrate how to do this. It will be part of the samples that will come with the next version of Ab3d.PowerToys.

Until the release you can use the code bellow - create a new SceneEditor.xaml file in Ab3d.PowerToys samples under UseCases folder and add the following content to the files:

SceneEditor.xaml:
Code:
<Page x:Class="Ab3d.PowerToys.Samples.UseCases.SceneEditor"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:cameras="clr-namespace:Ab3d.Cameras;assembly=Ab3d.PowerToys"
      xmlns:ab3d="clr-namespace:Ab3d.Controls;assembly=Ab3d.PowerToys"  
      xmlns:visuals="clr-namespace:Ab3d.Visuals;assembly=Ab3d.PowerToys"        
      mc:Ignorable="d"
      d:DesignHeight="550" d:DesignWidth="800"
    Title="SceneEditor">
    <Page.Resources>
        <DiffuseMaterial x:Key="ObjectsMaterial" Brush="#247589"/>
    </Page.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <Border Name="ViewportBorder" Grid.Column="0" BorderBrush="Black" BorderThickness="2" Margin="5" CornerRadius="5">
            <Border.Background>
                <LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
                    <GradientStop Offset="0" Color="#033C62"/>
                    <GradientStop Offset="1" Color="#01131F"/>
                </LinearGradientBrush>
            </Border.Background>
            <Viewport3D Name="MainViewport">
                <ModelVisual3D x:Name="LinesVisual" />
                <visuals:WireGridVisual3D CenterPosition="0 0 0" Size="100 100" WidthCellsCount="10" HeightCellsCount="10" LineColor="#555555" LineThickness="2"/>

                <visuals:BoxVisual3D CenterPosition="30 10 30" Size="20 20 20" Material="{StaticResource ObjectsMaterial}"/>
                <visuals:BoxVisual3D CenterPosition="-30 10 30" Size="20 20 20" Material="{StaticResource ObjectsMaterial}"/>

                <visuals:BoxVisual3D CenterPosition="-130 10 40" Size="20 20 20" Material="{StaticResource ObjectsMaterial}"/>
                <visuals:BoxVisual3D CenterPosition="60 -20 -50" Size="20 20 20" Material="{StaticResource ObjectsMaterial}"/>

                <!--<visuals:BoxVisual3D CenterPosition="20 50 -20" Size="20 100 20" Material="{StaticResource ObjectsMaterial}"/>-->
            </Viewport3D>
        </Border>
        
        <cameras:TargetPositionCamera Name="Camera1" TargetPosition="0 0 0" Heading="20" Attitude="-30" Bank="0" Distance="400" ShowCameraLight="Always" TargetViewport3D="{Binding ElementName=MainViewport}"/>
        <ab3d:CameraControlPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="5" Width="225" Height="75" ShowMoveButtons="True" TargetCamera="{Binding ElementName=Camera1}"/>
        <ab3d:MouseCameraController Name="MouseCameraController1" UsedMouseButton="Left" EventsSourceElement="{Binding ElementName=ViewportBorder}" TargetCamera="{Binding ElementName=Camera1}"/>

        <StackPanel Grid.Column="1" Orientation="Vertical" Margin="10">
            <CheckBox Name="CenterCameraCheckBox" IsChecked="True" Content="Center camera" />
            <Button Name="ResetCameraCenterButton" Content="Reset camera center" ToolTip="Sets camera TargetPosition to (0,0,0)" Click="ResetCameraCenterButton_Click" />
            
            <Button Name="FitView_DistanceButton" Content="Fit to view with Distance" Click="FitView_DistanceButton_Click"  Margin="0 20 0 5" />
            <TextBlock Name="DistanceTextBlock" Margin="0 0 0 5" />
        </StackPanel>
    </Grid>
</Page>

SceneEditor.xaml.cs:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;

namespace Ab3d.PowerToys.Samples.UseCases
{
    // TODO: Add support for Orthographic camera in fit to window

    /// <summary>
    /// Interaction logic for SceneEditor.xaml
    /// </summary>
    public partial class SceneEditor : Page
    {
        public SceneEditor()
        {
            InitializeComponent();

            Camera1.CameraChanged += new Ab3d.Cameras.BaseCamera.CameraChangedRoutedEventHandler(Camera1_CameraChanged);

            this.Loaded += new RoutedEventHandler(SceneEditor_Loaded);
        }

        void Camera1_CameraChanged(object sender, Ab3d.Common.Cameras.CameraChangedRoutedEventArgs e)
        {
            RefreshCameraDistance();
        }

        void SceneEditor_Loaded(object sender, RoutedEventArgs e)
        {
            RefreshCameraDistance();
        }

        private void FitView_DistanceButton_Click(object sender, RoutedEventArgs e)
        {
            Matrix3D viewMatrix, projectionMatrix;

            // Get view matrix
            // This will be used to transform 3d objects into view space (as seen from the camera)
            // This way the object bounds can be calculated with less error
            Camera1.GetCameraMatrixes(out viewMatrix, out projectionMatrix);

            // We only need view rotation - reset the translation values in the matrix
            viewMatrix.OffsetX = 0;
            viewMatrix.OffsetY = 0;
            viewMatrix.OffsetZ = 0;

            var viewTransform = new MatrixTransform3D(viewMatrix);


            // Now get scene bounds
            Rect3D sceneBounds3D;
            Rect3D viewBounds3D;

            GetSceneBounds(MainViewport.Children, viewTransform, out sceneBounds3D, out viewBounds3D);


            Point3D centerScenePosition = new Point3D(sceneBounds3D.X + sceneBounds3D.SizeX / 2,
                                                      sceneBounds3D.Y + sceneBounds3D.SizeY / 2,
                                                      sceneBounds3D.Z + sceneBounds3D.SizeZ / 2);



            // Because the camera's FieldOfView is horizontal field of view we need to get the required width
            // to get the required width we need to check the Viewport3D's aspect ratio

            double requiredWidth;

            double boundsAspect = viewBounds3D.SizeX / viewBounds3D.SizeY;
            double viewportAspect = MainViewport.ActualWidth / MainViewport.ActualHeight;

            if (boundsAspect >= viewportAspect)
            {
                requiredWidth = viewBounds3D.SizeX;
            }
            else
            {
                // the area available for MainViewport is higher than scene bounds - we need to adjust extend the width so all the objects will be visible
                requiredWidth = viewBounds3D.SizeX * (viewportAspect / boundsAspect);
            }


            // Now simple trigonometry
            // width / 2 = distance * tan(fov / 2)
            // distance = width / (2 * tan(fov / 2))

            double distance = requiredWidth / (2 * Math.Tan(Camera1.FieldOfView / 360.0 * Math.PI));

            // The distance here will show only the center of bounding box (objects closer to the camera may not be shown)
            // To fix that we need to increase the distance for half the Z size (depth of the objects)
            // This will show whole frontal rectangle of the bounds
            Camera1.Distance = distance + viewBounds3D.SizeZ / 2;


            if (CenterCameraCheckBox.IsChecked ?? false)
            {
                Camera1.TargetPosition = centerScenePosition;
                Camera1.Offset = new Vector3D(0, 0, 0);
            }

            Camera1.Refresh();

            RefreshCameraDistance();
        }

        private void ResetCameraCenterButton_Click(object sender, RoutedEventArgs e)
        {
            Camera1.TargetPosition = new Point3D(0, 0, 0);
            Camera1.Refresh();
        }

        private void RefreshCameraDistance()
        {
            DistanceTextBlock.Text = string.Format(System.Globalization.CultureInfo.InvariantCulture, "Distance: {0:0}", Camera1.Distance);
        }

        private static void GetSceneBounds(Visual3DCollection visuals, MatrixTransform3D viewTransform, out Rect3D sceneBounds, out Rect3D viewBounds)
        {
            Rect3D oneBounds;

            sceneBounds = Rect3D.Empty;
            viewBounds = Rect3D.Empty;


            foreach (Visual3D oneVisual in visuals)
            {
                oneBounds = VisualTreeHelper.GetContentBounds(oneVisual);

                if (!oneBounds.IsEmpty && !IsRect3DUnvalid(oneBounds))
                {
                    if (oneVisual.Transform != null && !oneVisual.Transform.Value.IsIdentity)
                        oneBounds = oneVisual.Transform.TransformBounds(oneBounds);

                    sceneBounds.Union(oneBounds);

                    Rect3D oneViewBounds = viewTransform.TransformBounds(oneBounds);
                    viewBounds.Union(oneViewBounds);
                }
            }
        }

        private static bool IsRect3DUnvalid(Rect3D rect)
        {
            return (double.IsNaN(rect.X) || double.IsNaN(rect.Y) || double.IsNaN(rect.Z) ||
                    double.IsNaN(rect.SizeX) || double.IsNaN(rect.SizeY) || double.IsNaN(rect.SizeZ));
        }
    }
}


Than open the samples.xml (in the root foloder) and add the following line:

<Sample Page="UseCases/SceneEditor.xaml" Title="Scene Editor" Description=""/>


Note that this solution is not 100% correct but is rather an approximation because it is using object bounds. The object will not be shown from Viewport3D border to border. But it will show all the objects.

If you find any better way to do it you are welcome for comments and improvements.
Thank you it's working well
it was working well for :visuals:Box Visual3D,but I would Like include the files,came from export of Viewers.3ds's. In this case it's doesn't work.

<!-- Exported with Viewer3ds and Ab3d.Reader3ds. See http://www.ab4d.com for more WPF and Silverlight tools. -->
<Viewport3D xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="MainViewport3D">
<Viewport3D.Camera>
<!-- Scene camera -->
<PerspectiveCamera FieldOfView="45" NearPlaneDistance="0.125" FarPlaneDistance="Infinity" Position="-1.63846395259819,1.69770421255941,2.84290280750182" LookDirection="0.469846310392954,-0.342020143325669,-0.813797681349374" UpDirection="0.171010071662834,0.939692620785908,-0.296198132726024"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<Model3DGroup/>
<Model3DGroup>
<Model3DGroup x:Name="__AllModelsGroup">
<GeometryModel3D x:Name="_Caisse">
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-0.379999995231628,0.509999990463257,0.509999990463257 0.419999986886978,0.509999990463257,0.310000002384186 0.419999986886978,0.509999990463257,0.509999990463257 -0.379999995231628,0.509999990463257,0.509999990463257 -0.379999995231628,0.509999990463257,0.310000002384186 0.419999986886978,0.509999990463257,0.310000002384186 -0.379999995231628,1.00999999046326,0.509999990463257 0.419999986886978,1.00999999046326,0.310000002384186 -0.379999995231628,1.00999999046326,0.310000002384186 -0.379999995231628,1.00999999046326,0.509999990463257 0.419999986886978,1.00999999046326,0.509999990463257 0.419999986886978,1.00999999046326,0.310000002384186 -0.379999995231628,0.509999990463257,0.509999990463257 0.419999986886978,1.00999999046326,0.509999990463257 -0.379999995231628,1.00999999046326,0.509999990463257 -0.379999995231628,0.509999990463257,0.509999990463257 0.419999986886978,0.509999990463257,0.509999990463257 0.419999986886978,1.00999999046326,0.509999990463257 0.419999986886978,0.509999990463257,0.509999990463257 0.419999986886978,1.00999999046326,0.310000002384186 0.419999986886978,1.00999999046326,0.509999990463257 0.419999986886978,0.509999990463257,0.509999990463257 0.419999986886978,0.509999990463257,0.310000002384186 0.419999986886978,1.00999999046326,0.310000002384186 0.419999986886978,0.509999990463257,0.310000002384186 -0.379999995231628,1.00999999046326,0.310000002384186 0.419999986886978,1.00999999046326,0.310000002384186 0.419999986886978,0.509999990463257,0.310000002384186 -0.379999995231628,0.509999990463257,0.310000002384186 -0.379999995231628,1.00999999046326,0.310000002384186 -0.379999995231628,0.509999990463257,0.310000002384186 -0.379999995231628,1.00999999046326,0.509999990463257 -0.379999995231628,1.00999999046326,0.310000002384186 -0.379999995231628,0.509999990463257,0.310000002384186 -0.379999995231628,0.509999990463257,0.509999990463257 -0.379999995231628,1.00999999046326,0.509999990463257"
TriangleIndices="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35"
TextureCoordinates="0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial Brush="#FFEFEFEF"/>
<SpecularMaterial Color="White" Brush="Black" SpecularPower="1586.9140625"/>
</MaterialGroup>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D x:Name="_Caisse_2">
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-0.5,0,0.5 0.5,0,-0.5 0.5,0,0.5 -0.5,0,0.5 -0.5,0,-0.5 0.5,0,-0.5 -0.5,1,0.5 0.5,1,-0.5 -0.5,1,-0.5 -0.5,1,0.5 0.5,1,0.5 0.5,1,-0.5 -0.5,0,0.5 0.5,1,0.5 -0.5,1,0.5 -0.5,0,0.5 0.5,0,0.5 0.5,1,0.5 0.5,0,0.5 0.5,1,-0.5 0.5,1,0.5 0.5,0,0.5 0.5,0,-0.5 0.5,1,-0.5 0.5,0,-0.5 -0.5,1,-0.5 0.5,1,-0.5 0.5,0,-0.5 -0.5,0,-0.5 -0.5,1,-0.5 -0.5,0,-0.5 -0.5,1,0.5 -0.5,1,-0.5 -0.5,0,-0.5 -0.5,0,0.5 -0.5,1,0.5"
TriangleIndices="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35"
TextureCoordinates="0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial Brush="#FF8E5E02"/>
<SpecularMaterial Color="White" Brush="#FF291D01" SpecularPower="546.875"/>
</MaterialGroup>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</Model3DGroup>
<Model3DGroup/>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<DirectionalLight Direction="0.469846310392954,-0.342020143325669,-0.813797681349374" Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
It works correctly. But you have to use the cameras from Ab3d.PowerToys.

To make the code from my previous post work with your model, just replace the content of Viewport3d with your new content (staring with <ModelVisual3D>).
(11-26-2012, 03:55 PM)abenedik Wrote: [ -> ]It works correctly. But you have to use the cameras from Ab3d.PowerToys.

To make the code from my previous post work with your model, just replace the content of Viewport3d with your new content (staring with <ModelVisual3D>).

Thank you
merci