Getting all existing UserData Properties from Node and other types

I’m writing a tool which re-organizes meshes from one scene into many scenes. I’m able to copy the meshes into the new scenes without issue, but I am losing some Node properties that are being used, and wish to keep intact.

The issue is there is no existing API to get properties from an A3DObject. I currently need to know the list of property names that I wish to copy over, grab them, and assign them to the node in the new scenes that I am creating. Ideally this could be done by querying all properties.

Are there any implications in adding a GetProperties (flags?) for A3DObject, or any alternatives?

This may also be helpful for AssetInfo and Entity

Edit: Additionally I have no way to get the flags of a known Property object. If I were to create a deep clone of a node I would also need to get these flags and set them on the new property instance.

Thanks

Example Code:

    public void CopyNodeAndProperties( Scene sourceScene, Node sourceNode, Scene targetScene )
    {
        // Creates new node instances in targetScene so the hierarchy is the same, but all nodes are new instances in the targt scene
        Node targetNode = GetOrCreateTargetNodeAndAnscestors( sourceScene, sourceNode, targetScene );

        // Example of API. Maybe parameterless overload as well?
        IReadOnlyList<Property> userDefinedProperties = targetNode.GetProperties( PropertyFlags.UserDefined );
        
        foreach( Property userDefinedProperty in userDefinedProperties )
        {
            // Create property in target node
            targetNode.SetProperty( userDefinedProperty.Name, userDefinedProperty.Value );
            
            // Have to set property first, then FindProperty after to get the Property instance
            var targetProperty = targetNode.FindProperty( userDefinedProperty.Name );
            targetProperty.SetFlags( userDefinedProperty.GetFlags() );
        }
    }

@bortos

Thank you for contacting support.

Would you please name some properties explicitly, which you are interested in. Please also share your sample file as ZIP, for our reference. We will further investigate and assist you accordingly.

Hi Farhan

They would be custom user properties that I am setting myself in a previous stage of processing models. I would potentially like to be able to query any custom user defined properties so I don’t need to keep track of all properties that may be used.

Example of a property I am using:

sceneNode.SetProperty( "Category", "Structural");    
sceneNode.SetProperty( "CAD.Material", "Steel");
sceneNode.SetProperty( "CAD.PartNumber", "111111111");
// SetFlags( PropertyFlags.UserDefined, true ); for all of the above properties

Lets say this was done in a custom exporter for a CAD program. If I write a separate tool that moves the models into new scenes based on Category, I can look for the “Category” property and that is no issue since I know about it. The issue is when I want to copy the other properties into the scene without having to list every single one.

For example:
Scene structuralScene = new Scene();

            // Check all node in the input scene for "Structural" ones 
            input.RootNode.Accept( sourceNode =>
            {
                Geometry g = sourceNode.GetEntity<Geometry>();

                if ( g == null )
                    return true;

                string categoryValue = sourceNode.GetProperty( "Category" )?.ToString();

                if ( categoryValue != "Structural" )
                    return true;

                // We have a node to copy into the structuralScene
                Node targetNode = CreateCopyOfNodeAndAncestors( targetScene: structuralScene, sourceScene: input, sourceNode: sourceNode );
                // The above just creates a new Node instance in structuralScene

                // Now we want to copy all properties over that were on the node
                // It cant be done currently
                IReadOnlyList<Property> properties = sourceNode.GetProperties();

                foreach( Property property in properties )
                    targetNode.SetProperty( property.Name, property.Value );

                return true;
            } );

Are there any implications of adding a GetProperties method?

@bortos

Thank you for elaborating further.

We will be refactoring the properties system and will get back to you soon.

@bortos

Would you please try attached code snippet which demonstrates the use of new property system with attached hotfix of the API.

Prop-Snippet.zip
Aspose.3D for .NET 19.10.1.zip

Hi Farhan

That looks good. I’ve had a play and it’s much nicer now.

I have been meaning to make another post regarding the “UpAxis” property for FBX not being exposed, but I may as well post it here. I’m not currently able to get this specific property, however other fbx properties are coming in nicely:

image.png (27.6 KB)
Teapot-Z-Up.zip (95.6 KB)

In the image above you can see what properties are being loaded. I’m particularly interested in the coordinate system properties. In the ascii fbx (in the zip attached) you can see them all here:

GlobalSettings: {
Version: 1000
Properties70: {
P: “UpAxis”, “int”, “Integer”, “”,2
P: “UpAxisSign”, “int”, “Integer”, “”,1
P: “FrontAxis”, “int”, “Integer”, “”,1
P: “FrontAxisSign”, “int”, “Integer”, “”,-1
P: “CoordAxis”, “int”, “Integer”, “”,0
P: “CoordAxisSign”, “int”, “Integer”, “”,1
P: “OriginalUpAxis”, “int”, “Integer”, “”,2
P: “OriginalUpAxisSign”, “int”, “Integer”, “”,1
P: “UnitScaleFactor”, “double”, “Number”, “”,100
P: “OriginalUnitScaleFactor”, “double”, “Number”, “”,100.00000066
P: “AmbientColor”, “ColorRGB”, “Color”, “”,0,0,0
P: “DefaultCamera”, “KString”, “”, “”, “Producer Perspective”
P: “TimeMode”, “enum”, “”, “”,6
P: “TimeProtocol”, “enum”, “”, “”,2
P: “SnapOnFrameMode”, “enum”, “”, “”,0
P: “TimeSpanStart”, “KTime”, “Time”, “”,0
P: “TimeSpanStop”, “KTime”, “Time”, “”,153953860000
P: “CustomFrameRate”, “double”, “Number”, “”,-1
P: “TimeMarker”, “Compound”, “”, “”
P: “CurrentTimeMarker”, “int”, “Integer”, “”,-1
}
}

Note that UpAxis does not appear in the properties list in scene.RootNode.AssetInfo.

        foreach( Property property in scene.RootNode.AssetInfo.Properties )
        {
            Debug.WriteLine( $"{property.Name}={property.Value}" );
        }

Outputs:

FrontAxisSign=-1
SnapOnFrameMode=0
CustomFrameRate=-1
OriginalUnitScaleFactor=100.00000066
FrontAxis=1
CoordAxisSign=1
TimeMarker=
TimeSpanStart=0
UpAxisSign=1
OriginalUpAxis=2
CurrentTimeMarker=-1
OriginalUpAxisSign=1
DefaultCamera=Producer Perspective
TimeSpanStop=3.33333333333333

@bortos

Thank you for your kind feedback.

We are glad to know that you are satisfied with new property system. For UpAxis property, we have logged a ticket with ID THREEDNET-581 in our issue management system and will let you know once any update will be available in this regard.

@bortos

According to FBX SDK Documentation:

enum EUpVector
 {         eXAxis = 1,         eYAxis = 2,         eZAxis = 3      };

The P: “UpAxis”, “int”, “Integer”, “”,2 means the up axis is y axis.

Code to read this:

Scene scene = new Scene(@"Teapot-Z-Up.FBX");
Console.WriteLine(scene.RootNode.AssetInfo.UpVector);

Builtin properties were filtered out because they are handled by AssetInfo.

In attached hotfix 19.10.3, there is a load option to bypass this behavior so you can get the raw value instead of the native property, changes on the dynamic property directly will not affect the resaved FBX file (Change AssetInfo.UpVector instead):

Scene scene = new Scene();            
var opt = new FBXLoadOptions() { KeepBuiltinGlobalSettings = true };            scene.Open(@"Teapot-Z-Up.FBX", opt);            
foreach (Property property in scene.RootNode.AssetInfo.Properties)
{                
Console.WriteLine(property);            
}

Aspose.3D for .NET 19.10.3.zip

The P: “UpAxis”, “int”, “Integer”, “”,2 means the up axis is y axis.

Hi Farhan, I’m not certain about the above, or at least there’s some confusion

I’ve manually created a teapot in 3ds max 2019 and exported it separately and named correctly with both “Z Up” and “Y Up” options in the fbx export settings in max.

Teapot-Y-Up.fbx ascii:

GlobalSettings: {
Version: 1000
Properties70: {
P: “UpAxis”, “int”, “Integer”, “”,1
P: “UpAxisSign”, “int”, “Integer”, “”,1
P: “FrontAxis”, “int”, “Integer”, “”,2
P: “FrontAxisSign”, “int”, “Integer”, “”,1
P: “CoordAxis”, “int”, “Integer”, “”,0
P: “CoordAxisSign”, “int”, “Integer”, “”,1

Teapot-Z-Up.fbx

GlobalSettings: {
Version: 1000
Properties70: {
P: “UpAxis”, “int”, “Integer”, “”,2
P: “UpAxisSign”, “int”, “Integer”, “”,1
P: “FrontAxis”, “int”, “Integer”, “”,1
P: “FrontAxisSign”, “int”, “Integer”, “”,-1
P: “CoordAxis”, “int”, “Integer”, “”,0
P: “CoordAxisSign”, “int”, “Integer”, “”,1

I’m trying to find more documentation and scour github, but it’s hard to find a clear answer.

Some more info from fbx sdk…

Teapots.zip (287.0 KB)

My source code:

		int upDir;
		FbxAxisSystem::EUpVector upVector = scene->GetGlobalSettings().GetAxisSystem().GetUpVector(upDir);


        if(upVector == FbxAxisSystem::eXAxis)
        {
			FbxLog::Log(String::Format("Up Axis is FbxAxisSystem::eXAxis with dir " + upDir.ToString()));
        }
		else if (upVector == FbxAxisSystem::eYAxis)
		{
			FbxLog::Log(String::Format("Up Axis is FbxAxisSystem::eYAxis with dir " + upDir.ToString()));
		}
		else if (upVector == FbxAxisSystem::eZAxis)
		{
			FbxLog::Log(String::Format("Up Axis is FbxAxisSystem::eZAxis with dir " + upDir.ToString()));
		}
		else
		{
			FbxLog::Log(String::Format("Up Axis is unknown"));
		}

		int frontDir;
		FbxAxisSystem::EFrontVector frontVector = scene->GetGlobalSettings().GetAxisSystem().GetFrontVector(frontDir);

		if (frontVector == FbxAxisSystem::eParityEven)
		{
			FbxLog::Log(String::Format("EFrontVector is FbxAxisSystem::eParityEven with dir " + frontDir.ToString()));
		}
		else if (frontVector == FbxAxisSystem::eParityOdd)
		{
			FbxLog::Log(String::Format("EFrontVector is FbxAxisSystem::eParityOdd with dir " + frontDir.ToString()));
		}

		FbxAxisSystem::ECoordSystem coordSystem = scene->GetGlobalSettings().GetAxisSystem().GetCoorSystem();

		if (coordSystem == FbxAxisSystem::eLeftHanded)
		{
			FbxLog::Log(String::Format("ECoordSystem is FbxAxisSystem::eLeftHanded"));
		}
		else if (coordSystem == FbxAxisSystem::eRightHanded)
		{
			FbxLog::Log(String::Format("ECoordSystem is FbxAxisSystem::eRightHanded"));
		}

Teapot-Y-Up.FBX:

10:52:07.7598|TRACE|TestAsposeCommand|1:Importing file C:\Users\Alek\Desktop\Teapot-Y-Up.FBX
10:52:08.1410|TRACE|TestAsposeCommand|1:Up Axis is FbxAxisSystem::eYAxis with dir 1
10:52:08.1410|TRACE|TestAsposeCommand|1:EFrontVector is FbxAxisSystem::eParityOdd with dir 1
10:52:08.1410|TRACE|TestAsposeCommand|1:ECoordSystem is FbxAxisSystem::eRightHanded
10:52:08.1440|TRACE|TestAsposeCommand|1:Releasing instance.

Teapot-Z-Up.FBX:

10:56:34.9714|TRACE|TestAsposeCommand|1:Importing file C:\Users\Alek\Desktop\Teapot-Z-Up.FBX
10:56:35.3565|TRACE|TestAsposeCommand|1:Up Axis is FbxAxisSystem::eZAxis with dir 1
10:56:35.3565|TRACE|TestAsposeCommand|1:EFrontVector is FbxAxisSystem::eParityOdd with dir -1
10:56:35.3565|TRACE|TestAsposeCommand|1:ECoordSystem is FbxAxisSystem::eRightHanded
10:56:35.3625|TRACE|TestAsposeCommand|1:Releasing instance.

Manually opening and modifying the fbx axis (Teapot-UpAxis0.FBX):

GlobalSettings: {
Version: 1000
Properties70: {
P: “UpAxis”, “int”, “Integer”, “”,0
P: “UpAxisSign”, “int”, “Integer”, “”,1

10:59:49.6750|TRACE|TestAsposeCommand|1:Importing file C:\Users\Alek\Desktop\Teapot-UpAxis0.FBX
10:59:50.0307|TRACE|TestAsposeCommand|1:Up Axis is FbxAxisSystem::eXAxis with dir 1
10:59:50.0307|TRACE|TestAsposeCommand|1:EFrontVector is FbxAxisSystem::eParityEven with dir -1
10:59:50.0307|TRACE|TestAsposeCommand|1:ECoordSystem is FbxAxisSystem::eLeftHanded
10:59:50.0337|TRACE|TestAsposeCommand|1:Releasing instance.

@bortos

We have recorded your comments and will update you once any information will be available.

@bortos

Due to the historical reasons and the compatibility between different applications, the FBX does not really record the coordinate system in semantic level.

According to FBX SDK’s documentation, the axis system is actually performed by two factors, the information defined in GlobalSettings and the PreRotation in each node of the scene.

I compared the file Teapot-Y-Up.FBX and Teapot-Z-Up.FBX, the major differences are the GlobalSettings and the PreRotation defined in Model section confirmed this guess:

P: "PreRotation", "Vector3D", "Vector", "",-90,-0,0            P: "RotationActive", "bool", "", "",1

So the export using Y/Z axis up, the FBX only guarantees the visual correctness.

Hi Farhan

I guess what I’m trying to achieve is the ability to perform my own coordinate space transformations so I can handle fbx exports by many applications and coordinate systems, and create a native mesh in my application, which operates in a left handed system with Y up, Z forward space.

For this I need to:

  1. Load fbx with Aspose.ThreeD.Scene
  2. Read global settings properties to determine the current system being used. i.e. up and forward vector, and the handedness.
  3. Perform a coordinate space transformation (the hard bit)

Step 3 can be done in the native mesh format in the application, but I need to get #2 working correctly. It would be nice if I could do this, and save the fbx with updated global settings properties, but that is not necessary yet.

In another thread I asked and it was mentioned that for advanced and customizable formats, such as fbx, Aspose.ThreeD doesn’t do any coordinate system conversion. Is that correct? That makes perfect sense to me if so.

@bortos

We are collecting details and will get back to you shortly.

@bortos

Even in Autodesk’s official FBX SDK’s documentation, the coordinate system conversion was performed by making changes to the transform (Do not rely too much on the GlobalSettings)

If you want to automate the pipeline of your assets from different tools, we suggest to formalize the asset export procedure, that could be better than a universal solution.

But for some company’s asset pipeline automation, you could do something like this:

  1. Make sure each modeling tool only use fixed coordinate and hand system

  2. Prepare a coordinate/hand system normalization matrix depends on the modeling tool (Can be determinted from the asset info)

    For example to swap y/z axis, you can use the following matrix:

    | 1 0 0 0 |
    | 0 0 1 0 |
    | 0 1 0 0 |
    | 0 0 0 1 |

  3. Update the scene node’s transform by multiplying the matrix.

The Aspose.3D’s FBX exporter does not do any thing about coordinate/hand system, and keep everything to what it was, and its features are totally equivalent to FBX SDK.

HI Farhan

Yep, I’m aware, and I currently do steps 2 at the moment. Step 1 is not possible, as it is outside of our toolchain. They are usually consistent but on occasion one is in a different system.

Currently step 2 requires me to configure the transformation matrix manually, but I would like to automate that. I believe that matrix can be created using the following properties from scene.RootNode.AssetInfo:

P: “UpAxis”, “int”, “Integer”, “”,2
P: “UpAxisSign”, “int”, “Integer”, “”,1
P: “FrontAxis”, “int”, “Integer”, “”,1
P: “FrontAxisSign”, “int”, “Integer”, “”,-1
P: “CoordAxis”, “int”, “Integer”, “”,0
P: “CoordAxisSign”, “int”, “Integer”, “”,1

For me to do the above, I need to be sure UpAxis is correct.

Did you end up discovering anything after this post? Getting all existing UserData Properties from Node and other types - #10 by bortos

@bortos

We will be sharing our findings shortly.

@bortos

Our suggestion is that you can not blindly rely on the UpAxis when PreRotation was used (which axis is up axis will be no longer correct in semantic level), that means it is impossible to build such a universal tool.

But for an artist team, you can be certain about the modeling tool that your members are using, so we can have their rule based on the UpAxis (even when it is incorrect) and the authoring tool information in the AssetInfo, like in 3ds max, the UpAxis = 1 you can infer that it is a Y axis up and when UpAxis=2 you can infer it is a Z axis (although according to FBX SDK it isn’t).

Moreover, we are further investigating FBX SDK about the UpAxis and will update you once any information will be available.

Hi Farhan, any updates?

@bortos

You can use the combination of these properties (Use raw dynamic properties from AssetInfo) to infer the up axis like the Autodesk FBX SDK did as a work around (e.g. UpAxis=2/UpAxisSign=1/FrontAxis=1/FrontAxisSign=-1/CoordAxis=0/CoordAxisSign=1 means 3ds max’s Z up).

Currently, Aspose.3D API only exposes the exact value from FBX file, which is not what you want so we will refactor the coordinate/axis system in upcoming release, this will include:

  1. Better interface to access coordinate/axis system
  2. Conversion between different coordinate/axis systems