2020-09-24 00:43:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
2020-11-24 18:42:39 -04:00
using System.Linq ;
2020-12-11 14:21:20 -04:00
using System.Text ;
2021-02-03 14:57:28 -04:00
using System.Threading.Tasks ;
2020-09-24 00:43:27 -04:00
using Autodesk.Revit.DB ;
using Autodesk.Revit.DB.Architecture ;
using Autodesk.Revit.DB.Mechanical ;
using Autodesk.Revit.DB.Plumbing ;
using Autodesk.Revit.DB.Structure ;
using Autodesk.Revit.DB.Visual ;
namespace DatasmithRevitExporter
{
2022-09-20 21:29:32 -04:00
public class OrientatedBoundingBox
{
int tlf = 0 ;
int trf = 1 ;
int blf = 2 ;
int brf = 3 ;
int tlb = 4 ;
int trb = 5 ;
int blb = 6 ;
int brb = 7 ;
double Epsilon = 0.000001 ;
// Z
// | Y
// |/
// o---X
//
// tlb-------MAX
// /| /|
// / | / |
// tlf-------trf |
// | | | |
// | blb-----|-brb
// | / | /
// |/ |/
// MIN-------brf
//
// Vertices[0] := tlf: Top Left Front
// Vertices[1] := trf: Top Right Front
// Vertices[2] := blf: Bottom Left Front => min
// Vertices[3] := brf: Bottom Right Front
// Vertices[4] := tlb: Top Left Back
// Vertices[5] := trb: Top Right Back => max
// Vertices[6] := blb: Bottom Left Back
// Vertices[7] := brb: Bottom Right Back
public XYZ [ ] Vertices = new XYZ [ 8 ] ; //8 vertices // corners
double SideXDistance ;
double SideYDistance ;
double SideZDistance ;
public XYZ AxisAllignedMin ;
public XYZ AxisAllignedMax ;
Plane SideX0 , SideX1 ;
Plane SideY0 , SideY1 ;
Plane SideZ0 , SideZ1 ;
public bool bIsValidData ;
public OrientatedBoundingBox ( Transform InTransform , XYZ MIN , XYZ MAX , bool InCalculatePlanes = false )
{
SideXDistance = ( MAX . X - MIN . X ) + Epsilon ;
SideYDistance = ( MAX . Y - MIN . Y ) + Epsilon ;
SideZDistance = ( MAX . Z - MIN . Z ) + Epsilon ;
bIsValidData = SideXDistance > Epsilon & & SideYDistance > Epsilon & & SideZDistance > Epsilon ;
if ( ! bIsValidData )
{
return ;
}
Vertices [ tlf ] = new XYZ ( MIN . X , MIN . Y , MAX . Z ) ;
Vertices [ trf ] = new XYZ ( MAX . X , MIN . Y , MAX . Z ) ;
Vertices [ brf ] = new XYZ ( MAX . X , MIN . Y , MIN . Z ) ;
Vertices [ blf ] = MIN ;
Vertices [ tlb ] = new XYZ ( MIN . X , MAX . Y , MAX . Z ) ;
Vertices [ trb ] = MAX ;
Vertices [ brb ] = new XYZ ( MAX . X , MAX . Y , MIN . Z ) ;
Vertices [ blb ] = new XYZ ( MIN . X , MAX . Y , MIN . Z ) ;
double MinX = double . MaxValue ;
double MinY = double . MaxValue ;
double MinZ = double . MaxValue ;
double MaxX = - double . MaxValue ;
double MaxY = - double . MaxValue ;
double MaxZ = - double . MaxValue ;
for ( int Index = 0 ; Index < = brb ; Index + + )
{
Vertices [ Index ] = InTransform . OfPoint ( Vertices [ Index ] ) ;
if ( Vertices [ Index ] . X < MinX ) MinX = Vertices [ Index ] . X ;
if ( Vertices [ Index ] . Y < MinY ) MinY = Vertices [ Index ] . Y ;
if ( Vertices [ Index ] . Z < MinZ ) MinZ = Vertices [ Index ] . Z ;
if ( Vertices [ Index ] . X > MaxX ) MaxX = Vertices [ Index ] . X ;
if ( Vertices [ Index ] . Y > MaxY ) MaxY = Vertices [ Index ] . Y ;
if ( Vertices [ Index ] . Z > MaxZ ) MaxZ = Vertices [ Index ] . Z ;
}
AxisAllignedMax = new XYZ ( MaxX , MaxY , MaxZ ) ;
AxisAllignedMin = new XYZ ( MinX , MinY , MinZ ) ;
if ( InCalculatePlanes )
{
//We only need the planes for the SectionBox:
CalculatePlanes ( ) ;
}
}
private void CalculatePlanes ( )
{
SideX0 = Plane . CreateByThreePoints ( Vertices [ tlf ] , Vertices [ blb ] , Vertices [ tlb ] ) ;
SideX1 = Plane . CreateByThreePoints ( Vertices [ trf ] , Vertices [ trb ] , Vertices [ brb ] ) ;
SideY0 = Plane . CreateByThreePoints ( Vertices [ tlf ] , Vertices [ trf ] , Vertices [ brf ] ) ;
SideY1 = Plane . CreateByThreePoints ( Vertices [ tlb ] , Vertices [ brb ] , Vertices [ trb ] ) ;
SideZ0 = Plane . CreateByThreePoints ( Vertices [ blf ] , Vertices [ brb ] , Vertices [ blb ] ) ;
SideZ1 = Plane . CreateByThreePoints ( Vertices [ tlf ] , Vertices [ tlb ] , Vertices [ trb ] ) ;
}
private bool IsPointWithin ( Plane Plane1 , Plane Plane2 , double Distance , XYZ Point )
{
UV Uv ;
double Distance1 ;
double Distance2 ;
Plane1 . Project ( Point , out Uv , out Distance1 ) ;
if ( Distance1 > Distance )
{
return false ;
}
Plane2 . Project ( Point , out Uv , out Distance2 ) ;
if ( Distance2 > Distance )
{
return false ;
}
return true ;
}
private bool IsPointInside ( XYZ Point )
{
return IsPointWithin ( SideX0 , SideX1 , SideXDistance , Point )
& & IsPointWithin ( SideY0 , SideY1 , SideYDistance , Point )
& & IsPointWithin ( SideZ0 , SideZ1 , SideZDistance , Point ) ;
}
// If self contains Candidate it returns false:
public bool DoesIntersect ( OrientatedBoundingBox Candidate )
{
int VertexWithinCounter = 0 ;
int VertexOutsideCounter = 0 ;
for ( int Index = 0 ; Index < Candidate . Vertices . Length ; Index + + )
{
if ( IsPointInside ( Candidate . Vertices [ Index ] ) )
{
VertexWithinCounter + + ;
}
else
{
VertexOutsideCounter + + ;
}
if ( VertexOutsideCounter > 0 & & VertexWithinCounter > 0 )
{
return true ;
}
}
//It returns true only if we have at least 1 vertex within and at least 1 vertex outside
//every other scenario should return false
//as in:
// - all vertex outside := false
// - all vertex inside := false => geometries with completely contained bounding boxes won't get clipped.
return false ;
}
}
2020-09-24 00:43:27 -04:00
public class FDocumentData
{
2021-05-11 01:10:20 -04:00
// This class reflects the child -> super component relationship in Revit into the exported hierarchy (children under super components actors).
class FSuperComponentOptimizer
{
private Dictionary < FBaseElementData , Element > ElementDataToElementMap = new Dictionary < FBaseElementData , Element > ( ) ;
private Dictionary < ElementId , FBaseElementData > ElementIdToElementDataMap = new Dictionary < ElementId , FBaseElementData > ( ) ;
public void UpdateCache ( FBaseElementData ParentElement , FBaseElementData ChildElement )
{
if ( ! ElementDataToElementMap . ContainsKey ( ParentElement ) )
{
Element Elem = null ;
if ( ParentElement . GetType ( ) = = typeof ( FElementData ) )
{
Elem = ( ( FElementData ) ParentElement ) . CurrentElement ;
}
else if ( ChildElement . GetType ( ) = = typeof ( FElementData ) )
{
Elem = ( ( FElementData ) ChildElement ) . CurrentElement ;
}
if ( Elem ! = null )
{
ElementDataToElementMap [ ParentElement ] = Elem ;
ElementIdToElementDataMap [ Elem . Id ] = ParentElement ;
}
}
}
public void Optimize ( )
{
foreach ( var KV in ElementDataToElementMap )
{
FBaseElementData ElemData = KV . Key ;
Element Elem = KV . Value ;
if ( ( Elem as FamilyInstance ) ! = null )
{
Element Parent = ( Elem as FamilyInstance ) . SuperComponent ;
if ( Parent ! = null )
{
FBaseElementData SuperParent = null ;
bool bGot = ElementIdToElementDataMap . TryGetValue ( Parent . Id , out SuperParent ) ;
if ( bGot & & SuperParent ! = ElemData . Parent )
{
if ( ElemData . Parent ! = null )
{
ElemData . Parent . ElementActor . RemoveChild ( ElemData . ElementActor ) ;
ElemData . Parent . ChildElements . Remove ( ElemData ) ;
}
SuperParent . ChildElements . Add ( ElemData ) ;
SuperParent . ElementActor . AddChild ( ElemData . ElementActor ) ;
}
}
}
}
}
} ;
2021-02-03 14:57:28 -04:00
public struct FPolymeshFace
{
public int V1 ;
public int V2 ;
public int V3 ;
public int MaterialIndex ;
public FPolymeshFace ( int InVertex1 , int InVertex2 , int InVertex3 , int InMaterialIndex = 0 )
{
V1 = InVertex1 ;
V2 = InVertex2 ;
V3 = InVertex3 ;
MaterialIndex = InMaterialIndex ;
}
}
public class FDatasmithPolymesh
{
public List < XYZ > Vertices = new List < XYZ > ( ) ;
public List < XYZ > Normals = new List < XYZ > ( ) ;
public List < FPolymeshFace > Faces = new List < FPolymeshFace > ( ) ;
public List < UV > UVs = new List < UV > ( ) ;
}
2020-09-24 00:43:27 -04:00
public class FBaseElementData
{
2021-02-03 14:57:28 -04:00
public ElementType BaseElementType ;
public FDatasmithPolymesh DatasmithPolymesh = null ;
public FDatasmithFacadeMeshElement DatasmithMeshElement = null ;
public FDatasmithFacadeActor ElementActor = null ;
public FDatasmithFacadeMetaData ElementMetaData = null ;
public FDocumentData DocumentData = null ;
public bool bOptimizeHierarchy = true ;
public bool bIsModified = true ;
public bool bAllowMeshInstancing = true ;
2022-03-17 02:24:47 -04:00
public bool bIsDecalElement = false ;
2020-09-24 00:43:27 -04:00
2021-03-18 15:20:03 -04:00
public Dictionary < string , int > MeshMaterialsMap = new Dictionary < string , int > ( ) ;
2021-05-25 02:43:26 -04:00
public Transform WorldTransform ;
2020-10-09 22:42:26 -04:00
public List < FBaseElementData > ChildElements = new List < FBaseElementData > ( ) ;
2020-09-24 00:43:27 -04:00
2020-10-09 22:42:26 -04:00
public FBaseElementData Parent = null ;
2020-09-24 00:43:27 -04:00
2022-11-25 11:37:12 -05:00
public bool bOwnedByParent = false ; // Lifetime is controlled by parent Element
2020-09-24 00:43:27 -04:00
public FBaseElementData (
ElementType InElementType , FDocumentData InDocumentData
)
{
BaseElementType = InElementType ;
DocumentData = InDocumentData ;
}
public FBaseElementData ( FDatasmithFacadeActor InElementActor , FDatasmithFacadeMetaData InElementMetaData , FDocumentData InDocumentData )
{
ElementActor = InElementActor ;
ElementMetaData = InElementMetaData ;
DocumentData = InDocumentData ;
}
2021-08-03 11:56:47 -04:00
void CopyActorData ( FDatasmithFacadeActor InFromActor , FDatasmithFacadeActor InToActor )
{
InToActor . SetLabel ( InFromActor . GetLabel ( ) ) ;
2022-03-28 16:55:59 -04:00
double X , Y , Z , W ;
2021-08-03 11:56:47 -04:00
InFromActor . GetTranslation ( out X , out Y , out Z ) ;
InToActor . SetTranslation ( X , Y , Z ) ;
InFromActor . GetScale ( out X , out Y , out Z ) ;
InToActor . SetScale ( X , Y , Z ) ;
InFromActor . GetRotation ( out X , out Y , out Z , out W ) ;
InToActor . SetRotation ( X , Y , Z , W ) ;
InToActor . SetLayer ( InFromActor . GetLayer ( ) ) ;
for ( int TagIndex = 0 ; TagIndex < InFromActor . GetTagsCount ( ) ; + + TagIndex )
2021-12-17 03:41:59 -05:00
{
2021-08-03 11:56:47 -04:00
InToActor . AddTag ( InFromActor . GetTag ( TagIndex ) ) ;
2021-12-17 03:41:59 -05:00
}
2021-08-03 11:56:47 -04:00
InToActor . SetIsComponent ( InFromActor . IsComponent ( ) ) ;
InToActor . SetVisibility ( InFromActor . GetVisibility ( ) ) ;
for ( int ChildIndex = 0 ; ChildIndex < InFromActor . GetChildrenCount ( ) ; + + ChildIndex )
2021-12-17 03:41:59 -05:00
{
2021-08-03 11:56:47 -04:00
InToActor . AddChild ( InFromActor . GetChild ( ChildIndex ) ) ;
2021-12-17 03:41:59 -05:00
}
2021-08-03 11:56:47 -04:00
ElementMetaData ? . SetAssociatedElement ( InToActor ) ;
}
2022-11-25 11:37:12 -05:00
// Return element when this 'ElementData' is associated with an Element
// todo: Probably worth separating FBaseElementData into different entity, calling it now "Element" data is confusing as it's also used to hold for non-Element datasmith actors
protected virtual Element GetElement ( )
{
return null ;
}
2020-10-22 19:19:16 -04:00
public void AddToScene ( FDatasmithFacadeScene InScene , FBaseElementData InParent , bool bInSkipChildren , bool bInForceAdd = false )
2020-09-24 00:43:27 -04:00
{
2022-11-25 11:37:12 -05:00
Element ThisElement = GetElement ( ) ;
2020-10-22 19:19:16 -04:00
if ( ! bInSkipChildren )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
foreach ( FBaseElementData CurrentChild in ChildElements )
{
2021-04-02 08:44:04 -04:00
// Stairs get special treatment: elements of stairs (strings, landings etc.) can be duplicated,
// meaning that the same element id can exist multiple times under the same parent.
bool bIsStairsElement = ( ThisElement ! = null ) & & ( ThisElement . GetType ( ) = = typeof ( Stairs ) ) ;
bool bIsInstance = ( ThisElement = = null ) ;
bool bForceAdd = ( bIsInstance & & bIsModified ) | | ( bIsStairsElement & & bIsModified ) ;
CurrentChild . AddToScene ( InScene , this , false , bForceAdd ) ;
2020-10-22 19:19:16 -04:00
}
2020-09-24 00:43:27 -04:00
}
2022-03-28 16:55:59 -04:00
bool bIsCached =
ThisElement ! = null & &
ThisElement . IsValidObject & &
2020-10-22 19:19:16 -04:00
( DocumentData . DirectLink ? . IsElementCached ( ThisElement ) ? ? false ) ;
2020-10-09 22:42:26 -04:00
2021-08-03 11:56:47 -04:00
// Check if actor type has changed for this element (f.e. static mesh actor -> regular actor),
// and re-created if needed.
if ( bIsCached & & bIsModified & & ElementActor ! = null )
{
FDatasmithFacadeActor CachedActor = DocumentData . DirectLink . GetCachedActor ( ElementActor . GetName ( ) ) ;
if ( CachedActor ! = null & & CachedActor . GetType ( ) ! = ElementActor . GetType ( ) )
{
InScene . RemoveActor ( CachedActor ) ;
DocumentData . DirectLink . CacheActorType ( ElementActor ) ;
bIsCached = false ;
}
}
2020-10-22 19:19:16 -04:00
if ( ( ! bIsCached & & bIsModified ) | | bInForceAdd )
2020-09-24 00:43:27 -04:00
{
if ( InParent = = null )
{
InScene . AddActor ( ElementActor ) ;
}
else
{
InParent . ElementActor . AddChild ( ElementActor ) ;
}
if ( ! DocumentData . bSkipMetadataExport & & ElementMetaData ! = null )
{
InScene . AddMetaData ( ElementMetaData ) ;
}
2020-10-09 22:42:26 -04:00
if ( ThisElement ! = null )
{
DocumentData . DirectLink ? . CacheElement ( DocumentData . CurrentDocument , ThisElement , this ) ;
}
2020-09-24 00:43:27 -04:00
}
bIsModified = false ;
}
2022-11-25 11:37:12 -05:00
// Returns true if element's actor is simple(plain actor without descendants)
2020-09-24 00:43:27 -04:00
public bool Optimize ( )
{
2022-11-25 11:37:12 -05:00
// Replace MeshActor without geometry by a dummy Actor
if ( ElementActor is FDatasmithFacadeActorMesh MeshActor & & MeshActor . GetMeshName ( ) . Length = = 0 )
2020-09-24 00:43:27 -04:00
{
2022-11-25 11:37:12 -05:00
ElementActor = new FDatasmithFacadeActor ( MeshActor . GetName ( ) ) ;
CopyActorData ( MeshActor , ElementActor ) ;
2020-09-24 00:43:27 -04:00
}
2022-11-25 11:37:12 -05:00
// Optimize and remove children whose actors are simple
List < FBaseElementData > SimpleChildren = ChildElements . Where ( Child = > Child . Optimize ( ) ) . ToList ( ) ; // Build a list of elements to remove after enumeration
foreach ( FBaseElementData Child in SimpleChildren )
2020-09-24 00:43:27 -04:00
{
ChildElements . Remove ( Child ) ;
}
2022-11-25 11:37:12 -05:00
bool bIsSimpleActor = ! ( ElementActor is FDatasmithFacadeActorMesh | | ElementActor is FDatasmithFacadeActorLight | | ElementActor is FDatasmithFacadeActorCamera ) ;
2020-09-24 00:43:27 -04:00
return bIsSimpleActor & & ( ChildElements . Count = = 0 ) & & bOptimizeHierarchy ;
}
public void UpdateMeshName ( )
{
FDatasmithFacadeActorMesh MeshActor = ElementActor as FDatasmithFacadeActorMesh ;
2021-08-03 11:56:47 -04:00
if ( MeshActor = = null & & DocumentData . DirectLink ! = null )
{
// We have valid mesh but the actor is not a mesh actor -- the type of element has changed (DirectLink).
MeshActor = new FDatasmithFacadeActorMesh ( ElementActor . GetName ( ) ) ;
CopyActorData ( ElementActor , MeshActor ) ;
ElementActor = MeshActor ;
}
2021-04-02 08:48:06 -04:00
MeshActor ? . SetMesh ( DatasmithMeshElement . GetName ( ) ) ;
2020-09-24 00:43:27 -04:00
bOptimizeHierarchy = false ;
}
}
2020-12-11 14:21:20 -04:00
public class FElementData : FBaseElementData
2020-09-24 00:43:27 -04:00
{
2020-10-09 22:42:26 -04:00
public Element CurrentElement = null ;
public Transform MeshPointsTransform = null ;
2020-09-24 00:43:27 -04:00
2022-11-28 19:14:00 -05:00
public Stack < FBaseElementData > InstanceDataStack = new Stack < FBaseElementData > ( ) ;
2020-09-24 00:43:27 -04:00
public FElementData (
Element InElement ,
Transform InWorldTransform ,
FDocumentData InDocumentData
)
: base ( InElement . Document . GetElement ( InElement . GetTypeId ( ) ) as ElementType , InDocumentData )
{
CurrentElement = InElement ;
}
2022-11-25 11:37:12 -05:00
protected override Element GetElement ( )
{
return CurrentElement ;
}
2020-09-24 00:43:27 -04:00
public void InitializePivotPlacement ( ref Transform InOutWorldTransform )
{
// If element has location, use it as a transform in order to have better pivot placement.
Transform PivotTransform = GetPivotTransform ( CurrentElement ) ;
if ( PivotTransform ! = null )
{
if ( ! InOutWorldTransform . IsIdentity )
{
InOutWorldTransform = InOutWorldTransform * PivotTransform ;
2022-03-28 16:55:59 -04:00
}
2020-09-24 00:43:27 -04:00
else
{
InOutWorldTransform = PivotTransform ;
}
if ( CurrentElement . GetType ( ) = = typeof ( Wall )
| | CurrentElement . GetType ( ) = = typeof ( ModelText )
| | CurrentElement . GetType ( ) . IsSubclassOf ( typeof ( MEPCurve ) )
2021-05-11 01:10:20 -04:00
| | CurrentElement . GetType ( ) = = typeof ( StructuralConnectionHandler )
| | CurrentElement . GetType ( ) = = typeof ( Floor )
| | CurrentElement . GetType ( ) = = typeof ( Ceiling )
| | CurrentElement . GetType ( ) = = typeof ( RoofBase )
| | CurrentElement . GetType ( ) . IsSubclassOf ( typeof ( RoofBase ) ) )
2020-09-24 00:43:27 -04:00
{
MeshPointsTransform = PivotTransform . Inverse ;
}
}
}
// Compute orthonormal basis, given the X vector.
static private void ComputeBasis ( XYZ BasisX , ref XYZ BasisY , ref XYZ BasisZ )
{
BasisY = XYZ . BasisZ . CrossProduct ( BasisX ) ;
if ( BasisY . GetLength ( ) < 0.0001 )
{
// BasisX is aligned with Z, use dot product to take direction in account
BasisY = BasisX . CrossProduct ( BasisX . DotProduct ( XYZ . BasisZ ) * XYZ . BasisX ) . Normalize ( ) ;
BasisZ = BasisX . CrossProduct ( BasisY ) . Normalize ( ) ;
}
else
{
BasisY = BasisY . Normalize ( ) ;
BasisZ = BasisX . CrossProduct ( BasisY ) . Normalize ( ) ;
}
}
private Transform GetPivotTransform ( Element InElement )
{
2020-10-22 19:19:16 -04:00
if ( ( InElement as FamilyInstance ) ! = null )
2020-09-24 00:43:27 -04:00
{
return null ;
}
XYZ Translation = null ;
XYZ BasisX = new XYZ ( ) ;
XYZ BasisY = new XYZ ( ) ;
XYZ BasisZ = new XYZ ( ) ;
// Get pivot translation
2020-10-22 19:19:16 -04:00
if ( InElement . GetType ( ) = = typeof ( Railing ) )
2020-09-24 00:43:27 -04:00
{
// Railings don't have valid location, so instead we need to get location from its path.
IList < Curve > Paths = ( InElement as Railing ) . GetPath ( ) ;
if ( Paths . Count > 0 & & Paths [ 0 ] . IsBound )
{
Translation = Paths [ 0 ] . GetEndPoint ( 0 ) ;
}
}
else if ( InElement . GetType ( ) = = typeof ( StructuralConnectionHandler ) )
{
Translation = ( InElement as StructuralConnectionHandler ) . GetOrigin ( ) ;
}
2022-03-28 16:55:59 -04:00
else if ( InElement . GetType ( ) = = typeof ( Floor )
| | InElement . GetType ( ) = = typeof ( Ceiling )
2021-05-11 01:10:20 -04:00
| | InElement . GetType ( ) = = typeof ( RoofBase )
| | InElement . GetType ( ) . IsSubclassOf ( typeof ( RoofBase ) ) )
{
BoundingBoxXYZ BoundingBox = InElement . get_BoundingBox ( InElement . Document . ActiveView ) ;
if ( BoundingBox ! = null )
{
Translation = BoundingBox . Min ;
}
}
2020-10-22 19:19:16 -04:00
else if ( InElement . Location ! = null )
{
if ( InElement . Location . GetType ( ) = = typeof ( LocationCurve ) )
{
LocationCurve CurveLocation = InElement . Location as LocationCurve ;
if ( CurveLocation . Curve ! = null & & CurveLocation . Curve . IsBound )
{
Translation = CurveLocation . Curve . GetEndPoint ( 0 ) ;
}
}
else if ( InElement . Location . GetType ( ) = = typeof ( LocationPoint ) )
{
Translation = ( InElement . Location as LocationPoint ) . Point ;
}
}
2020-09-24 00:43:27 -04:00
if ( Translation = = null )
{
return null ; // Cannot get valid translation
}
// Get pivot basis
if ( InElement . GetType ( ) = = typeof ( Wall ) )
{
// In rare cases, wall may not support orientation.
2022-03-28 16:55:59 -04:00
// If this happens, we need to use the direction of its Curve property and
2020-09-24 00:43:27 -04:00
// derive orientation from there.
try
{
BasisY = ( InElement as Wall ) . Orientation . Normalize ( ) ;
BasisX = BasisY . CrossProduct ( XYZ . BasisZ ) . Normalize ( ) ;
BasisZ = BasisX . CrossProduct ( BasisY ) . Normalize ( ) ;
2022-03-28 16:55:59 -04:00
}
2020-09-24 00:43:27 -04:00
catch
{
if ( InElement . Location . GetType ( ) = = typeof ( LocationCurve ) )
{
LocationCurve CurveLocation = InElement . Location as LocationCurve ;
if ( CurveLocation . Curve . GetType ( ) = = typeof ( Line ) )
{
BasisX = ( CurveLocation . Curve as Line ) . Direction ;
ComputeBasis ( BasisX , ref BasisY , ref BasisZ ) ;
2022-03-28 16:55:59 -04:00
}
2020-09-24 00:43:27 -04:00
else if ( CurveLocation . Curve . IsBound )
{
Transform Derivatives = CurveLocation . Curve . ComputeDerivatives ( 0f , true ) ;
BasisX = Derivatives . BasisX . Normalize ( ) ;
BasisY = Derivatives . BasisY . Normalize ( ) ;
BasisZ = Derivatives . BasisZ . Normalize ( ) ;
2022-03-28 16:55:59 -04:00
}
else
2020-09-24 00:43:27 -04:00
{
BasisX = XYZ . BasisX ;
BasisY = XYZ . BasisY ;
BasisZ = XYZ . BasisZ ;
}
}
}
}
else if ( InElement . GetType ( ) = = typeof ( Railing ) )
{
IList < Curve > Paths = ( InElement as Railing ) . GetPath ( ) ;
if ( Paths . Count > 0 )
{
Curve FirstPath = Paths [ 0 ] ;
if ( FirstPath . GetType ( ) = = typeof ( Line ) )
{
BasisX = ( FirstPath as Line ) . Direction . Normalize ( ) ;
ComputeBasis ( BasisX , ref BasisY , ref BasisZ ) ;
}
else if ( FirstPath . GetType ( ) = = typeof ( Arc ) & & FirstPath . IsBound )
{
Transform Derivatives = ( FirstPath as Arc ) . ComputeDerivatives ( 0f , true ) ;
BasisX = Derivatives . BasisX . Normalize ( ) ;
BasisY = Derivatives . BasisY . Normalize ( ) ;
BasisZ = Derivatives . BasisZ . Normalize ( ) ;
}
}
}
else if ( InElement . GetType ( ) = = typeof ( StructuralConnectionHandler ) )
{
BasisX = XYZ . BasisX ;
BasisY = XYZ . BasisY ;
BasisZ = XYZ . BasisZ ;
}
else if ( InElement . GetType ( ) = = typeof ( ModelText ) )
{
// Model text has no direction information!
BasisX = XYZ . BasisX ;
BasisY = XYZ . BasisY ;
BasisZ = XYZ . BasisZ ;
}
else if ( InElement . GetType ( ) = = typeof ( FlexDuct ) )
{
BasisX = ( InElement as FlexDuct ) . StartTangent ;
ComputeBasis ( BasisX , ref BasisY , ref BasisZ ) ;
}
else if ( InElement . GetType ( ) = = typeof ( FlexPipe ) )
{
BasisX = ( InElement as FlexPipe ) . StartTangent ;
ComputeBasis ( BasisX , ref BasisY , ref BasisZ ) ;
}
2022-03-28 16:55:59 -04:00
else if ( InElement . GetType ( ) = = typeof ( Floor )
2021-05-11 01:10:20 -04:00
| | InElement . GetType ( ) = = typeof ( Ceiling )
| | InElement . GetType ( ) = = typeof ( RoofBase )
| | InElement . GetType ( ) . IsSubclassOf ( typeof ( RoofBase ) ) )
{
BasisX = XYZ . BasisX ;
BasisY = XYZ . BasisY ;
BasisZ = XYZ . BasisZ ;
}
2020-09-24 00:43:27 -04:00
else if ( InElement . Location . GetType ( ) = = typeof ( LocationCurve ) )
{
LocationCurve CurveLocation = InElement . Location as LocationCurve ;
if ( CurveLocation . Curve . GetType ( ) = = typeof ( Line ) )
{
BasisX = ( CurveLocation . Curve as Line ) . Direction ;
ComputeBasis ( BasisX , ref BasisY , ref BasisZ ) ;
}
else if ( CurveLocation . Curve . IsBound )
{
Transform Derivatives = CurveLocation . Curve . ComputeDerivatives ( 0f , true ) ;
BasisX = Derivatives . BasisX . Normalize ( ) ;
BasisY = Derivatives . BasisY . Normalize ( ) ;
BasisZ = Derivatives . BasisZ . Normalize ( ) ;
}
else
{
return null ;
}
}
else
{
return null ; // Cannot get valid basis
}
Transform PivotTransform = Transform . CreateTranslation ( Translation ) ;
PivotTransform . BasisX = BasisX ;
PivotTransform . BasisY = BasisY ;
PivotTransform . BasisZ = BasisZ ;
return PivotTransform ;
}
2021-01-08 19:56:07 -04:00
public FBaseElementData PushInstance (
2020-09-24 00:43:27 -04:00
ElementType InInstanceType ,
2021-02-03 14:57:28 -04:00
Transform InWorldTransform ,
bool bInAllowMeshInstancing
2020-09-24 00:43:27 -04:00
)
{
FBaseElementData InstanceData = new FBaseElementData ( InInstanceType , DocumentData ) ;
2022-11-25 11:37:12 -05:00
InstanceData . bOwnedByParent = true ;
2022-09-15 17:57:03 -04:00
FamilyInstance CurrentFamilyInstance = CurrentElement as FamilyInstance ;
if ( CurrentFamilyInstance ! = null & & CurrentFamilyInstance . HasModifiedGeometry ( ) )
{
//In case the FamilyInstance has a modified geometry, then don't instantiate the original mesh,
//the onPolymesh will provide the customized geometry instead.
bInAllowMeshInstancing = false ;
}
2021-02-03 14:57:28 -04:00
InstanceData . bAllowMeshInstancing = bInAllowMeshInstancing ;
2020-09-24 00:43:27 -04:00
InstanceDataStack . Push ( InstanceData ) ;
InitializeElement ( InWorldTransform , InstanceData ) ;
// The Datasmith instance actor is a component in the hierarchy.
InstanceData . ElementActor . SetIsComponent ( true ) ;
2021-01-08 19:56:07 -04:00
return InstanceData ;
2020-09-24 00:43:27 -04:00
}
public FBaseElementData PopInstance ( )
{
FBaseElementData Instance = InstanceDataStack . Pop ( ) ;
Instance . bIsModified = true ;
return Instance ;
}
2021-02-05 11:30:21 -04:00
public FDatasmithFacadeMeshElement GetCurrentMeshElement ( )
{
2021-03-05 19:27:14 -04:00
if ( InstanceDataStack . Count > 0 )
{
return InstanceDataStack . Peek ( ) . DatasmithMeshElement ;
}
2021-02-05 11:30:21 -04:00
return DatasmithMeshElement ;
}
2020-09-24 00:43:27 -04:00
public void AddLightActor (
Transform InWorldTransform ,
Asset InLightAsset
)
{
// Create a new Datasmith light actor.
// Hash the Datasmith light actor name to shorten it.
2021-10-25 20:05:28 -04:00
string HashedActorName = FDatasmithFacadeElement . GetStringHash ( "L:" + GetActorName ( true ) ) ;
2020-09-24 00:43:27 -04:00
FDatasmithFacadeActorLight LightActor = FDatasmithRevitLight . CreateLightActor ( CurrentElement , HashedActorName ) ;
LightActor . SetLabel ( GetActorLabel ( ) ) ;
// Set the world transform of the Datasmith light actor.
2022-03-16 07:58:12 -04:00
DocumentData . SetActorTransform ( InWorldTransform , LightActor ) ;
2020-09-24 00:43:27 -04:00
// Set the base properties of the Datasmith light actor.
string LayerName = Category . GetCategory ( CurrentElement . Document , BuiltInCategory . OST_LightingFixtureSource ) ? . Name ? ? "Light Sources" ;
SetActorProperties ( LayerName , LightActor ) ;
2021-09-09 03:37:24 -04:00
FDatasmithFacadeMetaData LightMetaData = null ;
if ( ! DocumentData . bSkipMetadataExport )
{
LightMetaData = GetActorMetaData ( LightActor ) ;
}
2020-09-24 00:43:27 -04:00
// Set the Datasmith light actor layer to its predefined name.
string CategoryName = Category . GetCategory ( CurrentElement . Document , BuiltInCategory . OST_LightingFixtureSource ) ? . Name ? ? "Light Sources" ;
LightActor . SetLayer ( CategoryName ) ;
// Set the specific properties of the Datasmith light actor.
FDatasmithRevitLight . SetLightProperties ( InLightAsset , CurrentElement , LightActor ) ;
// Add the light actor to the Datasmith actor hierarchy.
2022-11-25 11:37:12 -05:00
AddChildActor ( LightActor , LightMetaData , false , true ) ;
2020-09-24 00:43:27 -04:00
}
2021-02-03 14:57:28 -04:00
public bool AddRPCActor (
2020-09-24 00:43:27 -04:00
Transform InWorldTransform ,
Asset InRPCAsset ,
2021-02-03 14:57:28 -04:00
FMaterialData InMaterialData ,
out FDatasmithFacadeMesh OutDatasmithMesh ,
out FDatasmithFacadeMeshElement OutDatasmithMeshElement
2020-09-24 00:43:27 -04:00
)
{
// Create a new Datasmith RPC mesh.
// Hash the Datasmith RPC mesh name to shorten it.
2021-10-25 20:05:28 -04:00
string HashedName = FDatasmithFacadeElement . GetStringHash ( "RPCM:" + GetActorName ( false ) ) ;
2021-02-03 14:57:28 -04:00
FDatasmithFacadeMesh RPCMesh = new FDatasmithFacadeMesh ( ) ;
RPCMesh . SetName ( HashedName ) ;
2020-09-24 00:43:27 -04:00
Transform AffineTransform = Transform . Identity ;
LocationPoint RPCLocationPoint = CurrentElement . Location as LocationPoint ;
if ( RPCLocationPoint ! = null )
{
if ( RPCLocationPoint . Rotation ! = 0.0 )
{
AffineTransform = AffineTransform . Multiply ( Transform . CreateRotation ( XYZ . BasisZ , - RPCLocationPoint . Rotation ) ) ;
AffineTransform = AffineTransform . Multiply ( Transform . CreateTranslation ( RPCLocationPoint . Point . Negate ( ) ) ) ;
}
else
{
AffineTransform = Transform . CreateTranslation ( RPCLocationPoint . Point . Negate ( ) ) ;
}
}
2021-02-03 14:57:28 -04:00
int TotalVertexCount = 0 ;
int TotalTriangleCount = 0 ;
List < Mesh > GeometryObjectList = new List < Mesh > ( ) ;
foreach ( GeometryObject RPCGeometryObject in CurrentElement . get_Geometry ( new Options ( ) ) )
2020-09-24 00:43:27 -04:00
{
GeometryInstance RPCGeometryInstance = RPCGeometryObject as GeometryInstance ;
2021-02-03 14:57:28 -04:00
if ( RPCGeometryInstance = = null )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
continue ;
}
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
foreach ( GeometryObject RPCInstanceGeometryObject in RPCGeometryInstance . GetInstanceGeometry ( ) )
{
Mesh RPCInstanceGeometryMesh = RPCInstanceGeometryObject as Mesh ;
if ( RPCInstanceGeometryMesh = = null | | RPCInstanceGeometryMesh . NumTriangles < 1 )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
continue ;
}
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
TotalVertexCount + = RPCInstanceGeometryMesh . Vertices . Count ;
TotalTriangleCount + = RPCInstanceGeometryMesh . NumTriangles ;
GeometryObjectList . Add ( RPCInstanceGeometryMesh ) ;
}
}
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
RPCMesh . SetVerticesCount ( TotalVertexCount ) ;
RPCMesh . SetFacesCount ( TotalTriangleCount * 2 ) ; // Triangles are added twice for RPC meshes: CW & CCW
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
int MeshMaterialIndex = 0 ;
int VertexIndexOffset = 0 ;
int TriangleIndexOffset = 0 ;
foreach ( Mesh RPCInstanceGeometryMesh in GeometryObjectList )
{
// RPC geometry does not have normals nor UVs available through the Revit Mesh interface.
int VertexCount = RPCInstanceGeometryMesh . Vertices . Count ;
int TriangleCount = RPCInstanceGeometryMesh . NumTriangles ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
// Add the RPC geometry vertices to the Datasmith RPC mesh.
for ( int VertexIndex = 0 ; VertexIndex < RPCInstanceGeometryMesh . Vertices . Count ; + + VertexIndex )
{
XYZ PositionedVertex = AffineTransform . OfPoint ( RPCInstanceGeometryMesh . Vertices [ VertexIndex ] ) ;
RPCMesh . SetVertex ( VertexIndexOffset + VertexIndex , ( float ) PositionedVertex . X , ( float ) PositionedVertex . Y , ( float ) PositionedVertex . Z ) ;
}
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
// Add the RPC geometry triangles to the Datasmith RPC mesh.
for ( int TriangleNo = 0 , BaseTriangleIndex = 0 ; TriangleNo < TriangleCount ; TriangleNo + + , BaseTriangleIndex + = 2 )
{
MeshTriangle Triangle = RPCInstanceGeometryMesh . get_Triangle ( TriangleNo ) ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
try
{
int Index0 = VertexIndexOffset + Convert . ToInt32 ( Triangle . get_Index ( 0 ) ) ;
int Index1 = VertexIndexOffset + Convert . ToInt32 ( Triangle . get_Index ( 1 ) ) ;
int Index2 = VertexIndexOffset + Convert . ToInt32 ( Triangle . get_Index ( 2 ) ) ;
// Add triangles for both the front and back faces.
RPCMesh . SetFace ( TriangleIndexOffset + BaseTriangleIndex , Index0 , Index1 , Index2 , MeshMaterialIndex ) ;
RPCMesh . SetFace ( TriangleIndexOffset + BaseTriangleIndex + 1 , Index2 , Index1 , Index0 , MeshMaterialIndex ) ;
}
catch ( OverflowException )
{
continue ;
2020-09-24 00:43:27 -04:00
}
}
2021-02-03 14:57:28 -04:00
VertexIndexOffset + = VertexCount ;
TriangleIndexOffset + = TriangleCount * 2 ;
2020-09-24 00:43:27 -04:00
}
// Create a new Datasmith RPC mesh actor.
// Hash the Datasmith RPC mesh actor name to shorten it.
2021-10-25 20:05:28 -04:00
string HashedActorName = FDatasmithFacadeElement . GetStringHash ( "RPC:" + GetActorName ( true ) ) ;
2020-09-24 00:43:27 -04:00
FDatasmithFacadeActor FacadeActor ;
2021-02-03 14:57:28 -04:00
if ( RPCMesh . GetVerticesCount ( ) > 0 & & RPCMesh . GetFacesCount ( ) > 0 )
2020-09-24 00:43:27 -04:00
{
FDatasmithFacadeActorMesh RPCMeshActor = new FDatasmithFacadeActorMesh ( HashedActorName ) ;
RPCMeshActor . SetMesh ( RPCMesh . GetName ( ) ) ;
FacadeActor = RPCMeshActor ;
2022-03-28 16:55:59 -04:00
2021-02-03 14:57:28 -04:00
OutDatasmithMesh = RPCMesh ;
OutDatasmithMeshElement = new FDatasmithFacadeMeshElement ( HashedName ) ;
OutDatasmithMeshElement . SetLabel ( GetActorLabel ( ) ) ;
2022-08-25 15:29:37 -04:00
OutDatasmithMeshElement . SetMaterial ( InMaterialData . MaterialInstance . GetName ( ) , MeshMaterialIndex ) ;
2020-09-24 00:43:27 -04:00
}
else
{
//Create an empty actor instead of a static mesh actor with no mesh.
FacadeActor = new FDatasmithFacadeActor ( HashedActorName ) ;
2021-02-03 14:57:28 -04:00
OutDatasmithMesh = null ;
OutDatasmithMeshElement = null ;
2020-09-24 00:43:27 -04:00
}
FacadeActor . SetLabel ( GetActorLabel ( ) ) ;
// Set the world transform of the Datasmith RPC mesh actor.
2022-03-16 07:58:12 -04:00
DocumentData . SetActorTransform ( InWorldTransform , FacadeActor ) ;
2020-09-24 00:43:27 -04:00
// Set the base properties of the Datasmith RPC mesh actor.
string LayerName = GetCategoryName ( ) ;
SetActorProperties ( LayerName , FacadeActor ) ;
// Add a Revit element RPC tag to the Datasmith RPC mesh actor.
FacadeActor . AddTag ( "Revit.Element.RPC" ) ;
// Add some Revit element RPC metadata to the Datasmith RPC mesh actor.
AssetProperty RPCTypeId = InRPCAsset . FindByName ( "RPCTypeId" ) ;
AssetProperty RPCFilePath = InRPCAsset . FindByName ( "RPCFilePath" ) ;
FDatasmithFacadeMetaData ElementMetaData = new FDatasmithFacadeMetaData ( FacadeActor . GetName ( ) + "_DATA" ) ;
ElementMetaData . SetLabel ( FacadeActor . GetLabel ( ) ) ;
ElementMetaData . SetAssociatedElement ( FacadeActor ) ;
if ( RPCTypeId ! = null )
{
ElementMetaData . AddPropertyString ( "Type*RPCTypeId" , ( RPCTypeId as AssetPropertyString ) . Value ) ;
}
if ( RPCFilePath ! = null )
{
ElementMetaData . AddPropertyString ( "Type*RPCFilePath" , ( RPCFilePath as AssetPropertyString ) . Value ) ;
}
// Add the RPC mesh actor to the Datasmith actor hierarchy.
2022-11-25 11:37:12 -05:00
AddChildActor ( FacadeActor , ElementMetaData , false , true ) ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
return OutDatasmithMesh ! = null ;
2020-09-24 00:43:27 -04:00
}
public void AddChildActor (
FBaseElementData InChildActor
)
{
FBaseElementData ParentElement = ( InstanceDataStack . Count = = 0 ) ? this : InstanceDataStack . Peek ( ) ;
ParentElement . ChildElements . Add ( InChildActor ) ;
InChildActor . Parent = ParentElement ;
}
public void AddChildActor (
2022-03-28 16:55:59 -04:00
FDatasmithFacadeActor ChildActor ,
FDatasmithFacadeMetaData MetaData ,
2022-11-25 11:37:12 -05:00
bool bOptimizeHierarchy ,
bool bOwned // Make its lifetime controlled by this ElementData
2020-09-24 00:43:27 -04:00
)
{
FBaseElementData ElementData = new FBaseElementData ( ChildActor , MetaData , DocumentData ) ;
ElementData . bOptimizeHierarchy = bOptimizeHierarchy ;
FBaseElementData Parent = ( InstanceDataStack . Count = = 0 ) ? this : InstanceDataStack . Peek ( ) ;
Parent . ChildElements . Add ( ElementData ) ;
ElementData . Parent = Parent ;
2022-11-25 11:37:12 -05:00
ElementData . bOwnedByParent = bOwned ;
2020-09-24 00:43:27 -04:00
}
public void InitializeElement (
Transform InWorldTransform ,
FBaseElementData InElement
)
{
2021-05-25 02:43:26 -04:00
InElement . WorldTransform = InWorldTransform ;
2020-09-24 00:43:27 -04:00
// Create a new Datasmith mesh.
// Hash the Datasmith mesh name to shorten it.
2020-10-29 13:38:15 -04:00
string HashedMeshName = FDatasmithFacadeElement . GetStringHash ( "M:" + GetMeshName ( ) ) ;
2021-02-03 14:57:28 -04:00
InElement . DatasmithPolymesh = new FDatasmithPolymesh ( ) ;
InElement . DatasmithMeshElement = new FDatasmithFacadeMeshElement ( HashedMeshName ) ;
InElement . DatasmithMeshElement . SetLabel ( GetActorLabel ( ) ) ;
2020-09-24 00:43:27 -04:00
2021-01-08 19:56:07 -04:00
if ( InElement . ElementActor = = null )
2020-09-24 00:43:27 -04:00
{
// Create a new Datasmith mesh actor.
// Hash the Datasmith mesh actor name to shorten it.
2021-10-25 20:05:28 -04:00
string HashedActorName = FDatasmithFacadeElement . GetStringHash ( "A:" + GetActorName ( true ) ) ;
2022-03-17 02:24:47 -04:00
2022-03-24 08:00:44 -04:00
if ( BaseElementType ! = null & & BaseElementType . FamilyName = = "Decal" )
2022-03-17 02:24:47 -04:00
{
bOptimizeHierarchy = false ;
bIsDecalElement = true ;
InElement . ElementActor = new FDatasmithFacadeActorDecal ( HashedActorName ) ;
}
else
{
InElement . ElementActor = new FDatasmithFacadeActorMesh ( HashedActorName ) ;
}
2022-05-09 03:25:49 -04:00
InElement . ElementActor . SetLabel ( GetActorLabel ( ) ) ;
2020-09-24 00:43:27 -04:00
}
// Set the world transform of the Datasmith mesh actor.
2022-03-16 07:58:12 -04:00
DocumentData . SetActorTransform ( InWorldTransform , InElement . ElementActor ) ;
2020-09-24 00:43:27 -04:00
// Set the base properties of the Datasmith mesh actor.
string LayerName = GetCategoryName ( ) ;
SetActorProperties ( LayerName , InElement . ElementActor ) ;
if ( ! DocumentData . bSkipMetadataExport )
{
InElement . ElementMetaData = GetActorMetaData ( InElement . ElementActor ) ;
}
}
public string GetCategoryName ( )
{
return BaseElementType ? . Category ? . Name ? ? CurrentElement . Category ? . Name ;
}
public bool IgnoreElementGeometry ( )
{
// Ignore elements that have unwanted geometry, such as level symbols.
return ( BaseElementType as LevelType ) ! = null ;
}
2021-02-03 14:57:28 -04:00
public FDatasmithPolymesh GetCurrentPolymesh ( )
2020-09-24 00:43:27 -04:00
{
if ( InstanceDataStack . Count = = 0 )
{
2021-02-03 14:57:28 -04:00
return DatasmithPolymesh ;
2020-09-24 00:43:27 -04:00
}
else
{
2021-02-03 14:57:28 -04:00
return InstanceDataStack . Peek ( ) . DatasmithPolymesh ;
2020-09-24 00:43:27 -04:00
}
}
2021-02-03 14:57:28 -04:00
public FBaseElementData PeekInstance ( )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
return InstanceDataStack . Count > 0 ? InstanceDataStack . Peek ( ) : null ;
2020-09-24 00:43:27 -04:00
}
public FBaseElementData GetCurrentActor ( )
{
if ( InstanceDataStack . Count = = 0 )
{
return this ;
}
else
{
return InstanceDataStack . Peek ( ) ;
}
}
public void Log (
FDatasmithFacadeLog InDebugLog ,
string InLinePrefix ,
int InLineIndentation
)
{
if ( InDebugLog ! = null )
{
if ( InLineIndentation < 0 )
{
InDebugLog . LessIndentation ( ) ;
}
Element SourceElement = ( InstanceDataStack . Count = = 0 ) ? CurrentElement : InstanceDataStack . Peek ( ) . BaseElementType ;
InDebugLog . AddLine ( $"{InLinePrefix} {SourceElement.Id.IntegerValue} '{SourceElement.Name}' {SourceElement.GetType()}: '{GetActorLabel()}'" ) ;
if ( InLineIndentation > 0 )
{
InDebugLog . MoreIndentation ( ) ;
}
}
}
2021-10-25 20:05:28 -04:00
private string GetActorName ( bool bEnsureUnique )
2020-09-24 00:43:27 -04:00
{
2022-11-28 19:14:00 -05:00
return DocumentData . GetActorName ( this ) ;
2020-10-29 13:38:15 -04:00
}
2022-11-28 19:14:00 -05:00
public string GenerateUniqueInstanceNameSuffix ( )
2021-02-03 14:57:28 -04:00
{
2022-03-28 16:55:59 -04:00
// GenerateUniqueInstanceName is being called when generating a name for instance.
// After the call, the intance is added as a child to its parent.
2021-02-03 14:57:28 -04:00
// Next time the method gets called for the next instance, ChildElements.Count will be different/incremented.
2022-03-28 16:55:59 -04:00
// To add uniqueness to the generated name, we construct a string with child counts from
2021-02-03 14:57:28 -04:00
// current parent instance, up to the root:
2022-11-28 19:14:00 -05:00
// Elem->Instance->Instance->Instance can produce something like: "1:5:3" for example.
2022-03-28 16:55:59 -04:00
// However, this is not enough because elsewhere we might encounter the same sequence in terms of child counts,
2021-02-03 14:57:28 -04:00
// but adding the CurrentElement unique id ensures we get unique name string in the end.
StringBuilder ChildCounts = new StringBuilder ( ) ;
for ( int ElemIndex = 1 ; ElemIndex < InstanceDataStack . Count ; + + ElemIndex )
{
FBaseElementData Elem = InstanceDataStack . ElementAt ( ElemIndex ) ;
ChildCounts . AppendFormat ( ":{0}" , Elem . ChildElements . Count ) ;
}
// Add child count for the root element (parent of all instances)
ChildCounts . AppendFormat ( ":{0}" , ChildElements . Count ) ;
2022-11-28 19:14:00 -05:00
return $"I:{ChildCounts}" ;
2021-02-03 14:57:28 -04:00
}
2020-10-29 13:38:15 -04:00
private string GetMeshName ( )
{
if ( InstanceDataStack . Count = = 0 )
{
2021-11-07 23:43:01 -05:00
return $"{DocumentData.DocumentId}:{CurrentElement.UniqueId}" ;
2020-10-29 13:38:15 -04:00
}
2021-02-03 14:57:28 -04:00
2022-11-28 19:14:00 -05:00
FBaseElementData Instance = InstanceDataStack . Peek ( ) ;
if ( Instance . bAllowMeshInstancing )
{
// Generate instanced mesh name using Instance geometry UniqueId
return $"{DocumentData.DocumentId}:{Instance.BaseElementType.UniqueId}" ;
2020-09-24 00:43:27 -04:00
}
2022-11-28 19:14:00 -05:00
return GetActorName ( true ) ; // Use unique element's actor name to base mesh name on
2020-09-24 00:43:27 -04:00
}
private string GetActorLabel ( )
{
string CategoryName = GetCategoryName ( ) ;
string FamilyName = BaseElementType ? . FamilyName ;
string TypeName = BaseElementType ? . Name ;
string InstanceName = ( InstanceDataStack . Count > 1 ) ? InstanceDataStack . Peek ( ) . BaseElementType ? . Name : null ;
string ActorLabel = "" ;
if ( CurrentElement as Level ! = null )
{
ActorLabel + = string . IsNullOrEmpty ( FamilyName ) ? "" : FamilyName + "*" ;
ActorLabel + = string . IsNullOrEmpty ( TypeName ) ? "" : TypeName + "*" ;
ActorLabel + = CurrentElement . Name ;
}
else
{
ActorLabel + = string . IsNullOrEmpty ( CategoryName ) ? "" : CategoryName + "*" ;
ActorLabel + = string . IsNullOrEmpty ( FamilyName ) ? "" : FamilyName + "*" ;
ActorLabel + = string . IsNullOrEmpty ( TypeName ) ? CurrentElement . Name : TypeName ;
ActorLabel + = string . IsNullOrEmpty ( InstanceName ) ? "" : "*" + InstanceName ;
}
2023-02-03 11:00:25 -05:00
DocumentData . Context . LogDebug ( $"GetActorLabel(CategoryName='{CategoryName}', FamilyName='{FamilyName}', TypeName='{TypeName}', InstanceName='{InstanceName}') -> '{ActorLabel}'" ) ;
2020-09-24 00:43:27 -04:00
return ActorLabel ;
}
private void SetActorProperties (
string InLayerName ,
FDatasmithFacadeActor IOActor
)
{
// Set the Datasmith actor layer to the element type category name.
IOActor . SetLayer ( InLayerName ) ;
// Add the Revit element ID and Unique ID tags to the Datasmith actor.
IOActor . AddTag ( $"Revit.Element.Id.{CurrentElement.Id.IntegerValue}" ) ;
IOActor . AddTag ( $"Revit.Element.UniqueId.{CurrentElement.UniqueId}" ) ;
// For an hosted Revit family instance, add the host ID, Unique ID and Mirrored/Flipped flags as tags to the Datasmith actor.
FamilyInstance CurrentFamilyInstance = CurrentElement as FamilyInstance ;
if ( CurrentFamilyInstance ! = null )
{
IOActor . AddTag ( $"Revit.DB.FamilyInstance.Mirrored.{CurrentFamilyInstance.Mirrored}" ) ;
IOActor . AddTag ( $"Revit.DB.FamilyInstance.HandFlipped.{CurrentFamilyInstance.HandFlipped}" ) ;
IOActor . AddTag ( $"Revit.DB.FamilyInstance.FaceFlipped.{CurrentFamilyInstance.FacingFlipped}" ) ;
if ( CurrentFamilyInstance . Host ! = null )
{
IOActor . AddTag ( $"Revit.Host.Id.{CurrentFamilyInstance.Host.Id.IntegerValue}" ) ;
IOActor . AddTag ( $"Revit.Host.UniqueId.{CurrentFamilyInstance.Host.UniqueId}" ) ;
}
}
}
private FDatasmithFacadeMetaData GetActorMetaData ( FDatasmithFacadeActor IOActor )
{
FDatasmithFacadeMetaData ElementMetaData = new FDatasmithFacadeMetaData ( IOActor . GetName ( ) + "_DATA" ) ;
ElementMetaData . SetLabel ( IOActor . GetLabel ( ) ) ;
ElementMetaData . SetAssociatedElement ( IOActor ) ;
// Add the Revit element category name metadata to the Datasmith actor.
string CategoryName = GetCategoryName ( ) ;
if ( ! string . IsNullOrEmpty ( CategoryName ) )
{
ElementMetaData . AddPropertyString ( "Element*Category" , CategoryName ) ;
}
// Add the Revit element family name metadata to the Datasmith actor.
string FamilyName = BaseElementType ? . FamilyName ;
if ( ! string . IsNullOrEmpty ( FamilyName ) )
{
ElementMetaData . AddPropertyString ( "Element*Family" , FamilyName ) ;
}
// Add the Revit element type name metadata to the Datasmith actor.
string TypeName = BaseElementType ? . Name ;
if ( ! string . IsNullOrEmpty ( TypeName ) )
{
ElementMetaData . AddPropertyString ( "Element*Type" , TypeName ) ;
}
// Add Revit element metadata to the Datasmith actor.
2022-05-19 03:35:26 -04:00
FUtils . AddActorMetadata ( CurrentElement , "Element*" , ElementMetaData , DocumentData . CurrentSettings ) ;
2020-09-24 00:43:27 -04:00
if ( BaseElementType ! = null )
{
// Add Revit element type metadata to the Datasmith actor.
2022-05-19 03:35:26 -04:00
FUtils . AddActorMetadata ( BaseElementType , "Type*" , ElementMetaData , DocumentData . CurrentSettings ) ;
2020-09-24 00:43:27 -04:00
}
return ElementMetaData ;
}
}
2022-11-28 19:14:00 -05:00
private string GetActorName ( FElementData InElementData )
{
// GetActorName should be called only for the current processed element
// so InElementData serves internally the only purpose to validate the call
Debug . Assert ( InElementData = = ElementDataStack . Peek ( ) ) ;
string ActorName = Context . GetActorName ( this ) ;
if ( InElementData . InstanceDataStack . Count = = 0 )
{
return ActorName ;
}
// Instance actor name (when InstanceDataStack is not empty), using current element's encountered instance count as instance's identification
// (to to OnElementBegin could follow multiple OnInstanceBegin/End calls, meaning an element can have multiple instances nested right under it)
// And each instance should have a separate unique name name identified by element path from root plus instances index(calling GetActorName for next
// instance in the same element will have InstanceDataStack.Count increased)
return ActorName + InElementData . GenerateUniqueInstanceNameSuffix ( ) ;
}
2022-03-28 16:55:59 -04:00
public Dictionary < string , Tuple < FDatasmithFacadeMeshElement , Task < bool > > >
2021-02-03 14:57:28 -04:00
MeshMap = new Dictionary < string , Tuple < FDatasmithFacadeMeshElement , Task < bool > > > ( ) ;
2020-10-09 22:42:26 -04:00
public Dictionary < ElementId , FBaseElementData > ActorMap = new Dictionary < ElementId , FDocumentData . FBaseElementData > ( ) ;
2020-12-11 14:21:20 -04:00
public Dictionary < string , FMaterialData > MaterialDataMap = null ;
2021-01-08 19:56:07 -04:00
public Dictionary < string , FMaterialData > NewMaterialsMap = new Dictionary < string , FMaterialData > ( ) ;
2020-09-24 00:43:27 -04:00
2022-03-17 02:24:47 -04:00
public Dictionary < ElementId , FElementData > DecalElementsMap = new Dictionary < ElementId , FElementData > ( ) ;
public Dictionary < ElementId , FDecalMaterial > DecalMaterialsMap = new Dictionary < ElementId , FDecalMaterial > ( ) ;
2020-10-09 22:42:26 -04:00
private Stack < FElementData > ElementDataStack = new Stack < FElementData > ( ) ;
2020-12-11 14:21:20 -04:00
private string CurrentMaterialName = null ;
2020-10-09 22:42:26 -04:00
private List < string > MessageList = null ;
2020-09-24 00:43:27 -04:00
2022-05-19 03:35:26 -04:00
private FSettings CurrentSettings = null ;
2022-03-16 07:58:12 -04:00
// Apply world offset to elements
public FSettings . EInsertionPoint InsertionPoint { get ; set ; } = FSettings . EInsertionPoint . Default ;
private XYZ ProjectSurveyPoint = null ;
private XYZ ProjectBasePoint = null ;
2022-11-28 19:14:00 -05:00
public FDatasmithRevitExportContext Context = null ;
2022-03-16 07:58:12 -04:00
public string DocumentId { get ; private set ; } = "" ;
2021-11-07 23:43:01 -05:00
2020-10-09 22:42:26 -04:00
public bool bSkipMetadataExport { get ; private set ; } = false ;
public Document CurrentDocument { get ; private set ; } = null ;
public FDirectLink DirectLink { get ; private set ; } = null ;
2020-09-24 00:43:27 -04:00
2022-09-20 21:29:32 -04:00
public Outline SectionBoxOutline = null ;
private OrientatedBoundingBox SectionBox = null ;
2021-02-03 14:57:28 -04:00
2020-09-24 00:43:27 -04:00
public FDocumentData (
Document InDocument ,
2022-05-19 03:35:26 -04:00
FSettings InSettings ,
2020-09-24 00:43:27 -04:00
ref List < string > InMessageList ,
2021-11-07 23:43:01 -05:00
FDirectLink InDirectLink ,
2022-11-28 19:14:00 -05:00
string InDocumentId
2020-09-24 00:43:27 -04:00
)
{
2022-11-28 19:14:00 -05:00
2022-05-19 03:35:26 -04:00
CurrentSettings = InSettings ;
2020-10-09 22:42:26 -04:00
DirectLink = InDirectLink ;
2020-09-24 00:43:27 -04:00
CurrentDocument = InDocument ;
MessageList = InMessageList ;
2020-10-09 22:42:26 -04:00
// With DirectLink, we delay export of metadata for a faster initial export.
bSkipMetadataExport = ( DirectLink ! = null ) ;
2020-12-11 14:21:20 -04:00
2022-11-28 19:14:00 -05:00
DocumentId = InDocumentId ;
}
public void Reset ( FDatasmithRevitExportContext InContext )
{
Context = InContext ; // Make currently processing document aware of the current export context (currently context is recreated on each Sync)
MeshMap = new Dictionary < string , Tuple < FDatasmithFacadeMeshElement , Task < bool > > > ( ) ;
ActorMap = new Dictionary < ElementId , FBaseElementData > ( ) ;
MaterialDataMap = null ;
NewMaterialsMap = new Dictionary < string , FMaterialData > ( ) ;
DecalElementsMap = new Dictionary < ElementId , FElementData > ( ) ;
DecalMaterialsMap = new Dictionary < ElementId , FDecalMaterial > ( ) ;
ElementDataStack = new Stack < FElementData > ( ) ;
CurrentMaterialName = null ;
InsertionPoint = FSettings . EInsertionPoint . Default ;
ProjectSurveyPoint = null ;
ProjectBasePoint = null ;
SectionBoxOutline = null ;
SectionBox = null ;
2020-12-11 14:21:20 -04:00
if ( DirectLink ! = null )
{
MaterialDataMap = DirectLink . MaterialDataMap ;
}
else
{
MaterialDataMap = new Dictionary < string , FMaterialData > ( ) ;
}
2021-02-03 14:57:28 -04:00
2022-11-28 19:14:00 -05:00
InsertionPoint = CurrentSettings ? . InsertionPoint ? ? FSettings . EInsertionPoint . Default ;
2021-02-03 14:57:28 -04:00
// Cache document section boxes
2021-02-18 18:13:28 -04:00
if ( CurrentDocument . ActiveView ! = null )
2021-02-03 14:57:28 -04:00
{
2022-09-22 18:58:59 -04:00
View3D CurrentView3d = ( GetElement ( CurrentDocument . ActiveView . Id ) as View3D ) ;
2021-02-18 18:13:28 -04:00
2022-09-22 18:58:59 -04:00
SectionBox = null ;
SectionBoxOutline = null ;
if ( CurrentView3d ! = null & & CurrentView3d . IsSectionBoxActive )
2021-02-18 18:13:28 -04:00
{
2022-09-22 18:58:59 -04:00
BoundingBoxXYZ BBox = CurrentView3d . GetSectionBox ( ) ;
2022-09-28 15:18:24 -04:00
#if REVIT_API_2023
2022-09-22 18:58:59 -04:00
if ( BBox . IsSet & & BBox . Enabled )
2022-09-28 15:18:24 -04:00
#else
if ( BBox . Enabled )
#endif
2022-09-20 21:29:32 -04:00
{
2022-09-22 18:58:59 -04:00
SectionBox = new OrientatedBoundingBox ( BBox . Transform , BBox . Min , BBox . Max , true ) ;
if ( SectionBox . bIsValidData )
{
SectionBoxOutline = new Outline ( SectionBox . AxisAllignedMin , SectionBox . AxisAllignedMax ) ;
}
else
{
SectionBox = null ;
}
2022-09-20 21:29:32 -04:00
}
2021-02-18 18:13:28 -04:00
}
2021-02-03 14:57:28 -04:00
}
}
2020-09-24 00:43:27 -04:00
public Element GetElement (
ElementId InElementId
)
{
return ( InElementId ! = ElementId . InvalidElementId ) ? CurrentDocument . GetElement ( InElementId ) : null ;
}
public bool ContainsMesh ( string MeshName )
{
2020-10-09 22:42:26 -04:00
return MeshMap . ContainsKey ( MeshName ) ;
2020-09-24 00:43:27 -04:00
}
public bool PushElement (
Element InElement ,
Transform InWorldTransform
)
{
2022-10-27 14:56:53 -04:00
#if REVIT_API_2023
if ( DirectLink ! = null & & InElement . Category ! = null & & InElement . Category . BuiltInCategory ! = BuiltInCategory . OST_Levels )
#else
if ( DirectLink ! = null & & InElement . Category ! = null & & ( BuiltInCategory ) InElement . Category . Id . IntegerValue ! = BuiltInCategory . OST_Levels )
#endif
{
//Check if any of its children is Decal:
//If so track them, so that we can use the owner object elementId to update the Decals (when modified, for ep: changing its location)
2023-06-06 00:01:27 -04:00
foreach ( ElementId DependentElementId in FUtils . GetAllDependentElements ( InElement ) )
2022-10-27 14:56:53 -04:00
{
if ( FUtils . IsElementIdDecal ( InElement . Document , DependentElementId ) )
{
if ( DependentElementId ! = InElement . Id & & ! DirectLink . DecalIdToOwnerObjectIdMap . ContainsKey ( DependentElementId ) )
{
DirectLink . DecalIdToOwnerObjectIdMap . Add ( DependentElementId , InElement . Id ) ;
}
}
}
}
2020-10-09 22:42:26 -04:00
DirectLink ? . MarkForExport ( InElement ) ;
2020-09-24 00:43:27 -04:00
FElementData ElementData = null ;
2020-10-22 19:19:16 -04:00
if ( ActorMap . ContainsKey ( InElement . Id ) )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
if ( DirectLink ! = null & & DirectLink . IsElementCached ( InElement ) )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
return false ;
}
ElementData = ActorMap [ InElement . Id ] as FElementData ;
}
2022-03-28 16:55:59 -04:00
2020-10-22 19:19:16 -04:00
if ( ElementData = = null )
{
if ( DirectLink ? . IsElementCached ( InElement ) ? ? false )
{
ElementData = ( FElementData ) DirectLink . GetCachedElement ( InElement ) ;
2020-09-24 00:43:27 -04:00
2021-04-02 08:48:06 -04:00
bool bIsLinkedDocument = InElement . GetType ( ) = = typeof ( RevitLinkInstance ) ;
if ( bIsLinkedDocument | | DirectLink . IsElementModified ( InElement ) )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
FDatasmithFacadeActor ExistingActor = ElementData . ElementActor ;
// Remove children that are instances: they will be re-created;
// The reason is that we cannot uniquely identify family instances (no id) and when element changes,
// we need to export all of its child instances anew.
2021-08-03 11:56:47 -04:00
if ( ExistingActor ! = null )
2020-09-24 00:43:27 -04:00
{
2021-08-03 11:56:47 -04:00
if ( ElementData . ChildElements . Count > 0 )
{
2021-12-17 03:41:59 -05:00
List < FBaseElementData > ChildrenToRemove = new List < FBaseElementData > ( ) ;
2022-03-28 16:55:59 -04:00
2021-12-17 03:41:59 -05:00
for ( int ChildIndex = 0 ; ChildIndex < ElementData . ChildElements . Count ; + + ChildIndex )
2020-10-22 19:19:16 -04:00
{
2021-12-17 03:41:59 -05:00
FBaseElementData ChildElement = ElementData . ChildElements [ ChildIndex ] ;
2022-03-28 16:55:59 -04:00
bool bIsFamilyIntance =
( ( ChildElement as FElementData ) = = null ) & &
2021-12-17 03:41:59 -05:00
ChildElement . ElementActor . IsComponent ( ) ;
if ( bIsFamilyIntance )
{
ChildrenToRemove . Add ( ChildElement ) ;
}
}
foreach ( FBaseElementData Child in ChildrenToRemove )
{
ExistingActor . RemoveChild ( Child . ElementActor ) ;
ElementData . ChildElements . Remove ( Child ) ;
2020-10-22 19:19:16 -04:00
}
}
2021-08-03 11:56:47 -04:00
ExistingActor . ResetTags ( ) ;
( ExistingActor as FDatasmithFacadeActorMesh ) ? . SetMesh ( null ) ;
}
2022-04-25 04:57:38 -04:00
ElementData . BaseElementType = InElement . Document . GetElement ( InElement . GetTypeId ( ) ) as ElementType ;
ElementData . CurrentElement = InElement ;
2021-03-18 15:20:03 -04:00
ElementData . MeshMaterialsMap . Clear ( ) ;
2020-10-22 19:19:16 -04:00
}
else
{
ActorMap [ InElement . Id ] = ElementData ;
return false ; // We have up to date cache for this element.
2020-09-24 00:43:27 -04:00
}
}
else
{
2020-10-22 19:19:16 -04:00
ElementData = new FElementData ( InElement , InWorldTransform , this ) ;
2020-09-24 00:43:27 -04:00
}
2022-11-28 19:14:00 -05:00
ElementDataStack . Push ( ElementData ) ;
// Initialize element after pushing it on the stack(to unify this with other calls to element's methods)
// GetActorName depends on this
ElementData . InitializePivotPlacement ( ref InWorldTransform ) ;
ElementData . InitializeElement ( InWorldTransform , ElementData ) ;
2023-06-06 00:01:27 -04:00
if ( ElementData . bIsDecalElement )
{
if ( ! DecalElementsMap . ContainsKey ( InElement . Id ) )
{
DecalElementsMap . Add ( InElement . Id , ElementData ) ;
}
}
2022-11-28 19:14:00 -05:00
}
else
{
ElementDataStack . Push ( ElementData ) ;
2020-09-24 00:43:27 -04:00
}
ElementDataStack . Peek ( ) . ElementActor . AddTag ( "IsElement" ) ;
return true ;
}
2021-02-03 14:57:28 -04:00
public void PopElement ( FDatasmithFacadeScene InDatasmithScene )
2020-09-24 00:43:27 -04:00
{
FElementData ElementData = ElementDataStack . Pop ( ) ;
2021-02-03 14:57:28 -04:00
FDatasmithPolymesh DatasmithPolymesh = ElementData . DatasmithPolymesh ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
if ( DatasmithPolymesh . Vertices . Count > 0 & & DatasmithPolymesh . Faces . Count > 0 )
2020-09-24 00:43:27 -04:00
{
ElementData . UpdateMeshName ( ) ;
}
2021-02-03 14:57:28 -04:00
CollectMesh ( ElementData . DatasmithPolymesh , ElementData . DatasmithMeshElement , InDatasmithScene ) ;
2020-09-24 00:43:27 -04:00
2020-10-09 22:42:26 -04:00
DirectLink ? . ClearModified ( ElementData . CurrentElement ) ;
2020-09-24 00:43:27 -04:00
ElementData . bIsModified = true ;
2021-01-08 19:56:07 -04:00
ElementId ElemId = ElementData . CurrentElement . Id ;
2020-09-24 00:43:27 -04:00
if ( ElementDataStack . Count = = 0 )
{
2020-10-09 22:42:26 -04:00
if ( ActorMap . ContainsKey ( ElemId ) & & ActorMap [ ElemId ] ! = ElementData )
2020-09-24 00:43:27 -04:00
{
// Handle the spurious case of Revit Custom Exporter calling back more than once for the same element.
// These extra empty actors will be cleaned up later by the Datasmith actor hierarchy optimization.
2020-10-09 22:42:26 -04:00
ActorMap [ ElemId ] . ChildElements . Add ( ElementData ) ;
ElementData . Parent = ActorMap [ ElemId ] ;
2020-09-24 00:43:27 -04:00
}
else
{
// Collect the element mesh actor into the Datasmith actor dictionary.
2020-10-09 22:42:26 -04:00
ActorMap [ ElemId ] = ElementData ;
2020-09-24 00:43:27 -04:00
}
}
else
{
2021-01-08 19:56:07 -04:00
if ( ! ActorMap . ContainsKey ( ElemId ) )
{
// Add the element mesh actor to the Datasmith actor hierarchy.
ElementDataStack . Peek ( ) . AddChildActor ( ElementData ) ;
}
2020-09-24 00:43:27 -04:00
}
}
private static FDatasmithFacadeActor DuplicateBaseActor ( FDatasmithFacadeActor SourceActor )
{
FDatasmithFacadeActor CloneActor = new FDatasmithFacadeActor ( SourceActor . GetName ( ) ) ;
CloneActor . SetLabel ( SourceActor . GetLabel ( ) ) ;
2022-03-28 16:55:59 -04:00
double X , Y , Z , W ;
2020-09-24 00:43:27 -04:00
SourceActor . GetTranslation ( out X , out Y , out Z ) ;
CloneActor . SetTranslation ( X , Y , Z ) ;
SourceActor . GetScale ( out X , out Y , out Z ) ;
CloneActor . SetScale ( X , Y , Z ) ;
SourceActor . GetRotation ( out X , out Y , out Z , out W ) ;
CloneActor . SetRotation ( X , Y , Z , W ) ;
CloneActor . SetLayer ( SourceActor . GetLayer ( ) ) ;
for ( int TagIndex = 0 ; TagIndex < SourceActor . GetTagsCount ( ) ; + + TagIndex )
{
CloneActor . AddTag ( SourceActor . GetTag ( TagIndex ) ) ;
}
CloneActor . SetIsComponent ( SourceActor . IsComponent ( ) ) ;
CloneActor . SetVisibility ( SourceActor . GetVisibility ( ) ) ;
for ( int ChildIndex = 0 ; ChildIndex < SourceActor . GetChildrenCount ( ) ; + + ChildIndex )
{
CloneActor . AddChild ( SourceActor . GetChild ( ChildIndex ) ) ;
}
return CloneActor ;
}
public void PushInstance (
ElementType InInstanceType ,
Transform InWorldTransform
)
{
2021-02-03 14:57:28 -04:00
// Check if this instance intersects any section box.
// If so, we can't instance its mesh--will be considered unique.
2021-01-08 19:56:07 -04:00
2021-02-03 14:57:28 -04:00
bool bIntersectedBySectionBox = false ;
2022-09-20 21:29:32 -04:00
if ( SectionBox ! = null )
2021-01-08 19:56:07 -04:00
{
2021-02-18 18:13:28 -04:00
BoundingBoxXYZ InstanceBoundingBox = InInstanceType . get_BoundingBox ( CurrentDocument . ActiveView ) ;
2022-09-20 21:29:32 -04:00
2021-02-18 18:13:28 -04:00
if ( InstanceBoundingBox ! = null )
2021-02-03 14:57:28 -04:00
{
2022-09-20 21:29:32 -04:00
OrientatedBoundingBox InstanceOrientatedBoundingBox = new OrientatedBoundingBox ( InWorldTransform , InstanceBoundingBox . Min , InstanceBoundingBox . Max ) ;
if ( InstanceOrientatedBoundingBox . bIsValidData )
2021-02-03 14:57:28 -04:00
{
2022-09-20 21:29:32 -04:00
Outline InstanceOutline = new Outline ( InstanceOrientatedBoundingBox . AxisAllignedMin , InstanceOrientatedBoundingBox . AxisAllignedMax ) ;
bIntersectedBySectionBox = SectionBox . DoesIntersect ( InstanceOrientatedBoundingBox ) ;
2021-02-03 14:57:28 -04:00
}
}
2021-01-08 19:56:07 -04:00
}
2021-02-03 14:57:28 -04:00
FElementData CurrentElementData = ElementDataStack . Peek ( ) ;
FBaseElementData NewInstance = CurrentElementData . PushInstance ( InInstanceType , InWorldTransform , ! bIntersectedBySectionBox ) ;
2020-09-24 00:43:27 -04:00
}
2021-02-03 14:57:28 -04:00
public void PopInstance ( FDatasmithFacadeScene InDatasmithScene )
2020-09-24 00:43:27 -04:00
{
FElementData CurrentElement = ElementDataStack . Peek ( ) ;
FBaseElementData InstanceData = CurrentElement . PopInstance ( ) ;
2021-02-03 14:57:28 -04:00
FDatasmithPolymesh DatasmithPolymesh = InstanceData . DatasmithPolymesh ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
if ( ContainsMesh ( InstanceData . DatasmithMeshElement . GetName ( ) ) | | ( DatasmithPolymesh . Vertices . Count > 0 & & DatasmithPolymesh . Faces . Count > 0 ) )
2020-09-24 00:43:27 -04:00
{
InstanceData . UpdateMeshName ( ) ;
}
2021-05-25 02:43:26 -04:00
else
{
/ * Instance has no mesh .
* Handle the case where instance has valid transform , but parent element has valid mesh ( exported after instance gets finished ) ,
* in which case we want to apply the instance transform as a pivot transform .
* This is a case currently encountered for steel beams .
* /
bool bElementHasMesh = ContainsMesh ( CurrentElement . DatasmithMeshElement . GetName ( ) ) | | ( CurrentElement . DatasmithPolymesh . Vertices . Count > 0 & & CurrentElement . DatasmithPolymesh . Faces . Count > 0 ) ;
if ( CurrentElement . CurrentElement . GetType ( ) = = typeof ( FamilyInstance ) & & ! bElementHasMesh )
{
if ( ! CurrentElement . WorldTransform . IsIdentity )
{
2021-06-22 00:27:54 -04:00
CurrentElement . MeshPointsTransform = ( CurrentElement . WorldTransform . Inverse * InstanceData . WorldTransform ) . Inverse ;
}
2021-05-25 02:43:26 -04:00
else
{
2021-06-22 00:27:54 -04:00
CurrentElement . MeshPointsTransform = InstanceData . WorldTransform . Inverse ;
2021-05-25 02:43:26 -04:00
}
2021-06-22 00:27:54 -04:00
CurrentElement . WorldTransform = InstanceData . WorldTransform ;
2021-05-25 02:43:26 -04:00
SetActorTransform ( CurrentElement . WorldTransform , CurrentElement . ElementActor ) ;
}
}
2020-09-24 00:43:27 -04:00
// Collect the element Datasmith mesh into the mesh dictionary.
2021-02-03 14:57:28 -04:00
CollectMesh ( InstanceData . DatasmithPolymesh , InstanceData . DatasmithMeshElement , InDatasmithScene ) ;
2020-09-24 00:43:27 -04:00
// Add the instance mesh actor to the Datasmith actor hierarchy.
CurrentElement . AddChildActor ( InstanceData ) ;
}
public void AddLocationActors (
Transform InWorldTransform
)
{
// Add a new Datasmith placeholder actor for this document site location.
AddSiteLocation ( CurrentDocument . SiteLocation ) ;
// Add new Datasmith placeholder actors for the project base point and survey points.
// A project has one base point and at least one survey point. Linked documents also have their own points.
AddPointLocations ( InWorldTransform ) ;
}
public void AddLightActor (
Transform InWorldTransform ,
Asset InLightAsset
)
{
ElementDataStack . Peek ( ) . AddLightActor ( InWorldTransform , InLightAsset ) ;
}
public void AddRPCActor (
Transform InWorldTransform ,
2021-02-03 14:57:28 -04:00
Asset InRPCAsset ,
FDatasmithFacadeScene InDatasmithScene
2020-09-24 00:43:27 -04:00
)
{
// Create a simple fallback material for the RPC mesh.
string RPCCategoryName = ElementDataStack . Peek ( ) . GetCategoryName ( ) ;
bool isRPCPlant = ! string . IsNullOrEmpty ( RPCCategoryName ) & & RPCCategoryName = = Category . GetCategory ( CurrentDocument , BuiltInCategory . OST_Planting ) ? . Name ;
string RPCMaterialName = isRPCPlant ? "RPC_Plant" : "RPC_Material" ;
2020-12-11 14:21:20 -04:00
string RPCHashedMaterialName = FDatasmithFacadeElement . GetStringHash ( RPCMaterialName ) ;
2020-09-24 00:43:27 -04:00
2020-12-11 14:21:20 -04:00
if ( ! MaterialDataMap . ContainsKey ( RPCHashedMaterialName ) )
2020-09-24 00:43:27 -04:00
{
// Color reference: https://www.color-hex.com/color-palette/70002
Color RPCColor = isRPCPlant ? /* green */ new Color ( 88 , 126 , 96 ) : /* gray */ new Color ( 128 , 128 , 128 ) ;
// Keep track of a new RPC master material.
2020-12-11 14:21:20 -04:00
MaterialDataMap [ RPCHashedMaterialName ] = new FMaterialData ( RPCHashedMaterialName , RPCMaterialName , RPCColor ) ;
2021-01-08 19:56:07 -04:00
NewMaterialsMap [ RPCHashedMaterialName ] = MaterialDataMap [ RPCHashedMaterialName ] ;
2020-09-24 00:43:27 -04:00
}
2020-12-11 14:21:20 -04:00
FMaterialData RPCMaterialData = MaterialDataMap [ RPCHashedMaterialName ] ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
if ( ElementDataStack . Peek ( ) . AddRPCActor ( InWorldTransform , InRPCAsset , RPCMaterialData , out FDatasmithFacadeMesh RPCMesh , out FDatasmithFacadeMeshElement RPCMeshElement ) )
{
// Collect the RPC mesh into the Datasmith mesh dictionary.
CollectMesh ( RPCMesh , RPCMeshElement , InDatasmithScene ) ;
}
2020-09-24 00:43:27 -04:00
}
public bool SetMaterial (
MaterialNode InMaterialNode ,
IList < string > InExtraTexturePaths
)
{
Material CurrentMaterial = GetElement ( InMaterialNode . MaterialId ) as Material ;
2022-03-17 02:24:47 -04:00
if ( InMaterialNode . HasOverriddenAppearance )
{
2022-09-20 11:38:37 -04:00
IList < KeyValuePair < ElementId , Asset > > ReferencingDecalIdAndAssetPairs = FDecalMaterial . GetDecalElementIdAndAppearancePairList ( InMaterialNode ) ;
//Assets in on the .Value seem to be always unique, regardless if they are "instances" of the same DecalType:
if ( ReferencingDecalIdAndAssetPairs . Count > 0 )
2022-03-17 02:24:47 -04:00
{
2022-09-20 11:38:37 -04:00
IList < FDecalMaterial > DecalMaterials = new List < FDecalMaterial > ( ) ;
foreach ( KeyValuePair < ElementId , Asset > DecalIdAndAssetPair in ReferencingDecalIdAndAssetPairs )
2022-03-17 02:24:47 -04:00
{
2022-10-17 11:56:52 -04:00
if ( DecalMaterialsMap . ContainsKey ( DecalIdAndAssetPair . Key ) )
{
continue ;
}
2022-09-20 11:38:37 -04:00
FDecalMaterial DecalMaterial = FDecalMaterial . Create ( InMaterialNode , CurrentMaterial , DecalMaterials . Count , DecalIdAndAssetPair . Value ) ;
if ( DecalMaterial ! = null )
{
//unique DecalMaterial:
FDecalMaterial ExistingDecalMaterial = null ;
foreach ( FDecalMaterial ExistingDecalMaterialCandidate in DecalMaterials )
{
if ( ExistingDecalMaterialCandidate . CheckRenderValueEquiality ( DecalMaterial ) )
{
ExistingDecalMaterial = ExistingDecalMaterialCandidate ;
break ;
}
}
if ( ExistingDecalMaterial ! = null )
{
DecalMaterialsMap . Add ( DecalIdAndAssetPair . Key , ExistingDecalMaterial ) ;
}
else
{
DecalMaterialsMap . Add ( DecalIdAndAssetPair . Key , DecalMaterial ) ;
DecalMaterials . Add ( DecalMaterial ) ;
}
}
2022-03-17 02:24:47 -04:00
}
}
}
2020-12-11 14:21:20 -04:00
CurrentMaterialName = FMaterialData . GetMaterialName ( InMaterialNode , CurrentMaterial ) ;
2020-09-24 00:43:27 -04:00
2021-12-08 07:55:20 -05:00
if ( ! MaterialDataMap . ContainsKey ( CurrentMaterialName ) | | ( CurrentMaterial ! = null & & DirectLink ! = null & & DirectLink . IsMaterialDirty ( CurrentMaterial ) ) )
2020-09-24 00:43:27 -04:00
{
// Keep track of a new Datasmith master material.
2020-12-11 14:21:20 -04:00
MaterialDataMap [ CurrentMaterialName ] = new FMaterialData ( InMaterialNode , CurrentMaterial , InExtraTexturePaths ) ;
2021-01-08 19:56:07 -04:00
NewMaterialsMap [ CurrentMaterialName ] = MaterialDataMap [ CurrentMaterialName ] ;
2020-09-24 00:43:27 -04:00
2021-12-08 07:55:20 -05:00
DirectLink ? . SetMaterialClean ( CurrentMaterial ) ;
2020-09-24 00:43:27 -04:00
// A new Datasmith master material was created.
return true ;
}
// No new Datasmith master material created.
return false ;
}
public bool IgnoreElementGeometry ( )
{
bool bIgnore = ElementDataStack . Peek ( ) . IgnoreElementGeometry ( ) ;
if ( ! bIgnore )
{
// Check for instanced meshes.
2021-02-03 14:57:28 -04:00
// For mesh to be reused, it must not be cutoff by a section box.
FBaseElementData CurrentInstance = ElementDataStack . Peek ( ) . PeekInstance ( ) ;
2021-02-05 11:30:21 -04:00
if ( CurrentInstance ! = null & & CurrentInstance . bAllowMeshInstancing & & CurrentInstance . DatasmithMeshElement ! = null )
2020-09-24 00:43:27 -04:00
{
2021-02-05 11:30:21 -04:00
bIgnore = MeshMap . ContainsKey ( CurrentInstance . DatasmithMeshElement . GetName ( ) ) ;
2020-09-24 00:43:27 -04:00
}
}
return bIgnore ;
}
2021-02-03 14:57:28 -04:00
public FDatasmithPolymesh GetCurrentPolymesh ( )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
return ElementDataStack . Peek ( ) . GetCurrentPolymesh ( ) ;
}
public FDatasmithFacadeMeshElement GetCurrentMeshElement ( )
{
return ElementDataStack . Peek ( ) . GetCurrentMeshElement ( ) ;
2020-09-24 00:43:27 -04:00
}
public Transform GetCurrentMeshPointsTransform ( )
{
return ElementDataStack . Peek ( ) . MeshPointsTransform ;
}
2020-12-11 14:21:20 -04:00
public int GetCurrentMaterialIndex ( )
{
2021-03-18 15:20:03 -04:00
FElementData ElemData = ElementDataStack . Peek ( ) ;
FBaseElementData InstanceData = ElemData . PeekInstance ( ) ;
FBaseElementData CurrentElement = InstanceData ! = null ? InstanceData : ElemData ;
if ( ! CurrentElement . MeshMaterialsMap . ContainsKey ( CurrentMaterialName ) )
{
int NewMaterialIndex = CurrentElement . MeshMaterialsMap . Count ;
CurrentElement . MeshMaterialsMap [ CurrentMaterialName ] = NewMaterialIndex ;
CurrentElement . DatasmithMeshElement . SetMaterial ( CurrentMaterialName , NewMaterialIndex ) ;
}
return CurrentElement . MeshMaterialsMap [ CurrentMaterialName ] ;
2020-09-24 00:43:27 -04:00
}
public FBaseElementData GetCurrentActor ( )
{
return ElementDataStack . Peek ( ) . GetCurrentActor ( ) ;
}
2021-04-02 09:06:54 -04:00
public Element GetCurrentElement ( )
{
return ElementDataStack . Count > 0 ? ElementDataStack . Peek ( ) . CurrentElement : null ;
}
2021-05-11 01:10:20 -04:00
private FBaseElementData OptimizeElementRecursive ( FBaseElementData InElementData , FDatasmithFacadeScene InDatasmithScene , FSuperComponentOptimizer SuperComponentOptimizer )
2021-03-05 19:27:14 -04:00
{
2021-05-11 01:10:20 -04:00
List < FBaseElementData > RemoveChildren = new List < FBaseElementData > ( ) ;
List < FBaseElementData > AddChildren = new List < FBaseElementData > ( ) ;
2021-03-05 19:27:14 -04:00
for ( int ChildIndex = 0 ; ChildIndex < InElementData . ChildElements . Count ; ChildIndex + + )
{
FBaseElementData ChildElement = InElementData . ChildElements [ ChildIndex ] ;
// Optimize the Datasmith child actor.
2021-05-11 01:10:20 -04:00
FBaseElementData ResultElement = OptimizeElementRecursive ( ChildElement , InDatasmithScene , SuperComponentOptimizer ) ;
2021-03-05 19:27:14 -04:00
if ( ChildElement ! = ResultElement )
{
2021-05-11 01:10:20 -04:00
RemoveChildren . Add ( ChildElement ) ;
2021-03-05 19:27:14 -04:00
if ( ResultElement ! = null )
{
2021-05-11 01:10:20 -04:00
AddChildren . Add ( ResultElement ) ;
SuperComponentOptimizer . UpdateCache ( ResultElement , ChildElement ) ;
2021-03-05 19:27:14 -04:00
}
}
}
2021-05-11 01:10:20 -04:00
foreach ( FBaseElementData Child in RemoveChildren )
2021-03-05 19:27:14 -04:00
{
2021-05-11 01:10:20 -04:00
Child . Parent = null ;
InElementData . ChildElements . Remove ( Child ) ;
InElementData . ElementActor . RemoveChild ( Child . ElementActor ) ;
2021-03-05 19:27:14 -04:00
}
2021-05-11 01:10:20 -04:00
foreach ( FBaseElementData Child in AddChildren )
2021-03-05 19:27:14 -04:00
{
2021-05-11 01:10:20 -04:00
Child . Parent = InElementData ;
InElementData . ChildElements . Add ( Child ) ;
InElementData . ElementActor . AddChild ( Child . ElementActor ) ;
2021-03-05 19:27:14 -04:00
}
if ( InElementData . bOptimizeHierarchy )
{
int ChildrenCount = InElementData . ElementActor . GetChildrenCount ( ) ;
if ( ChildrenCount = = 0 )
{
// This Datasmith actor can be removed by optimization.
return null ;
}
if ( ChildrenCount = = 1 )
{
Debug . Assert ( InElementData . ChildElements . Count = = 1 ) ;
// This intermediate Datasmith actor can be removed while keeping its single child actor.
FBaseElementData SingleChild = InElementData . ChildElements [ 0 ] ;
// Make sure the single child actor will not become a dangling component in the actor hierarchy.
if ( ! InElementData . ElementActor . IsComponent ( ) & & SingleChild . ElementActor . IsComponent ( ) )
{
SingleChild . ElementActor . SetIsComponent ( false ) ;
}
return SingleChild ;
}
}
return InElementData ;
}
public void OptimizeActorHierarchy ( FDatasmithFacadeScene InDatasmithScene )
{
2021-05-11 01:10:20 -04:00
FSuperComponentOptimizer SuperComponentOptimizer = new FSuperComponentOptimizer ( ) ;
2021-03-05 19:27:14 -04:00
foreach ( var ElementEntry in ActorMap )
{
FBaseElementData ElementData = ElementEntry . Value ;
2021-05-11 01:10:20 -04:00
FBaseElementData ResultElementData = OptimizeElementRecursive ( ElementData , InDatasmithScene , SuperComponentOptimizer ) ;
2021-03-05 19:27:14 -04:00
if ( ResultElementData ! = ElementData )
{
if ( ResultElementData = = null )
{
InDatasmithScene . RemoveActor ( ElementData . ElementActor , FDatasmithFacadeScene . EActorRemovalRule . RemoveChildren ) ;
}
else
{
InDatasmithScene . RemoveActor ( ElementData . ElementActor , FDatasmithFacadeScene . EActorRemovalRule . KeepChildrenAndKeepRelativeTransform ) ;
2021-05-11 01:10:20 -04:00
if ( ElementData . ChildElements . Count = = 1 )
{
SuperComponentOptimizer . UpdateCache ( ElementData . ChildElements [ 0 ] , ElementData ) ;
}
2021-03-05 19:27:14 -04:00
}
}
}
2021-05-11 01:10:20 -04:00
SuperComponentOptimizer . Optimize ( ) ;
2021-03-05 19:27:14 -04:00
}
2020-09-24 00:43:27 -04:00
public void WrapupLink (
FDatasmithFacadeScene InDatasmithScene ,
FBaseElementData InLinkActor ,
HashSet < string > UniqueTextureNameSet
)
{
// Add the collected meshes from the Datasmith mesh dictionary to the Datasmith scene.
AddCollectedMeshes ( InDatasmithScene ) ;
// Factor in the Datasmith actor hierarchy the Revit document host hierarchy.
AddHostHierarchy ( ) ;
// Factor in the Datasmith actor hierarchy the Revit document level hierarchy.
AddLevelHierarchy ( ) ;
2020-10-09 22:42:26 -04:00
if ( ActorMap . Count > 0 )
2020-09-24 00:43:27 -04:00
{
// Prevent the Datasmith link actor from being removed by optimization.
InLinkActor . bOptimizeHierarchy = false ;
// Add the collected actors from the Datasmith actor dictionary as children of the Datasmith link actor.
2020-10-09 22:42:26 -04:00
foreach ( var Actor in ActorMap . Values )
2020-09-24 00:43:27 -04:00
{
InLinkActor . ChildElements . Add ( Actor ) ;
Actor . Parent = InLinkActor ;
}
}
// Add the collected master materials from the material data dictionary to the Datasmith scene.
AddCollectedMaterials ( InDatasmithScene , UniqueTextureNameSet ) ;
}
public void WrapupScene (
FDatasmithFacadeScene InDatasmithScene ,
HashSet < string > UniqueTextureNameSet
)
{
2022-03-17 02:24:47 -04:00
AddCollectedDecals ( InDatasmithScene ) ;
2020-09-24 00:43:27 -04:00
// Add the collected meshes from the Datasmith mesh dictionary to the Datasmith scene.
AddCollectedMeshes ( InDatasmithScene ) ;
// Factor in the Datasmith actor hierarchy the Revit document host hierarchy.
AddHostHierarchy ( ) ;
// Factor in the Datasmith actor hierarchy the Revit document level hierarchy.
AddLevelHierarchy ( ) ;
List < ElementId > OptimizedAwayElements = new List < ElementId > ( ) ;
2020-10-09 22:42:26 -04:00
foreach ( var CollectedActor in ActorMap )
2020-09-24 00:43:27 -04:00
{
if ( CollectedActor . Value . Optimize ( ) )
{
OptimizedAwayElements . Add ( CollectedActor . Key ) ;
}
}
foreach ( ElementId Element in OptimizedAwayElements )
{
2020-10-09 22:42:26 -04:00
ActorMap . Remove ( Element ) ;
2020-09-24 00:43:27 -04:00
}
// Add the collected actors from the Datasmith actor dictionary to the Datasmith scene.
2020-10-22 19:19:16 -04:00
foreach ( var ActorEntry in ActorMap )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
Element CollectedElement = CurrentDocument . GetElement ( ActorEntry . Key ) ;
if ( DirectLink ! = null & & CollectedElement . GetType ( ) = = typeof ( RevitLinkInstance ) )
{
Document LinkedDoc = ( CollectedElement as RevitLinkInstance ) . GetLinkDocument ( ) ;
if ( LinkedDoc ! = null )
{
2021-03-18 15:20:03 -04:00
DirectLink . OnBeginLinkedDocument ( CollectedElement ) ;
2020-10-22 19:19:16 -04:00
foreach ( FBaseElementData CurrentChild in ActorEntry . Value . ChildElements )
{
CurrentChild . AddToScene ( InDatasmithScene , ActorEntry . Value , false ) ;
}
DirectLink . OnEndLinkedDocument ( ) ;
ActorEntry . Value . AddToScene ( InDatasmithScene , null , true ) ;
}
else
{
ActorEntry . Value . AddToScene ( InDatasmithScene , null , false ) ;
}
}
else
{
ActorEntry . Value . AddToScene ( InDatasmithScene , null , false ) ;
}
2020-09-24 00:43:27 -04:00
}
// Add the collected master materials from the material data dictionary to the Datasmith scene.
AddCollectedMaterials ( InDatasmithScene , UniqueTextureNameSet ) ;
}
public void LogElement (
FDatasmithFacadeLog InDebugLog ,
string InLinePrefix ,
int InLineIndentation
)
{
ElementDataStack . Peek ( ) . Log ( InDebugLog , InLinePrefix , InLineIndentation ) ;
}
public void LogMaterial (
MaterialNode InMaterialNode ,
FDatasmithFacadeLog InDebugLog ,
string InLinePrefix
)
{
2020-12-11 14:21:20 -04:00
if ( MaterialDataMap . ContainsKey ( CurrentMaterialName ) )
2020-09-24 00:43:27 -04:00
{
2020-12-11 14:21:20 -04:00
MaterialDataMap [ CurrentMaterialName ] . Log ( InMaterialNode , InDebugLog , InLinePrefix ) ;
2020-09-24 00:43:27 -04:00
}
}
private void AddSiteLocation (
SiteLocation InSiteLocation
)
{
2020-10-09 22:42:26 -04:00
if ( InSiteLocation = = null | | ! InSiteLocation . IsValidObject )
2020-09-24 00:43:27 -04:00
{
return ;
}
2020-10-09 22:42:26 -04:00
FDatasmithFacadeActor SiteLocationActor = null ;
FBaseElementData ElementData = null ;
DirectLink ? . MarkForExport ( InSiteLocation ) ;
if ( DirectLink ? . IsElementCached ( InSiteLocation ) ? ? false )
{
if ( ! DirectLink . IsElementModified ( InSiteLocation ) )
{
return ;
}
ElementData = DirectLink . GetCachedElement ( InSiteLocation ) ;
SiteLocationActor = ElementData . ElementActor ;
SiteLocationActor . ResetTags ( ) ;
}
else
{
// Create a new Datasmith placeholder actor for the site location.
// Hash the Datasmith placeholder actor name to shorten it.
string NameHash = FDatasmithFacadeElement . GetStringHash ( "SiteLocation" ) ;
SiteLocationActor = new FDatasmithFacadeActor ( NameHash ) ;
SiteLocationActor . SetLabel ( "Site Location" ) ;
}
2020-09-24 00:43:27 -04:00
// Set the Datasmith placeholder actor layer to the site location category name.
SiteLocationActor . SetLayer ( InSiteLocation . Category . Name ) ;
// Add the Revit element ID and Unique ID tags to the Datasmith placeholder actor.
SiteLocationActor . AddTag ( $"Revit.Element.Id.{InSiteLocation.Id.IntegerValue}" ) ;
SiteLocationActor . AddTag ( $"Revit.Element.UniqueId.{InSiteLocation.UniqueId}" ) ;
// Add a Revit element site location tag to the Datasmith placeholder actor.
SiteLocationActor . AddTag ( "Revit.Element.SiteLocation" ) ;
FDatasmithFacadeMetaData SiteLocationMetaData = new FDatasmithFacadeMetaData ( SiteLocationActor . GetName ( ) + "_DATA" ) ;
SiteLocationMetaData . SetLabel ( SiteLocationActor . GetLabel ( ) ) ;
SiteLocationMetaData . SetAssociatedElement ( SiteLocationActor ) ;
// Add site location metadata to the Datasmith placeholder actor.
const double RadiansToDegrees = 180.0 / Math . PI ;
SiteLocationMetaData . AddPropertyFloat ( "SiteLocation*Latitude" , ( float ) ( InSiteLocation . Latitude * RadiansToDegrees ) ) ;
SiteLocationMetaData . AddPropertyFloat ( "SiteLocation*Longitude" , ( float ) ( InSiteLocation . Longitude * RadiansToDegrees ) ) ;
SiteLocationMetaData . AddPropertyFloat ( "SiteLocation*Elevation" , ( float ) InSiteLocation . Elevation ) ;
SiteLocationMetaData . AddPropertyFloat ( "SiteLocation*TimeZone" , ( float ) InSiteLocation . TimeZone ) ;
SiteLocationMetaData . AddPropertyString ( "SiteLocation*Place" , InSiteLocation . PlaceName ) ;
// Collect the site location placeholder actor into the Datasmith actor dictionary.
2020-10-09 22:42:26 -04:00
if ( ElementData = = null )
{
ElementData = new FBaseElementData ( SiteLocationActor , null , this ) ;
// Prevent the Datasmith placeholder actor from being removed by optimization.
2022-03-28 16:55:59 -04:00
ElementData . bOptimizeHierarchy = false ;
2020-10-09 22:42:26 -04:00
}
else
{
ElementData . ElementMetaData = SiteLocationMetaData ;
}
2022-03-28 16:55:59 -04:00
2020-10-09 22:42:26 -04:00
ActorMap [ InSiteLocation . Id ] = ElementData ;
DirectLink ? . CacheElement ( CurrentDocument , InSiteLocation , ElementData ) ;
2020-09-24 00:43:27 -04:00
}
private void AddPointLocations (
Transform InWorldTransform
)
{
FilteredElementCollector Collector = new FilteredElementCollector ( CurrentDocument ) ;
ICollection < Element > PointLocations = Collector . OfClass ( typeof ( BasePoint ) ) . ToElements ( ) ;
foreach ( Element PointLocation in PointLocations )
{
BasePoint BasePointLocation = PointLocation as BasePoint ;
if ( BasePointLocation ! = null )
{
// Since BasePoint.Location is not a location point we cannot get a position from it; so we use a bounding box approach.
// Note that, as of Revit 2020, BasePoint has 2 new properties: Position for base point and SharedPosition for survey point.
BoundingBoxXYZ BasePointBoundingBox = BasePointLocation . get_BoundingBox ( CurrentDocument . ActiveView ) ;
if ( BasePointBoundingBox = = null )
{
continue ;
}
string ActorName = BasePointLocation . IsShared ? "SurveyPoint" : "BasePoint" ;
string ActorLabel = BasePointLocation . IsShared ? "Survey Point" : "Base Point" ;
2020-10-09 22:42:26 -04:00
FDatasmithFacadeActor BasePointActor = null ;
FBaseElementData BasePointElement = null ;
DirectLink ? . MarkForExport ( PointLocation ) ;
if ( DirectLink ? . IsElementCached ( PointLocation ) ? ? false )
{
if ( ! DirectLink . IsElementModified ( PointLocation ) )
{
continue ;
}
BasePointElement = DirectLink . GetCachedElement ( PointLocation ) ;
BasePointActor = BasePointElement . ElementActor ;
BasePointActor . ResetTags ( ) ;
}
else
{
// Create a new Datasmith placeholder actor for the base point.
// Hash the Datasmith placeholder actor name to shorten it.
string HashedActorName = FDatasmithFacadeElement . GetStringHash ( ActorName ) ;
BasePointActor = new FDatasmithFacadeActor ( HashedActorName ) ;
BasePointActor . SetLabel ( ActorLabel ) ;
}
2020-09-24 00:43:27 -04:00
// Set the world transform of the Datasmith placeholder actor.
XYZ BasePointPosition = BasePointBoundingBox . Min ;
2022-03-16 07:58:12 -04:00
if ( BasePointLocation . IsShared )
{
ProjectSurveyPoint = BasePointPosition ;
}
else
{
ProjectBasePoint = BasePointPosition ;
}
2020-09-24 00:43:27 -04:00
Transform TranslationMatrix = Transform . CreateTranslation ( BasePointPosition ) ;
2022-05-09 03:25:49 -04:00
// Don't apply offset since basepoints aren't yet initialized
SetActorTransform ( TranslationMatrix . Multiply ( InWorldTransform ) , BasePointActor , false ) ;
2020-09-24 00:43:27 -04:00
2022-05-19 03:35:26 -04:00
2020-09-24 00:43:27 -04:00
// Set the Datasmith placeholder actor layer to the base point category name.
BasePointActor . SetLayer ( BasePointLocation . Category . Name ) ;
// Add the Revit element ID and Unique ID tags to the Datasmith placeholder actor.
BasePointActor . AddTag ( $"Revit.Element.Id.{BasePointLocation.Id.IntegerValue}" ) ;
BasePointActor . AddTag ( $"Revit.Element.UniqueId.{BasePointLocation.UniqueId}" ) ;
// Add a Revit element base point tag to the Datasmith placeholder actor.
BasePointActor . AddTag ( "Revit.Element." + ActorName ) ;
// Add base point metadata to the Datasmith actor.
string MetadataPrefix = BasePointLocation . IsShared ? "SurveyPointLocation*" : "BasePointLocation*" ;
FDatasmithFacadeMetaData BasePointMetaData = new FDatasmithFacadeMetaData ( BasePointActor . GetName ( ) + "_DATA" ) ;
BasePointMetaData . SetLabel ( BasePointActor . GetLabel ( ) ) ;
BasePointMetaData . SetAssociatedElement ( BasePointActor ) ;
BasePointMetaData . AddPropertyVector ( MetadataPrefix + "Location" , $"{BasePointPosition.X} {BasePointPosition.Y} {BasePointPosition.Z}" ) ;
2021-09-09 03:37:24 -04:00
if ( ! bSkipMetadataExport )
{
2022-05-19 03:35:26 -04:00
FUtils . AddActorMetadata ( BasePointLocation , MetadataPrefix , BasePointMetaData , CurrentSettings ) ;
2021-09-09 03:37:24 -04:00
}
2020-09-24 00:43:27 -04:00
2020-10-09 22:42:26 -04:00
if ( BasePointElement = = null )
{
// Collect the base point placeholder actor into the Datasmith actor dictionary.
BasePointElement = new FBaseElementData ( BasePointActor , BasePointMetaData , this ) ;
BasePointElement . bOptimizeHierarchy = false ;
}
else
{
BasePointElement . ElementMetaData = BasePointMetaData ;
}
ActorMap [ BasePointLocation . Id ] = BasePointElement ;
DirectLink ? . CacheElement ( CurrentDocument , PointLocation , BasePointElement ) ;
2020-09-24 00:43:27 -04:00
}
}
}
private void CollectMesh (
2021-02-03 14:57:28 -04:00
FDatasmithFacadeMesh InMesh ,
FDatasmithFacadeMeshElement InMeshElement ,
FDatasmithFacadeScene InDatasmithScene
2020-09-24 00:43:27 -04:00
)
{
2021-02-03 14:57:28 -04:00
if ( InDatasmithScene ! = null & & InMesh . GetVerticesCount ( ) > 0 & & InMesh . GetFacesCount ( ) > 0 )
2020-09-24 00:43:27 -04:00
{
string MeshName = InMesh . GetName ( ) ;
2020-10-09 22:42:26 -04:00
if ( ! MeshMap . ContainsKey ( MeshName ) )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
// Export the DatasmithMesh in a task while we parse the rest of the document.
// The task result indicates if the export was successful and if the associated FDatasmithFacadeMeshElement can be added to the scene.
MeshMap [ MeshName ] = new Tuple < FDatasmithFacadeMeshElement , Task < bool > > ( InMeshElement , Task . Run < bool > ( ( ) = > InDatasmithScene . ExportDatasmithMesh ( InMeshElement , InMesh ) ) ) ;
2020-09-24 00:43:27 -04:00
}
}
}
2021-02-03 14:57:28 -04:00
private void CollectMesh (
FDatasmithPolymesh InPolymesh ,
FDatasmithFacadeMeshElement InMeshElement ,
FDatasmithFacadeScene InDatasmithScene
)
{
if ( InDatasmithScene ! = null & & InPolymesh . Vertices . Count > 0 & & InPolymesh . Faces . Count > 0 )
{
string MeshName = InMeshElement . GetName ( ) ;
if ( ! MeshMap . ContainsKey ( MeshName ) )
{
// Export the DatasmithMesh in a task while we parse the rest of the document.
// The task result indicates if the export was successful and if the associated FDatasmithFacadeMeshElement can be added to the scene.
MeshMap [ MeshName ] = new Tuple < FDatasmithFacadeMeshElement , Task < bool > > ( InMeshElement , Task . Run < bool > (
2022-03-28 16:55:59 -04:00
( ) = >
2021-02-03 14:57:28 -04:00
{
using ( FDatasmithFacadeMesh DatasmithMesh = ParsePolymesh ( InPolymesh , MeshName ) )
{
return InDatasmithScene . ExportDatasmithMesh ( InMeshElement , DatasmithMesh ) ;
}
}
) ) ;
}
}
}
private FDatasmithFacadeMesh ParsePolymesh ( FDatasmithPolymesh InPolymesh , string MeshName )
{
FDatasmithFacadeMesh DatasmithMesh = new FDatasmithFacadeMesh ( ) ;
DatasmithMesh . SetName ( MeshName ) ;
DatasmithMesh . SetVerticesCount ( InPolymesh . Vertices . Count ) ;
DatasmithMesh . SetFacesCount ( InPolymesh . Faces . Count ) ;
DatasmithMesh . SetUVChannelsCount ( 1 ) ;
DatasmithMesh . SetUVCount ( 0 , InPolymesh . UVs . Count ) ;
const int UVChannelIndex = 0 ;
// Add the vertex points (in right-handed Z-up coordinates) to the Datasmith mesh.
for ( int VertexIndex = 0 ; VertexIndex < InPolymesh . Vertices . Count ; + + VertexIndex )
{
XYZ Point = InPolymesh . Vertices [ VertexIndex ] ;
DatasmithMesh . SetVertex ( VertexIndex , ( float ) Point . X , ( float ) Point . Y , ( float ) Point . Z ) ;
}
// Add the vertex UV texture coordinates to the Datasmith mesh.
for ( int UVIndex = 0 ; UVIndex < InPolymesh . UVs . Count ; + + UVIndex )
{
UV CurrentUV = InPolymesh . UVs [ UVIndex ] ;
2022-03-28 16:55:59 -04:00
DatasmithMesh . SetUV ( UVChannelIndex , UVIndex , CurrentUV . U , CurrentUV . V ) ;
2021-02-03 14:57:28 -04:00
}
// Add the triangle vertex indexes to the Datasmith mesh.
for ( int FacetIndex = 0 ; FacetIndex < InPolymesh . Faces . Count ; + + FacetIndex )
{
FDocumentData . FPolymeshFace Face = InPolymesh . Faces [ FacetIndex ] ;
DatasmithMesh . SetFace ( FacetIndex , Face . V1 , Face . V2 , Face . V3 , Face . MaterialIndex ) ;
DatasmithMesh . SetFaceUV ( FacetIndex , UVChannelIndex , Face . V1 , Face . V2 , Face . V3 ) ;
}
for ( int NormalIndex = 0 ; NormalIndex < InPolymesh . Normals . Count ; + + NormalIndex )
{
XYZ Normal = InPolymesh . Normals [ NormalIndex ] ;
DatasmithMesh . SetNormal ( NormalIndex , ( float ) Normal . X , ( float ) Normal . Y , ( float ) Normal . Z ) ;
}
2022-03-28 16:55:59 -04:00
2021-02-03 14:57:28 -04:00
return DatasmithMesh ;
}
2022-03-17 02:24:47 -04:00
private void AddCollectedDecals ( FDatasmithFacadeScene InDatasmithScene )
{
foreach ( KeyValuePair < ElementId , FDecalMaterial > DecalMaterialPair in DecalMaterialsMap )
{
FElementData DecalElement = null ;
if ( ! DecalElementsMap . TryGetValue ( DecalMaterialPair . Key , out DecalElement ) )
{
continue ;
}
FDecalMaterial DecalMaterial = DecalMaterialPair . Value ;
2022-08-25 15:29:37 -04:00
FDatasmithFacadeMaterialInstance DatasmithMaterial = new FDatasmithFacadeMaterialInstance ( DecalMaterial . MaterialName ) ;
DatasmithMaterial . SetMaterialType ( FDatasmithFacadeMaterialInstance . EMaterialInstanceType . Decal ) ;
2022-03-17 02:24:47 -04:00
if ( ! string . IsNullOrEmpty ( DecalMaterial . DiffuseTexturePath ) )
{
FDatasmithFacadeTexture DiffuseTexture = FDatasmithFacadeMaterialsUtils . CreateSimpleTextureElement ( DecalMaterial . DiffuseTexturePath ) ;
DiffuseTexture . SetSRGB ( FDatasmithFacadeTexture . EColorSpace . sRGB ) ;
DiffuseTexture . SetTextureMode ( FDatasmithFacadeTexture . ETextureMode . Diffuse ) ;
DiffuseTexture . SetFile ( DecalMaterial . DiffuseTexturePath ) ;
2022-05-09 03:25:49 -04:00
DatasmithMaterial . AddTexture ( "ColorMap" , DiffuseTexture ) ;
2022-03-17 02:24:47 -04:00
InDatasmithScene . AddTexture ( DiffuseTexture ) ;
}
if ( ! string . IsNullOrEmpty ( DecalMaterial . BumpTexturePath ) )
{
FDatasmithFacadeTexture BumpTexture = FDatasmithFacadeMaterialsUtils . CreateSimpleTextureElement ( DecalMaterial . BumpTexturePath ) ;
BumpTexture . SetSRGB ( FDatasmithFacadeTexture . EColorSpace . sRGB ) ;
BumpTexture . SetTextureMode ( FDatasmithFacadeTexture . ETextureMode . Bump ) ;
BumpTexture . SetFile ( DecalMaterial . BumpTexturePath ) ;
2022-05-09 03:25:49 -04:00
DatasmithMaterial . AddTexture ( "NormalMap" , BumpTexture ) ;
DatasmithMaterial . AddFloat ( "NormalMapAmount" , ( float ) DecalMaterial . BumpAmount ) ;
2022-03-17 02:24:47 -04:00
InDatasmithScene . AddTexture ( BumpTexture ) ;
}
2022-05-09 03:25:49 -04:00
if ( ! string . IsNullOrEmpty ( DecalMaterial . CutoutTexturePath ) )
{
FDatasmithFacadeTexture CutoutTexture = FDatasmithFacadeMaterialsUtils . CreateSimpleTextureElement ( DecalMaterial . CutoutTexturePath ) ;
CutoutTexture . SetSRGB ( FDatasmithFacadeTexture . EColorSpace . sRGB ) ;
CutoutTexture . SetTextureMode ( FDatasmithFacadeTexture . ETextureMode . Other ) ;
CutoutTexture . SetFile ( DecalMaterial . CutoutTexturePath ) ;
DatasmithMaterial . AddBoolean ( "UseCustomOpacityMap" , true ) ;
DatasmithMaterial . AddTexture ( "OpacityMap" , CutoutTexture ) ;
InDatasmithScene . AddTexture ( CutoutTexture ) ;
}
if ( DecalMaterial . Luminance > 0f )
{
DatasmithMaterial . AddFloat ( "LuminanceAmount" , ( float ) DecalMaterial . Luminance ) ;
}
if ( DecalMaterial . Transparency > 0f )
{
DatasmithMaterial . AddFloat ( "Opacity" , ( float ) DecalMaterial . Transparency ) ;
}
2022-03-17 02:24:47 -04:00
InDatasmithScene . AddMaterial ( DatasmithMaterial ) ;
Transform DecalTransform = null ;
XYZ DecalDimensions = null ;
FUtils . GetDecalSpatialParams ( DecalElement . CurrentElement , ref DecalTransform , ref DecalDimensions ) ;
2022-06-02 09:22:13 -04:00
if ( DecalTransform = = null | | DecalDimensions = = null )
{
continue ;
}
2022-03-17 02:24:47 -04:00
FDatasmithFacadeActorDecal DecalActor = DecalElement . ElementActor as FDatasmithFacadeActorDecal ;
DecalActor . SetDimensions ( DecalDimensions . Z , DecalDimensions . X , DecalDimensions . Y ) ;
DecalActor . SetDecalMaterialPathName ( DatasmithMaterial . GetName ( ) ) ;
SetActorTransform ( DecalTransform , DecalActor ) ;
}
}
2020-09-24 00:43:27 -04:00
private void AddCollectedMeshes (
FDatasmithFacadeScene InDatasmithScene
)
{
// Add the collected meshes from the Datasmith mesh dictionary to the Datasmith scene.
2021-02-03 14:57:28 -04:00
foreach ( var MeshElementExportResultTuple in MeshMap . Values )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
// Wait for the export to complete and add the Mesh element on success.
if ( MeshElementExportResultTuple . Item2 . Result )
{
InDatasmithScene . AddMesh ( MeshElementExportResultTuple . Item1 ) ;
}
2020-09-24 00:43:27 -04:00
}
}
private void AddHostHierarchy ( )
{
AddParentElementHierarchy ( GetHostElement ) ;
}
private void AddLevelHierarchy ( )
{
AddParentElementHierarchy ( GetLevelElement ) ;
}
private void AddCollectedMaterials (
FDatasmithFacadeScene InDatasmithScene ,
HashSet < string > UniqueTextureNameSet
)
{
2020-12-11 14:21:20 -04:00
UniqueTextureNameSet = ( DirectLink ! = null ) ? DirectLink . UniqueTextureNameSet : UniqueTextureNameSet ;
2020-09-24 00:43:27 -04:00
// Add the collected master materials from the material data dictionary to the Datasmith scene.
2021-01-08 19:56:07 -04:00
foreach ( FMaterialData CollectedMaterialData in NewMaterialsMap . Values )
2020-09-24 00:43:27 -04:00
{
2022-08-25 15:29:37 -04:00
InDatasmithScene . AddMaterial ( CollectedMaterialData . MaterialInstance ) ;
2020-09-24 00:43:27 -04:00
foreach ( FDatasmithFacadeTexture CurrentTexture in CollectedMaterialData . CollectedTextures )
{
string TextureName = CurrentTexture . GetName ( ) ;
if ( ! UniqueTextureNameSet . Contains ( TextureName ) )
{
UniqueTextureNameSet . Add ( TextureName ) ;
InDatasmithScene . AddTexture ( CurrentTexture ) ;
}
}
if ( CollectedMaterialData . MessageList . Count > 0 )
{
MessageList . AddRange ( CollectedMaterialData . MessageList ) ;
}
}
}
private Element GetHostElement (
ElementId InElementId
)
{
Element SourceElement = CurrentDocument . GetElement ( InElementId ) ;
2020-10-09 22:42:26 -04:00
Element HostElement = null ;
2020-09-24 00:43:27 -04:00
if ( SourceElement as FamilyInstance ! = null )
{
2020-10-09 22:42:26 -04:00
HostElement = ( SourceElement as FamilyInstance ) . Host ;
2020-09-24 00:43:27 -04:00
}
else if ( SourceElement as Wall ! = null )
{
2020-10-09 22:42:26 -04:00
HostElement = CurrentDocument . GetElement ( ( SourceElement as Wall ) . StackedWallOwnerId ) ;
2020-09-24 00:43:27 -04:00
}
else if ( SourceElement as ContinuousRail ! = null )
{
2020-10-09 22:42:26 -04:00
HostElement = CurrentDocument . GetElement ( ( SourceElement as ContinuousRail ) . HostRailingId ) ;
2020-09-24 00:43:27 -04:00
}
else if ( SourceElement . GetType ( ) . IsSubclassOf ( typeof ( InsulationLiningBase ) ) )
{
2020-10-09 22:42:26 -04:00
HostElement = CurrentDocument . GetElement ( ( SourceElement as InsulationLiningBase ) . HostElementId ) ;
2020-09-24 00:43:27 -04:00
}
2020-10-22 19:19:16 -04:00
// DirectLink: if host is hidden, go up the hierarchy (NOTE this does not apply for linked documents)
2022-03-28 16:55:59 -04:00
if ( DirectLink ! = null & &
HostElement ! = null & &
CurrentDocument . ActiveView ! = null & &
2020-10-22 19:19:16 -04:00
HostElement . IsHidden ( CurrentDocument . ActiveView ) )
2020-10-09 22:42:26 -04:00
{
return GetHostElement ( HostElement . Id ) ;
}
return HostElement ;
2020-09-24 00:43:27 -04:00
}
private Element GetLevelElement (
ElementId InElementId
)
{
Element SourceElement = CurrentDocument . GetElement ( InElementId ) ;
return ( SourceElement = = null ) ? null : CurrentDocument . GetElement ( SourceElement . LevelId ) ;
}
private void AddParentElementHierarchy (
Func < ElementId , Element > InGetParentElement
)
{
2023-02-03 11:00:25 -05:00
Context . LogDebug ( "AddParentElementHierarchy" ) ;
2020-10-09 22:42:26 -04:00
Queue < ElementId > ElementIdQueue = new Queue < ElementId > ( ActorMap . Keys ) ;
2020-09-24 00:43:27 -04:00
// Make sure the Datasmith actor dictionary contains actors for all the Revit parent elements.
while ( ElementIdQueue . Count > 0 )
{
Element ParentElement = InGetParentElement ( ElementIdQueue . Dequeue ( ) ) ;
if ( ParentElement = = null )
{
continue ;
}
ElementId ParentElementId = ParentElement . Id ;
2020-10-09 22:42:26 -04:00
if ( ActorMap . ContainsKey ( ParentElementId ) )
2020-09-24 00:43:27 -04:00
{
continue ;
}
2020-10-09 22:42:26 -04:00
if ( DirectLink ? . IsElementCached ( ParentElement ) ? ? false )
2020-09-24 00:43:27 -04:00
{
// Move parent actor out of cache.
2020-10-09 22:42:26 -04:00
DirectLink . MarkForExport ( ParentElement ) ;
ActorMap [ ParentElementId ] = DirectLink . GetCachedElement ( ParentElement ) ;
2020-09-24 00:43:27 -04:00
}
else
{
2023-02-03 11:00:25 -05:00
Context . LogDebug ( " Add element" ) ;
2021-02-03 14:57:28 -04:00
const FDatasmithFacadeScene NullScene = null ;
2020-09-24 00:43:27 -04:00
PushElement ( ParentElement , Transform . Identity ) ;
2021-02-03 14:57:28 -04:00
PopElement ( NullScene ) ;
2020-09-24 00:43:27 -04:00
}
ElementIdQueue . Enqueue ( ParentElementId ) ;
}
// Add the parented actors as children of the parent Datasmith actors.
2020-10-09 22:42:26 -04:00
foreach ( ElementId ElemId in new List < ElementId > ( ActorMap . Keys ) )
2020-09-24 00:43:27 -04:00
{
Element ParentElement = InGetParentElement ( ElemId ) ;
if ( ParentElement = = null )
{
continue ;
}
Element SourceElement = CurrentDocument . GetElement ( ElemId ) ;
if ( ( SourceElement as FamilyInstance ! = null & & ParentElement as Truss ! = null ) | |
( SourceElement as Mullion ! = null ) | |
( SourceElement as Panel ! = null ) | |
( SourceElement as ContinuousRail ! = null ) )
{
// The Datasmith actor is a component in the hierarchy.
2020-10-09 22:42:26 -04:00
ActorMap [ ElemId ] . ElementActor . SetIsComponent ( true ) ;
2020-09-24 00:43:27 -04:00
}
ElementId ParentElementId = ParentElement . Id ;
// Add the parented actor as child of the parent Datasmith actor.
2020-10-09 22:42:26 -04:00
FBaseElementData ElementData = ActorMap [ ElemId ] ;
FBaseElementData ParentElementData = ActorMap [ ParentElementId ] ;
2022-03-28 16:55:59 -04:00
2020-09-24 00:43:27 -04:00
if ( ! ParentElementData . ChildElements . Contains ( ElementData ) )
{
ParentElementData . ChildElements . Add ( ElementData ) ;
ElementData . Parent = ParentElementData ;
}
// Prevent the parent Datasmith actor from being removed by optimization.
ParentElementData . bOptimizeHierarchy = false ;
}
// Remove the parented child actors from the Datasmith actor dictionary.
2020-10-09 22:42:26 -04:00
foreach ( ElementId ElemId in new List < ElementId > ( ActorMap . Keys ) )
2020-09-24 00:43:27 -04:00
{
Element ParentElement = InGetParentElement ( ElemId ) ;
if ( ParentElement = = null )
{
continue ;
}
// Remove the parented child actor from the Datasmith actor dictionary.
2020-10-09 22:42:26 -04:00
ActorMap . Remove ( ElemId ) ;
2020-09-24 00:43:27 -04:00
}
}
2022-03-16 07:58:12 -04:00
private void SetActorTransform (
2020-09-24 00:43:27 -04:00
Transform InWorldTransform ,
2022-05-09 03:25:49 -04:00
FDatasmithFacadeActor IOActor ,
2022-05-19 03:35:26 -04:00
bool bInApplyOffset = true
)
2020-09-24 00:43:27 -04:00
{
XYZ transformBasisX = InWorldTransform . BasisX ;
XYZ transformBasisY = InWorldTransform . BasisY ;
XYZ transformBasisZ = InWorldTransform . BasisZ ;
XYZ transformOrigin = InWorldTransform . Origin ;
2022-03-16 07:58:12 -04:00
// Check if need to apply world offset to element transform
2022-05-09 03:25:49 -04:00
if ( bInApplyOffset & & InsertionPoint ! = FSettings . EInsertionPoint . Default )
2022-03-16 07:58:12 -04:00
{
switch ( InsertionPoint )
{
2022-05-19 03:35:26 -04:00
case FSettings . EInsertionPoint . BasePoint :
2022-05-09 03:25:49 -04:00
{
if ( ProjectBasePoint ! = null )
{
transformOrigin - = ProjectBasePoint ;
}
2022-05-19 03:35:26 -04:00
}
break ;
2022-05-09 03:25:49 -04:00
2022-05-19 03:35:26 -04:00
case FSettings . EInsertionPoint . SurveyPoint :
2022-05-09 03:25:49 -04:00
{
if ( ProjectSurveyPoint ! = null )
{
transformOrigin - = ProjectSurveyPoint ;
}
2022-05-19 03:35:26 -04:00
}
break ;
2022-03-16 07:58:12 -04:00
}
}
2022-03-28 16:55:59 -04:00
double [ ] worldMatrix = new double [ 16 ] ;
2020-09-24 00:43:27 -04:00
2022-03-28 16:55:59 -04:00
worldMatrix [ 0 ] = transformBasisX . X ;
worldMatrix [ 1 ] = transformBasisX . Y ;
worldMatrix [ 2 ] = transformBasisX . Z ;
worldMatrix [ 3 ] = 0.0 ;
worldMatrix [ 4 ] = transformBasisY . X ;
worldMatrix [ 5 ] = transformBasisY . Y ;
worldMatrix [ 6 ] = transformBasisY . Z ;
worldMatrix [ 7 ] = 0.0 ;
worldMatrix [ 8 ] = transformBasisZ . X ;
worldMatrix [ 9 ] = transformBasisZ . Y ;
worldMatrix [ 10 ] = transformBasisZ . Z ;
worldMatrix [ 11 ] = 0.0 ;
worldMatrix [ 12 ] = transformOrigin . X ;
worldMatrix [ 13 ] = transformOrigin . Y ;
worldMatrix [ 14 ] = transformOrigin . Z ;
worldMatrix [ 15 ] = 1.0 ;
2020-09-24 00:43:27 -04:00
// Set the world transform of the Datasmith actor.
IOActor . SetWorldTransform ( worldMatrix ) ;
}
2022-11-28 19:14:00 -05:00
/// <summary>
/// Combine whole element stack in the document hierarchy to build unique path to the element in the document
/// </summary>
public string GetElementStackName ( )
{
return string . Join ( ", " , ElementDataStack . Select ( Data = > $"{Data.CurrentElement.UniqueId}" ) ) ;
}
2020-09-24 00:43:27 -04:00
}
}