Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ut 148/downcast fbx objects #302

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ fbxsharp_run_python(OUTPUT ${CMAKE_BINARY_DIR}/weakpointerhandles.i
TARGETDEPS SWIG_MODULE_UnityFbxSdkNative_EXTRA_DEPS
ARGS ${CMAKE_BINARY_DIR}/weakpointerhandles.i ${CMAKE_BINARY_DIR}/fbxsdk.typedefs "FbxEmitter" "FbxManager")

# Find all children of FbxObject to generate the downcast method to enable the
# downcast of FbxObjects on the C# side
fbxsharp_run_python(OUTPUT ${CMAKE_BINARY_DIR}/downcast_table.i
SCRIPT ${CMAKE_SOURCE_DIR}/scripts/generate-downcast-table.py
DEPENDS ${CMAKE_BINARY_DIR}/fbxsdk.typedefs
TARGETDEPS SWIG_MODULE_UnityFbxSdkNative_EXTRA_DEPS
ARGS ${CMAKE_BINARY_DIR}/downcast_table.i ${CMAKE_BINARY_DIR}/fbxsdk.typedefs)

# Finalize the swig output: build & link the library, munge the DllImport statements
SET(CMAKE_SWIG_OUTDIR ${CMAKE_BINARY_DIR}/swig/generated/csharp)
Expand Down
20 changes: 20 additions & 0 deletions Source/fbxclassid.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************************
// Copyright (c) 2017 Unity Technologies. All rights reserved.
//
// Licensed under the ##LICENSENAME##.
// See LICENSE.md file in the project root for full license information.
// ***********************************************************************

%rename("%s", %$isclass) FbxClassId;
// %rename("%s", %$isclass) FbxClassIdInfo* ;

%rename("%s") FbxClassId::FbxClassId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pulling in all the constructors, and one of them has an type we don't map -- so you get a SWIGTYPE.

%rename("%s") FbxClassId::Create;
%rename("%s") FbxClassId::Is;
%rename("%s") FbxClassId::GetName;
%rename("%s") FbxClassId::IsValid;
%rename("%s") FbxClassId::GetParent;


// also IS, getName, and others.
%include "fbxsdk/core/fbxclassid.h"
14 changes: 14 additions & 0 deletions Source/fbxcollection.i
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
%rename("%s") FbxCollection::Clear;
%rename("%s") FbxCollection::AddMember;
%rename("%s") FbxCollection::GetAnimLayerMember;
%rename("%s") FbxCollection::FindMemberObject;
%rename("%s") FbxCollection::GetMemberObject;
%rename("%s") FbxCollection::GetMemberCount() const;

// %template(FindMemberFbxObject) FbxCollection::FindMember<FbxObject>;
// %template(FindMemberFbxObject) FbxCollection::GetMember<FbxObject>;

%extend FbxCollection{
/*
* GetMember returns an FbxObject, but we need to get an object of
Expand All @@ -22,6 +27,15 @@
FbxAnimLayer* GetAnimLayerMember(int pIndex = 0) const {
return $self->GetMember<FbxAnimLayer>(pIndex);
}

FbxObject* GetMemberObject(int pIndex = 0) const
{
return $self->GetMember<FbxObject>(pIndex);
}
FbxObject* FindMemberObject(const char* pName) const
{
return $self->FindMember<FbxObject>(pName);
}
}

#endif
Expand Down
3 changes: 3 additions & 0 deletions Source/fbxobject.i
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
%rename("%s") FbxObject::GetFbxManager;
%rename("%s") FbxObject::GetScene;

%rename("%s") FbxObject::GetRuntimeClassId;

/* Properties */
%rename("%s") FbxObject::GetFirstProperty() const;
%rename("%s") FbxObject::GetNextProperty(const FbxProperty& pProperty) const;
Expand Down Expand Up @@ -82,6 +84,7 @@
return string.Format("{0}({1})", name, GetType().Name);
}
%}

}

%include "fbxsdk/core/fbxobject.h"
9 changes: 9 additions & 0 deletions Source/fbxsdk.i
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@
%include "FbxSharpObjectLifetime.i"
#ifndef SWIG_GENERATING_TYPEDEFS
%include "weakpointerhandles.i"
/*Downcast table information is generated at the same time as the weakpointer*/
%include "downcast_table.i"
#endif

/*
Expand Down Expand Up @@ -236,6 +238,12 @@
***************************************************************************/
%reveal_all_end;

%typemap(csout, excode=SWIGEXCODE) FbxDeformer*, FbxSkin*, FbxObject* {
System.IntPtr cPtr = $imcall;
$csclassname ret = ($csclassname) NativeMethods.Realtype(cPtr, $owner);$excode;
return ret;
}

/* Core classes */
%include "fbxmath.i"
%include "fbxmanager.i"
Expand All @@ -248,6 +256,7 @@
%include "fbxquaternion.i"
%include "fbxprogress.i"
%include "fbxtransforms.i"
%include "fbxclassid.i"

/* The emitter hierarchy. Must be in partial order (base class before derived class). */
%include "fbxemitter.i"
Expand Down
129 changes: 129 additions & 0 deletions scripts/generate-downcast-table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#! /usr/bin/python

# ***********************************************************************
# Copyright (c) 2017 Unity Technologies. All rights reserved.
#
# Licensed under the ##LICENSENAME##.
# See LICENSE.md file in the project root for full license information.
# ***********************************************************************

import sys
import re

# Input:
# 1- the output .i file
# 2- the file that is the result of swig -debug-typedef
# Output:
# - a swig .i file to be %included at the start of the fbxsdk.i file

# This should normally be integrated as part of the build system.
output_filename = sys.argv[1]
typedefs_filename = sys.argv[2]

rootclass = "FbxObject"

# For each derived class, a list of classes it inherits from. If a class isn't
# in this dict it's not a derived class (it inherits from nothing).
baseclasses = dict()

def fix_classname(clsname):
"""
Template arguments get wrapped in parens for some reason.
"""
return re.sub(r'<\(', '<', re.sub(r'\)>', '>', clsname))

with open(typedefs_filename) as typedef_file:
current_scope = None
bases = []
def store():
if current_scope and bases:
baseclasses[current_scope] = bases
for line in typedef_file:
m = re.search("Type scope '(.*)'", line)
if m:
# changing scope; store the old one, clear the accumulating list
store()
current_scope = fix_classname(m.group(1))
bases = []
m = re.search("Inherits from '(.*)'", line)
if m:
bases.append(fix_classname(m.group(1)))
# end of file; remember the last block we read
if current_scope and bases:
store()

# Find all the classes that derive from FbxObject.
handleclasses = set()

handleclasses.add(rootclass)
for cls in baseclasses:
# squash a warning about %extend, which happens when you
# declare a typedef to a root class
if cls.endswith('::ParentClass'): continue
if rootclass in baseclasses[cls]:
handleclasses.add(cls)

leafclasses = set()
allbases = []
# for klass in handleclasses:
# import pprint as pp
for klass in handleclasses:
allbases += baseclasses[klass]
allbases = set(allbases)
for klass in handleclasses:
if klass not in allbases:
leafclasses.add(klass)
nonleafclasses = handleclasses - leafclasses



statement_start = """
private static System.Collections.Generic.Dictionary<System.IntPtr, int> ptrToIndexDict = new System.Collections.Generic.Dictionary<System.IntPtr, int>();
public static FbxObject Realtype (System.IntPtr cPtr, bool ignored)
{
if (cPtr == System.IntPtr.Zero)
{
return null;
}

System.IntPtr p = NativeMethods.FbxObject_GetRuntimeClassId(new System.Runtime.InteropServices.HandleRef(null, cPtr));
string name = NativeMethods.FbxClassId_GetName(new System.Runtime.InteropServices.HandleRef(null, p));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 memory allocations per downcast... Let's optimize this on Monday.

// This switch statement is auto-generated by a script
switch (name)
{"""

statement_body_template = """
case "{}":
return new {}(cPtr, ignored);"""

statement_end = """
default:
break;
}
// we failed to get a match, return a bland FbxObject.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it the TODO is to repeat the switch but on the parent class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I have a prototype that has not yet been committed.

return new FbxObject(cPtr, ignored);
}
"""

typemap_template = """
%typemap(csout, excode=SWIGEXCODE) {} {{
System.IntPtr cPtr = $imcall;
$csclassname ret = ($csclassname) NativeMethods.Realtype(cPtr, $owner);$excode;
return ret;
}}
"""

body_statements = [statement_body_template.format(klass, klass) for klass in handleclasses]
constructor_func = statement_start + ''.join(body_statements) + statement_end

# write out the code
with open(output_filename, 'w') as outfile:
outfile.write("/*This code has been generated by the generate-downcast-table.py build script*/\n")
outfile.write("%pragma(csharp) imclasscode=%{\n")
outfile.write(constructor_func)
outfile.write("%}\n")
# the last type in the list also needs the pointer type qualifier
typemap_list = "*, \n".join(nonleafclasses) + '*'
outfile.write(typemap_template.format(typemap_list))

print("Generated downcast table for {} classes.".format(len(handleclasses)))
113 changes: 113 additions & 0 deletions tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// ***********************************************************************
// Copyright (c) 2017 Unity Technologies. All rights reserved.
//
// Licensed under the ##LICENSENAME##.
// See LICENSE.md file in the project root for full license information.
// ***********************************************************************
using NUnit.Framework;
using Autodesk.Fbx;
using System.IO;



namespace Autodesk.Fbx.UseCaseTests
{
public class DowncastFbxType
{
[Test]
public void TestDowncast ()
{
using(FbxManager man = FbxManager.Create())
{
FbxMesh m = FbxMesh.Create(man, "some mesh");
FbxSkin s = FbxSkin.Create(man, "some deformer skin");
int index = m.AddDeformer(s);
// FbxSkin o = m.GetDeformer(index) as FbxSkin;
FbxSkin o = (FbxSkin)m.GetDeformer(index);
Assert.That(o, Is.Not.Null);
}
}

[Test]
public void TestWeirdCase()
{
string constraintName = "sdkjhsdkjhfkjsdhfjks";
string fileName = "bobobo.fbx";
string meshName = "some mesh";
string skinName = "some skin";
int index = 0;
using(FbxManager manager = FbxManager.Create())
{
using (FbxExporter exporter = FbxExporter.Create (manager, "myExporter"))
{
var format = manager.GetIOPluginRegistry().FindWriterIDByDescription("FBX ascii (*.fbx)");
// Initialize the exporter.
bool status = exporter.Initialize (fileName, format, manager.GetIOSettings ());

// Create a scene with a single node that has an animation clip
// attached to it
FbxScene scene = FbxScene.Create(manager, "myScene");

FbxNode sourceNode = FbxNode.Create(scene, "source");
FbxNode constrainedNode = FbxNode.Create(scene, "constrained");

scene.GetRootNode().AddChild(sourceNode);
scene.GetRootNode().AddChild(constrainedNode);

FbxMesh m = FbxMesh.Create(manager, meshName);
FbxSkin s = FbxSkin.Create(manager, skinName);
index = m.AddDeformer(s);
scene.AddMember(m);
scene.AddMember(s);

// FbxConstraint posConstraint = CreatePositionConstraint(scene, sourceNode, constrainedNode);
FbxConstraintPosition constraint = FbxConstraintPosition.Create(scene, constraintName);

constraint.SetConstrainedObject(constrainedNode);
constraint.AddConstraintSource(sourceNode);

constraint.AffectX.Set(true);
constraint.AffectY.Set(true);
constraint.AffectZ.Set(true);

constraint.Translation.Set(new FbxDouble3(1, 2, 3));

Assert.That(constraint, Is.Not.Null);

bool result = constraint.ConnectDstObject(scene);
Assert.That(result, Is.True);

// export the scene
exporter.Export(scene);
}
FbxScene importedScene = null;
// FbxMesh importedMesh = null;
using (FbxImporter importer = FbxImporter.Create (manager, "myImporter"))
{

// Initialize the importer.
bool status = importer.Initialize (fileName, -1, manager.GetIOSettings ());

Assert.IsTrue (status);

// Create a new scene so it can be populated by the imported file.
importedScene = FbxScene.Create (manager, "myScene");

// Import the contents of the file into the scene.
importer.Import (importedScene);
}
FbxMesh importedMesh = (FbxMesh)importedScene.FindMemberObject(meshName);
Assert.That(importedMesh, Is.Not.Null);
FbxSkin importedSkin = (FbxSkin)importedScene.FindMemberObject(skinName);
Assert.That(importedSkin, Is.Not.Null);
FbxNode importSourceNode = importedScene.GetRootNode().GetChild(0);
FbxNode importConstrainedNode = importedScene.GetRootNode().GetChild(1);
FbxObject importPosConstraint = importedScene.FindSrcObject(constraintName);
UnityEngine.Debug.Log($"imported type is {importPosConstraint.GetType()}");
FbxConstraintPosition p = (FbxConstraintPosition)importPosConstraint;
FbxConstraint pp = (FbxConstraint)importPosConstraint; // fails here
}
}
}
}
//
11 changes: 11 additions & 0 deletions tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.