diff --git a/CMakeLists.txt b/CMakeLists.txt index bbe58cc3..d1318c9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Source/fbxclassid.i b/Source/fbxclassid.i new file mode 100644 index 00000000..8e92044f --- /dev/null +++ b/Source/fbxclassid.i @@ -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; +%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" diff --git a/Source/fbxcollection.i b/Source/fbxcollection.i index af0f4e3f..18e5ad06 100644 --- a/Source/fbxcollection.i +++ b/Source/fbxcollection.i @@ -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; +// %template(FindMemberFbxObject) FbxCollection::GetMember; + %extend FbxCollection{ /* * GetMember returns an FbxObject, but we need to get an object of @@ -22,6 +27,15 @@ FbxAnimLayer* GetAnimLayerMember(int pIndex = 0) const { return $self->GetMember(pIndex); } + + FbxObject* GetMemberObject(int pIndex = 0) const + { + return $self->GetMember(pIndex); + } + FbxObject* FindMemberObject(const char* pName) const + { + return $self->FindMember(pName); + } } #endif diff --git a/Source/fbxobject.i b/Source/fbxobject.i index 8953c699..279f12da 100644 --- a/Source/fbxobject.i +++ b/Source/fbxobject.i @@ -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; @@ -82,6 +84,7 @@ return string.Format("{0}({1})", name, GetType().Name); } %} + } %include "fbxsdk/core/fbxobject.h" diff --git a/Source/fbxsdk.i b/Source/fbxsdk.i index 095a6107..82fb8c8d 100644 --- a/Source/fbxsdk.i +++ b/Source/fbxsdk.i @@ -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 /* @@ -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" @@ -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" diff --git a/scripts/generate-downcast-table.py b/scripts/generate-downcast-table.py new file mode 100644 index 00000000..9a7dc3f5 --- /dev/null +++ b/scripts/generate-downcast-table.py @@ -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 ptrToIndexDict = new System.Collections.Generic.Dictionary(); +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)); + // 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. + 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))) \ No newline at end of file diff --git a/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs b/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs new file mode 100644 index 00000000..42fd4fcd --- /dev/null +++ b/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs @@ -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 + } + } + } +} +// \ No newline at end of file diff --git a/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs.meta b/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs.meta new file mode 100644 index 00000000..47e328ec --- /dev/null +++ b/tests/RuntimeTests/Editor/UseCaseTests/DowncastFbxType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9696dbb180ca224b8fa4daf9b6d3012 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: