ojbImporter() - Scale, Move and Rotate
#1
Hello,

I'm a newbie when it comes XAML and I have some very basic questions about using objImporter.ReadModel3D() which I would be very thankful if someone could help me answer.

From what I understand, the objImporter.ReadModel3D() returns either a Model3DGroup or a GeometryModel3D object depending on the imported object. (I have tried importing both the teapot.obj (GeometryModel3D) and the ship_boat.obj (Model3DGroup) from the Samples and it works fine, but I'm a bit stuck about how to move forward.

var wpf3DModel = objImporter.ReadModel3D(resourceStream, GetResourceStream)

What I would like to do is to import a model I have created and display it six times (but on different locations and with different rotations). I would also like to be able to hide and show some of the "models" when needed.

So here are some very basic questions...

1) How can I change the size of the imported 3D model?
Let's say I would like to change the width to 100 and preserve the scale aspect ratio.

2) How can I move the object? And and which part of the object is used as the reference when moving the object? Is it the center of the object?

3) How can i rotate the object?

4) Is it possible to hide and show an object, or do I need to delete it and then create it again?

Many thanks,
Kalle
#2
The most easy way to scale and position the models from obj files is to use the ObjModelVisual3D - this allows you to position and scale the read model. The following screenshot from Ab3d.PowerToys samples show some possible usages:

   

As you can see, you can set the Position and PositionType - the later can be set to Center or BottomCenter. You can also set SizeX, SizeY and SizeZ - if you set only one site property, then the model scaled so that aspect ratio is preserved.

You can play with the ObjModelVisual3D.xaml sample to see how to use the ObjModelVisual3D.

This sample shows how to use ObjModelVisual3D in XAML. But you can also use it in code - for example:

Code:
var objModelVisual = new Ab3d.Visuals.ObjModelVisual3D()
{
    Source = new Uri(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/myModel.obj"), UriKind.Absolute),
    DefaultMaterial = new DiffuseMaterial(Brushes.Silver),
    Position = new Point3D(0, 0, 0),
    PositionType = Visuals.ObjModelVisual3D.VisualPositionType.BottomCenter,
    SizeX = 100
};

MainViewport3D.Children.Add(objModelVisual);

Note that in XAML you can set string to Source, but in code you need to provide Uri - in the sample above the obj file is read from a Resources folder that is relative to the application's executable. To see other options on how to specify Uri see the Help with ImageBrush post.


You can rotate any 3D model in WPF with setting RotateTransform3D to Transform property. For example the following XAML rotates the obj model for 30 degrees around up axis (0,1,0):

Code:
<visuals:ObjModelVisual3D Source="/Resources/ObjFiles/Teapot.obj"
                            Position="0 -30 0" PositionType="BottomCenter">
    <visuals:ObjModelVisual3D.Transform>
        <RotateTransform3D>
            <RotateTransform3D.Rotation>
                <AxisAngleRotation3D Axis="0 1 0" Angle="30" />
            </RotateTransform3D.Rotation>
        </RotateTransform3D>
    </visuals:ObjModelVisual3D.Transform>
</visuals:ObjModelVisual3D>

The same can be achieved in code behind:

Code:
objModelVisual.Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 30));


To show or hide ObjModelVisual3D you can set the IsVisible property - this property is available to all Visual3D objects that are defined in Ab3d.PowerToys - for example also for BoxVisual3D or SphereVisual3D.

Other way to hide a 3D model is to remove it from its parent - for example:

Code:
MainViewport3D.Children.Remove(objModelVisual);


So, ObjModelVisual3D is very easy to use.
But if you want to show multiple instances of the same 3D model, then ObjModelVisual3D might not the the best choose. The problem is that each instance ObjModelVisual3D loads its own instance of the obj file. This might be improved in the future with adding some support for caching.

This means that if you want to load obj file only once and show it many time, you will need to use ReaderObj in code. To use ReaderObj you can use the following code:

Code:
var readerObj = new Ab3d.ReaderObj();
var objModel3D = readerObj.ReadModel3D(objFileName);

This will read the 3D object into objModel3D. You can also use another override of ReadModel3D to specify texturesFolder and default material.

After that you can show the same Model3D as many times as you wish with using multiple ModelVisual3D objects:

Code:
for (int x = 0; x < 10; x++)
{
    var modelVisual3D = new ModelVisual3D();
    modelVisual3D.Content = objModel3D;
    modelVisual3D.Transform = new TranslateTransform3D(x * 10, 0, 0);

    MainViewport3D.Children.Add(modelVisual3D);
}

This code creates 10 ModelVisual3D and transforms each one with translating it for x * 10 in x direction.

The size of the shown objects is the same as it is defined in the obj file - the same as defined in the 3D modelling application that was used to create the obj file. Also the position of the model is specified in obj file - in the positions defined in obj file.

To scale the model to your needs, you first need to get the size of the read 3D model. This can be get with reading the Bounds property of the read 3D model - objModel3D.Bounds. The bounds tell you the position and the size of the model.

From that you can calculate the required TranslateTransform3D and ScaleTransform3D - for example:

Code:
double targetSizeX = 100;
Point3D targetCenterPositon = new Point3D(0, 100, 0);

var bounds = objModel3D.Bounds;
var centerPosition = new Point3D(bounds.X + bounds.SizeX / 2,
                                    bounds.Y + bounds.SizeY / 2,
                                    bounds.Z + bounds.SizeZ / 2);

double scale = bounds.SizeX / targetSizeX;


var transform3DGroup = new Transform3DGroup();

// First position the center of the object to (0,0,0) so that scaling does not move the object
transform3DGroup.Children.Add(new TranslateTransform3D(-centerPosition.X, -centerPosition.Y, -centerPosition.Z));

// Scale the object
transform3DGroup.Children.Add(new ScaleTransform3D(scale, scale, scale));

// Move object to target position
transform3DGroup.Children.Add(new TranslateTransform3D(targetCenterPositon.X, targetCenterPositon.Y, targetCenterPositon.Z));

// Now set the Transformation to the ModelVisual3D
modelVisual3D.Transform = transform3DGroup;

This code sets the transformation to the ModelVisual3D object (shown in the previouos code sample).

Note that when using Transform3DGroup with multiple transformations, the order of transformations is important.


Also note that ModelVisual3D is a standard WPF 3D object and not a Visual3D object from Ab3d.PowerToys - this means that it does not have IsVisible property.

So to hide it, you will need to remove it from its parent. To show it again, you can add it to its parent again.

Another possibility to hide it is to set Content property to null. To show it you can set the Content back to objModel3D.

I hope that this post will help you show 3D models from obj files.
Andrej Benedik
#3
Thanks a lot! Great help!

Cheers,
Kalle
  


Forum Jump:


Users browsing this thread:
1 Guest(s)