2021-03-05 19:27:14 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DatasmithSketchUpComponent.h"
# include "DatasmithSketchUpExportContext.h"
# include "DatasmithSketchUpMaterial.h"
# include "DatasmithSketchUpMetadata.h"
# include "DatasmithSketchUpString.h"
# include "DatasmithSketchUpSummary.h"
# include "DatasmithSketchUpUtils.h"
// SketchUp SDK.
# include "DatasmithSketchUpSDKBegins.h"
# include "SketchUpAPI/model/component_definition.h"
2021-04-08 14:32:07 -04:00
# include "SketchUpAPI/model/drawing_element.h"
2021-03-05 19:27:14 -04:00
# include "SketchUpAPI/model/entities.h"
2022-07-07 02:05:52 -04:00
# include <SketchUpAPI/model/entity.h>
2021-03-05 19:27:14 -04:00
# include "SketchUpAPI/model/group.h"
# include "SketchUpAPI/model/layer.h"
# include "SketchUpAPI/model/model.h"
# include "SketchUpAPI/model/component_instance.h"
2022-06-16 00:24:28 -04:00
# include "SketchUpAPI/geometry/transformation.h"
2022-07-20 10:54:47 -04:00
# include "SketchUpAPI/geometry/vector3d.h"
2021-09-06 12:23:53 -04:00
# if !defined(SKP_SDK_2019) && !defined(SKP_SDK_2020)
# include "SketchUpAPI/model/layer_folder.h"
# endif
2021-03-05 19:27:14 -04:00
# include "DatasmithSketchUpSDKCeases.h"
# include "IDatasmithSceneElements.h"
# include "DatasmithSceneFactory.h"
# include "DatasmithUtils.h"
2022-07-20 10:54:47 -04:00
# include "Misc/Paths.h"
2021-04-08 14:32:07 -04:00
# define REMOVE_MESHES_WHEN_INVISIBLE
2021-03-05 19:27:14 -04:00
using namespace DatasmithSketchUp ;
2022-07-20 10:54:47 -04:00
void FDefinition : : ParseNode ( FExportContext & Context , FNodeOccurence & Node )
2021-04-08 14:32:07 -04:00
{
2021-03-18 15:20:03 -04:00
// Process child nodes
2022-07-20 10:54:47 -04:00
2021-04-08 14:32:07 -04:00
// Convert the SketchUp normal component instances into sub-hierarchies of Datasmith actors.
2022-07-20 10:54:47 -04:00
for ( SUComponentInstanceRef SComponentInstanceRef : GetEntities ( ) . GetComponentInstances ( ) )
2021-03-18 15:20:03 -04:00
{
2022-07-20 10:54:47 -04:00
TSharedPtr < FComponentInstance > ComponentInstance = Context . ComponentInstances . AddComponentInstance ( * this , SComponentInstanceRef ) ;
2021-04-08 14:32:07 -04:00
if ( ComponentInstance . IsValid ( ) )
2021-03-18 15:20:03 -04:00
{
2022-07-20 10:54:47 -04:00
FNodeOccurence & ChildNode = ComponentInstance - > CreateNodeOccurrence ( Context , Node ) ;
ComponentInstance - > ParseNode ( Context , ChildNode ) ;
2021-03-18 15:20:03 -04:00
}
}
2021-04-08 14:32:07 -04:00
// Convert the SketchUp group component instances into sub-hierarchies of Datasmith actors.
2022-07-20 10:54:47 -04:00
for ( SUGroupRef SGroupRef : GetEntities ( ) . GetGroups ( ) )
2021-03-18 15:20:03 -04:00
{
2021-04-08 14:32:07 -04:00
SUComponentInstanceRef SComponentInstanceRef = SUGroupToComponentInstance ( SGroupRef ) ;
2022-07-20 10:54:47 -04:00
TSharedPtr < FComponentInstance > ComponentInstance = Context . ComponentInstances . AddComponentInstance ( * this , SComponentInstanceRef ) ;
2021-04-08 14:32:07 -04:00
if ( ComponentInstance . IsValid ( ) )
2021-03-18 15:20:03 -04:00
{
2022-07-20 10:54:47 -04:00
FNodeOccurence & ChildNode = ComponentInstance - > CreateNodeOccurrence ( Context , Node ) ;
ComponentInstance - > ParseNode ( Context , ChildNode ) ;
2021-03-18 15:20:03 -04:00
}
}
2022-07-20 10:54:47 -04:00
for ( SUImageRef ImageRef : GetEntities ( ) . GetImages ( ) )
{
TSharedPtr < FImage > Image = Context . Images . AddImage ( * this , ImageRef ) ;
if ( Image . IsValid ( ) )
{
Image - > CreateNodeOccurrence ( Context , Node ) ;
}
}
}
// Update mesh actors of an entiry with Entities(that is model or component instance)
2022-08-19 00:48:28 -04:00
void FEntityWithEntities : : UpdateOccurrenceMeshActors ( FExportContext & Context , FNodeOccurence & Node )
2022-07-20 10:54:47 -04:00
{
FDefinition * EntityDefinition = GetDefinition ( ) ;
DatasmithSketchUp : : FEntitiesGeometry * EntitiesGeometry = EntityDefinition - > GetEntities ( ) . EntitiesGeometry . Get ( ) ;
if ( ! EntitiesGeometry )
{
2022-08-19 00:48:28 -04:00
return ;
2022-07-20 10:54:47 -04:00
}
Node . MeshActors . Reset ( EntitiesGeometry - > GetMeshCount ( ) ) ;
FString ComponentActorName = Node . GetActorName ( ) ;
for ( int32 MeshIndex = 0 ; MeshIndex < EntitiesGeometry - > GetMeshCount ( ) ; + + MeshIndex )
{
FString MeshActorName = FString : : Printf ( TEXT ( " %ls_%d " ) , * ComponentActorName , MeshIndex + 1 ) ; // Count meshes/mesh actors from 1
// Create a Datasmith mesh actor for the Datasmith mesh element.
TSharedPtr < IDatasmithMeshActorElement > DMeshActorPtr = FDatasmithSceneFactory : : CreateMeshActor ( * MeshActorName ) ;
Node . MeshActors . Add ( DMeshActorPtr ) ;
// Add the Datasmith actor component depth tag.
// We use component depth + 1 to factor in the added Datasmith scene root once imported in Unreal.
FString ComponentDepthTag = FString : : Printf ( TEXT ( " SU.DEPTH.%d " ) , Node . Depth + 1 ) ;
DMeshActorPtr - > AddTag ( * ComponentDepthTag ) ;
// Add the Datasmith actor component definition GUID tag.
FString DefinitionGUIDTag = FString : : Printf ( TEXT ( " SU.GUID.%ls " ) , * EntityDefinition - > GetSketchupSourceGUID ( ) ) ;
DMeshActorPtr - > AddTag ( * DefinitionGUIDTag ) ;
// Add the Datasmith actor component instance path tag.
FString InstancePathTag = ComponentActorName . Replace ( TEXT ( " SU " ) , TEXT ( " SU.PATH.0 " ) ) . Replace ( TEXT ( " _ " ) , TEXT ( " . " ) ) ;
DMeshActorPtr - > AddTag ( * InstancePathTag ) ;
// ADD_TRACE_LINE(TEXT("Actor %ls: %ls %ls %ls"), *MeshActorLabel, *ComponentDepthTag, *DefinitionGUIDTag, *InstancePathTag);
// Set the Datasmith mesh element used by the mesh actor.
DMeshActorPtr - > SetStaticMeshPathName ( EntitiesGeometry - > GetMeshElementName ( MeshIndex ) ) ;
}
2021-04-08 14:32:07 -04:00
}
void FNodeOccurence : : UpdateVisibility ( FExportContext & Context )
{
if ( bHierarchyInvalidated )
{
// todo: move hierarchy creation here?
bHierarchyInvalidated = false ;
}
if ( bVisibilityInvalidated )
{
Entity . UpdateOccurrenceVisibility ( Context , * this ) ;
bVisibilityInvalidated = false ;
}
for ( FNodeOccurence * ChildNode : Children )
{
ChildNode - > UpdateVisibility ( Context ) ;
2021-03-05 19:27:14 -04:00
}
}
void FNodeOccurence : : Update ( FExportContext & Context )
{
2021-03-18 15:20:03 -04:00
// todo: Is it possible not to traverse whole scene when only part of it changes?
// - one way is to collect all nodes that need to be updated
// - the other - only topmost invalidated nodes. and them traverse from them only, not from the top.
// E.g. when a node is invalidated - traverse its subtree to invalidate all the nodes below. Also when a node is invalidated check
// its parent - if its not invalidated this means any ancestor is not invalidated. This way complexity would be O(n) where n is number of nodes that need update, not number of all nodes
2021-04-08 14:32:07 -04:00
if ( bMeshActorsInvalidated )
{
2022-08-19 00:48:28 -04:00
Entity . ResetOccurrenceActors ( Context , * this ) ;
if ( bVisible )
{
Entity . UpdateOccurrenceMeshActors ( Context , * this ) ;
}
2021-04-08 14:32:07 -04:00
bMeshActorsInvalidated = false ;
}
2021-03-18 15:20:03 -04:00
if ( bPropertiesInvalidated )
{
2022-08-19 00:48:28 -04:00
if ( bVisible )
{
Entity . UpdateOccurrence ( Context , * this ) ;
}
2021-03-18 15:20:03 -04:00
bPropertiesInvalidated = false ;
}
for ( FNodeOccurence * ChildNode : Children )
{
ChildNode - > Update ( Context ) ;
}
}
2021-04-08 14:32:07 -04:00
void FNodeOccurence : : InvalidateProperties ( )
2021-03-18 15:20:03 -04:00
{
if ( bPropertiesInvalidated )
{
// if node is invalidated no need to traverse further - it's already done
return ;
}
bPropertiesInvalidated = true ;
// todo: register invalidated?
for ( FNodeOccurence * Child : Children )
{
2021-04-08 14:32:07 -04:00
Child - > InvalidateProperties ( ) ;
2021-03-18 15:20:03 -04:00
}
2021-03-05 19:27:14 -04:00
}
2021-04-08 14:32:07 -04:00
void FNodeOccurence : : InvalidateMeshActors ( )
{
bMeshActorsInvalidated = true ;
}
2021-03-05 19:27:14 -04:00
FString FNodeOccurence : : GetActorName ( )
{
return DatasmithActorName ;
}
FString FNodeOccurence : : GetActorLabel ( )
{
return DatasmithActorLabel ;
}
2021-04-08 14:32:07 -04:00
void FNodeOccurence : : RemoveOccurrence ( FExportContext & Context )
2021-03-18 15:20:03 -04:00
{
2021-04-08 14:32:07 -04:00
// RemoveOccurrence is called from Entity only(i.e. it doesn't remove occurrence from the Entity itself, it's done there)
2021-10-25 20:05:28 -04:00
if ( MaterialOverride )
{
2021-11-07 23:43:01 -05:00
MaterialOverride - > UnregisterInstance ( Context , this ) ;
2021-10-25 20:05:28 -04:00
}
2022-07-20 10:54:47 -04:00
if ( ParentNode )
{
ParentNode - > Children . Remove ( this ) ;
}
2022-08-19 00:48:28 -04:00
// Usually child component instances are removed in proper order - children first. Nut grouping entities
// has this weird behavior that containing component removed without cleaning its children. Test case:
2022-07-20 10:54:47 -04:00
// Group an instance and some other entity(e.g. face) and then convert group to component
// then converted group is removed without other events for its children
// Probably this is because those 'children' are actually entities in the group's definition
// And Group entity itself is just an instance of its definition(just like ComponentInstance)
// So that Group definition content is not changed and that definition just receives another instance
for ( FNodeOccurence * Child : Children . Array ( ) )
2021-04-08 14:32:07 -04:00
{
Child - > RemoveOccurrence ( Context ) ;
}
2022-08-19 00:48:28 -04:00
Entity . ResetOccurrenceActors ( Context , * this ) ;
2022-07-20 10:54:47 -04:00
Entity . DeleteOccurrence ( Context , this ) ;
2021-04-08 14:32:07 -04:00
}
2021-03-18 15:20:03 -04:00
2021-05-11 01:10:20 -04:00
void FNodeOccurence : : ResetMetadataElement ( FExportContext & Context )
{
// Create a Datasmith metadata element for the SketckUp component instance metadata definition.
FString MetadataElementName = FString : : Printf ( TEXT ( " %ls_DATA " ) , DatasmithActorElement - > GetName ( ) ) ;
if ( ! DatasmithMetadataElement . IsValid ( ) )
{
DatasmithMetadataElement = FDatasmithSceneFactory : : CreateMetaData ( * MetadataElementName ) ;
DatasmithMetadataElement - > SetAssociatedElement ( DatasmithActorElement ) ;
Context . DatasmithScene - > AddMetaData ( DatasmithMetadataElement ) ;
}
else
{
DatasmithMetadataElement - > SetName ( * MetadataElementName ) ;
}
2022-08-19 00:48:28 -04:00
DatasmithMetadataElement - > SetLabel ( * GetActorLabel ( ) ) ;
2021-05-11 01:10:20 -04:00
DatasmithMetadataElement - > ResetProperties ( ) ;
}
2021-04-08 14:32:07 -04:00
void FNodeOccurence : : SetVisibility ( bool bValue )
{
bVisible = bValue ;
2021-03-18 15:20:03 -04:00
}
2021-05-25 02:43:26 -04:00
void FNodeOccurence : : RemoveDatasmithActorHierarchy ( FExportContext & Context )
{
if ( ! DatasmithActorElement )
{
2022-08-19 00:48:28 -04:00
// Hierarchy already removed(or wasn't created)
2021-05-25 02:43:26 -04:00
return ;
}
// Remove depth-first
for ( FNodeOccurence * ChildNode : Children )
{
ChildNode - > RemoveDatasmithActorHierarchy ( Context ) ;
}
2022-08-19 00:48:28 -04:00
Entity . ResetOccurrenceActors ( Context , * this ) ;
}
2021-05-25 02:43:26 -04:00
2022-08-19 00:48:28 -04:00
void FNodeOccurence : : ResetNodeActors ( FExportContext & Context )
{
FNodeOccurence & Node = * this ;
2021-05-25 02:43:26 -04:00
2022-08-19 00:48:28 -04:00
// Remove old mesh actors
// todo: reuse old mesh actors (also can keep instances when removing due to say hidden)
if ( Node . DatasmithActorElement )
2021-05-25 02:43:26 -04:00
{
2022-08-19 00:48:28 -04:00
// Check if component used an actor to combine mesh and child nodes under it
// todo: just add flag for code clearness?
bool bHasActor = Node . MeshActors . IsEmpty ( ) | | ( Node . DatasmithActorElement ! = Node . MeshActors [ 0 ] ) ;
if ( bHasActor )
{
// In this case detach all the children before removing actor from the parent/scene
// note: DatasmithScene::RemoveActor has only two ways to remove children - relocating then to Scene root or deleting hierarchy
int32 ChildCount = Node . DatasmithActorElement - > GetChildrenCount ( ) ;
// Remove last child each time to optimize array elements relocation
for ( int32 ChildIndex = ChildCount - 1 ; ChildIndex > = 0 ; - - ChildIndex )
{
Node . DatasmithActorElement - > RemoveChild ( Node . DatasmithActorElement - > GetChild ( ChildIndex ) ) ;
}
}
if ( const TSharedPtr < IDatasmithActorElement > & ParentActor = Node . DatasmithActorElement - > GetParentActor ( ) )
{
ParentActor - > RemoveChild ( Node . DatasmithActorElement ) ;
}
else
{
Context . DatasmithScene - > RemoveActor ( Node . DatasmithActorElement , EDatasmithActorRemovalRule : : RemoveChildren ) ;
}
Node . DatasmithActorElement . Reset ( ) ;
if ( DatasmithMetadataElement )
{
Context . DatasmithScene - > RemoveMetaData ( DatasmithMetadataElement ) ;
}
DatasmithMetadataElement . Reset ( ) ;
2021-05-25 02:43:26 -04:00
}
2022-08-19 00:48:28 -04:00
Node . MeshActors . Reset ( ) ;
2021-05-25 02:43:26 -04:00
}
2021-03-05 19:27:14 -04:00
FModelDefinition : : FModelDefinition ( SUModelRef InModel ) : Model ( InModel )
{
}
void FModelDefinition : : Parse ( FExportContext & Context )
{
SUEntitiesRef EntitiesRef = SU_INVALID ;
// Retrieve the SketchUp model entities.
SUModelGetEntities ( Model , & EntitiesRef ) ;
Entities = Context . EntitiesObjects . AddEntities ( * this , EntitiesRef ) ;
}
void FModelDefinition : : UpdateGeometry ( FExportContext & Context )
{
Entities - > UpdateGeometry ( Context ) ;
}
2021-05-11 01:10:20 -04:00
void FModelDefinition : : UpdateMetadata ( FExportContext & Context )
{
}
2021-03-18 15:20:03 -04:00
void FModelDefinition : : InvalidateInstancesGeometry ( FExportContext & Context )
{
Context . Model - > InvalidateEntityGeometry ( ) ;
}
2021-05-11 01:10:20 -04:00
void FModelDefinition : : InvalidateInstancesMetadata ( FExportContext & Context )
{
}
void FModelDefinition : : FillOccurrenceActorMetadata ( FNodeOccurence & Node )
{
}
2021-03-05 19:27:14 -04:00
FString FModelDefinition : : GetSketchupSourceName ( )
{
FString SketchupSourceName = SuGetString ( SUModelGetName , Model ) ;
if ( SketchupSourceName . IsEmpty ( ) )
{
SketchupSourceName = TEXT ( " SketchUp_Model " ) ;
}
return SketchupSourceName ;
}
2022-07-07 02:05:52 -04:00
FString FModelDefinition : : GetSketchupSourceId ( )
{
return GetSketchupSourceGUID ( ) ;
}
2022-06-16 00:24:28 -04:00
SUTransformation FModelDefinition : : GetMeshBakedTransform ( )
{
SUTransformation Transform ;
SUTransformationScale ( & Transform , 1.0 ) ;
return Transform ;
}
2021-03-05 19:27:14 -04:00
FString FModelDefinition : : GetSketchupSourceGUID ( )
{
return TEXT ( " MODEL " ) ;
}
2021-04-08 14:32:07 -04:00
void FModelDefinition : : AddInstance ( FExportContext & Context , TSharedPtr < FComponentInstance > Instance )
{
2022-07-20 10:54:47 -04:00
FNodeOccurence & ChildNode = Instance - > CreateNodeOccurrence ( Context , * Context . RootNode ) ;
Instance - > ParseNode ( Context , ChildNode ) ;
2021-04-08 14:32:07 -04:00
}
2022-07-20 10:54:47 -04:00
void FModelDefinition : : AddImage ( FExportContext & Context , TSharedPtr < FImage > Image )
{
Image - > CreateNodeOccurrence ( Context , * Context . RootNode ) ;
}
2021-03-05 19:27:14 -04:00
FComponentDefinition : : FComponentDefinition (
SUComponentDefinitionRef InComponentDefinitionRef )
: ComponentDefinitionRef ( InComponentDefinitionRef )
{
}
2022-07-20 10:54:47 -04:00
FComponentDefinition : : ~ FComponentDefinition ( )
{
}
2021-03-05 19:27:14 -04:00
void FComponentDefinition : : Parse ( FExportContext & Context )
{
SUEntitiesRef EntitiesRef = SU_INVALID ;
// Retrieve the SketchUp component definition entities.
SUComponentDefinitionGetEntities ( ComponentDefinitionRef , & EntitiesRef ) ; // we can ignore the returned SU_RESULT
Entities = Context . EntitiesObjects . AddEntities ( * this , EntitiesRef ) ;
// Get the component ID of the SketckUp component definition.
SketchupSourceID = DatasmithSketchUpUtils : : GetComponentID ( ComponentDefinitionRef ) ;
// Retrieve the SketchUp component definition behavior in the rendering scene.
SUComponentBehavior SComponentBehavior ;
SUComponentDefinitionGetBehavior ( ComponentDefinitionRef , & SComponentBehavior ) ; // we can ignore the returned SU_RESULT
// Get whether or not the source SketchUp component behaves like a billboard.
bSketchupSourceFaceCamera = SComponentBehavior . component_always_face_camera ;
}
2022-08-19 00:48:28 -04:00
void FComponentInstance : : SetupActor ( FExportContext & Context , FNodeOccurence & Node )
2021-03-05 19:27:14 -04:00
{
2022-08-19 00:48:28 -04:00
FComponentDefinition & EntityDefinition = * ( FComponentDefinition * ) GetDefinition ( ) ;
2021-03-05 19:27:14 -04:00
2022-08-19 00:48:28 -04:00
// Add the Datasmith actor component depth tag.
// We use component depth + 1 to factor in the added Datasmith scene root once imported in Unreal.
FString ComponentDepthTag = FString : : Printf ( TEXT ( " SU.DEPTH.%d " ) , Node . Depth ) ;
Node . DatasmithActorElement - > AddTag ( * ComponentDepthTag ) ;
2021-03-05 19:27:14 -04:00
2022-08-19 00:48:28 -04:00
// Add the Datasmith actor component definition GUID tag.
FString DefinitionGUIDTag = FString : : Printf ( TEXT ( " SU.GUID.%ls " ) , * GetDefinition ( ) - > GetSketchupSourceGUID ( ) ) ;
Node . DatasmithActorElement - > AddTag ( * DefinitionGUIDTag ) ;
// Add the Datasmith actor component instance path tag.
FString InstancePathTag = Node . GetActorName ( ) . Replace ( TEXT ( " SU " ) , TEXT ( " SU.PATH.0 " ) ) . Replace ( TEXT ( " _ " ) , TEXT ( " . " ) ) ;
Node . DatasmithActorElement - > AddTag ( * InstancePathTag ) ;
// Add the Datasmith actor component instance face camera tag when required.
if ( EntityDefinition . bSketchupSourceFaceCamera )
2021-03-05 19:27:14 -04:00
{
2022-08-19 00:48:28 -04:00
Node . DatasmithActorElement - > AddTag ( TEXT ( " SU.BEHAVIOR.FaceCamera " ) ) ;
2021-03-05 19:27:14 -04:00
}
2021-03-18 15:20:03 -04:00
if ( Node . ParentNode - > DatasmithActorElement )
2021-03-05 19:27:14 -04:00
{
Node . ParentNode - > DatasmithActorElement - > AddChild ( Node . DatasmithActorElement ) ;
}
else
{
Context . DatasmithScene - > AddActor ( Node . DatasmithActorElement ) ;
}
}
void FComponentDefinition : : UpdateGeometry ( FExportContext & Context )
{
Entities - > UpdateGeometry ( Context ) ;
2022-06-16 00:24:28 -04:00
bBakeTransformIntoMesh = ShouldBakeTransformIntoMesh ( ) ;
2021-03-05 19:27:14 -04:00
}
2021-05-11 01:10:20 -04:00
void FComponentDefinition : : UpdateMetadata ( FExportContext & Context )
{
ParsedMetadata = MakeUnique < FMetadata > ( SUComponentDefinitionToEntity ( ComponentDefinitionRef ) ) ; ;
}
2022-08-19 00:48:28 -04:00
void FComponentInstance : : BuildNodeNames ( FNodeOccurence & Node )
2021-03-05 19:27:14 -04:00
{
// Get the SketckUp component instance persistent ID.
int64 SketchupPersistentID = Node . Entity . GetPersistentId ( ) ;
Node . DatasmithActorName = FString : : Printf ( TEXT ( " %ls_%lld " ) , * Node . ParentNode - > GetActorName ( ) , SketchupPersistentID ) ;
FString EntityName = Node . Entity . GetName ( ) ;
2022-08-19 00:48:28 -04:00
Node . DatasmithActorLabel = FDatasmithUtils : : SanitizeObjectName ( EntityName . IsEmpty ( ) ? GetDefinition ( ) - > GetSketchupSourceName ( ) : EntityName ) ;
2021-03-05 19:27:14 -04:00
}
2021-03-18 15:20:03 -04:00
void FComponentDefinition : : InvalidateInstancesGeometry ( FExportContext & Context )
2021-03-05 19:27:14 -04:00
{
2021-03-18 15:20:03 -04:00
// todo: keep all instances or incapsulate enumeration(duplicated) of FComponentInstance
2021-03-05 19:27:14 -04:00
size_t InstanceCount = 0 ;
SUComponentDefinitionGetNumInstances ( ComponentDefinitionRef , & InstanceCount ) ;
2021-04-08 14:32:07 -04:00
TArray < SUComponentInstanceRef > InstanceRefs ;
InstanceRefs . Init ( SU_INVALID , InstanceCount ) ;
SUComponentDefinitionGetInstances ( ComponentDefinitionRef , InstanceCount , InstanceRefs . GetData ( ) , & InstanceCount ) ;
InstanceRefs . SetNum ( InstanceCount ) ;
2021-03-05 19:27:14 -04:00
2021-04-08 14:32:07 -04:00
for ( const SUComponentInstanceRef & InstanceRef : InstanceRefs )
2021-03-05 19:27:14 -04:00
{
2021-03-18 15:20:03 -04:00
Context . ComponentInstances . InvalidateComponentInstanceGeometry ( DatasmithSketchUpUtils : : GetComponentInstanceID ( InstanceRef ) ) ;
2021-03-05 19:27:14 -04:00
}
}
2021-05-11 01:10:20 -04:00
void FComponentDefinition : : InvalidateInstancesMetadata ( FExportContext & Context )
{
// todo: keep all instances or incapsulate enumeration(duplicated) of FComponentInstance
size_t InstanceCount = 0 ;
SUComponentDefinitionGetNumInstances ( ComponentDefinitionRef , & InstanceCount ) ;
TArray < SUComponentInstanceRef > InstanceRefs ;
InstanceRefs . Init ( SU_INVALID , InstanceCount ) ;
SUComponentDefinitionGetInstances ( ComponentDefinitionRef , InstanceCount , InstanceRefs . GetData ( ) , & InstanceCount ) ;
InstanceRefs . SetNum ( InstanceCount ) ;
for ( const SUComponentInstanceRef & InstanceRef : InstanceRefs )
{
Context . ComponentInstances . InvalidateComponentInstanceMetadata ( DatasmithSketchUpUtils : : GetComponentInstanceID ( InstanceRef ) ) ;
}
}
void FComponentDefinition : : FillOccurrenceActorMetadata ( FNodeOccurence & Node )
{
if ( ParsedMetadata )
{
ParsedMetadata - > AddMetadata ( Node . DatasmithMetadataElement ) ;
}
}
2021-03-18 15:20:03 -04:00
FString FComponentDefinition : : GetSketchupSourceName ( )
{
// Retrieve the SketchUp component definition name.
return SuGetString ( SUComponentDefinitionGetName , ComponentDefinitionRef ) ;
}
2022-07-07 02:05:52 -04:00
FString FComponentDefinition : : GetSketchupSourceId ( )
{
2022-07-20 10:54:47 -04:00
// Although SUEntityGetPersistentID implemented since SU 2017 it returns valid Id for ComponentDefinitions
2022-07-07 02:05:52 -04:00
// only since SU 2020.1 (even though SUEntityGetPersistentID docs states SUComponentDefinitionRef 'supported' from 2017)
// see https://github.com/SketchUp/api-issue-tracker/issues/314
# ifndef SKP_SDK_2019
// Use Entity PersistentID - this one is persistent(between sessions) for model file and doesn't change when definition is modified(e.g. geometry edited)
int64_t EntityPid = 0 ;
if ( SUEntityGetPersistentID ( SUComponentDefinitionToEntity ( ComponentDefinitionRef ) , & EntityPid ) = = SU_ERROR_NONE )
{
if ( ensure ( EntityPid ! = 0 ) )
{
return FString : : Printf ( TEXT ( " %llx " ) , EntityPid ) ;
}
}
# endif
return FMD5 : : HashAnsiString ( * GetSketchupSourceGUID ( ) ) ;
}
2022-06-16 00:24:28 -04:00
// Bake transform into the mesh only when
// - component has no child components,
// - only SINGLE instance
// - local transform has non-trivial rotation component
// Baking transform into mesh might be useful to convert transformation which is not representable in Unreal
// for example rotated geometry with scaled parent. In Unreal scaling would only be applied in local static mesh space
// So that scaled(on parent node) rotated cube that should be a rhombus in SU in unreal would become just a box because scaling would apply along local axes(i.e. before rotation)
// But baking rotation into mesh itself makes scaling apply properly
bool FComponentDefinition : : ShouldBakeTransformIntoMesh ( )
{
size_t ComponentInstanceCount = 0 ;
SUEntitiesGetNumInstances ( GetEntities ( ) . EntitiesRef , & ComponentInstanceCount ) ;
size_t GroupCount = 0 ;
SUEntitiesGetNumGroups ( GetEntities ( ) . EntitiesRef , & GroupCount ) ;
if ( ( Instances . Num ( ) ! = 1 ) | | ( ( ComponentInstanceCount + GroupCount ) ! = 0 ) )
{
return false ;
}
// Check the (only) instance's transform
FComponentInstance * ComponentInstance = Instances . Array ( ) [ 0 ] ;
SUTransformation Transform ;
SUComponentInstanceGetTransform ( ComponentInstance - > GetComponentInstanceRef ( ) , & Transform ) ;
// Grab 3x3 rotation submatrix and check if it is not an identity transform
const double R [ ] = {
Transform . values [ 0 ] , Transform . values [ 1 ] , Transform . values [ 2 ] ,
Transform . values [ 4 ] , Transform . values [ 5 ] , Transform . values [ 6 ] ,
Transform . values [ 8 ] , Transform . values [ 9 ] , Transform . values [ 10 ]
} ;
static const double Identity [ ] = {
1 , 0 , 0 ,
0 , 1 , 0 ,
0 , 0 , 1 ,
} ;
static_assert ( sizeof ( Identity ) / sizeof ( Identity [ 0 ] ) = = sizeof ( R ) / sizeof ( R [ 0 ] ) ) ;
for ( int32 I = 0 ; I < sizeof ( Identity ) / sizeof ( Identity [ 0 ] ) ; + + I )
{
if ( ! FMath : : IsNearlyEqual ( Identity [ I ] , R [ I ] ) )
{
return true ;
}
}
return false ;
}
SUTransformation FComponentDefinition : : GetMeshBakedTransform ( )
{
if ( ! ShouldBakeTransformIntoMesh ( ) )
{
SUTransformation Transform ;
SUTransformationScale ( & Transform , 1.0 ) ;
return Transform ;
}
FComponentInstance * ComponentInstance = Instances . Array ( ) [ 0 ] ;
SUTransformation Transform ;
SUComponentInstanceGetTransform ( ComponentInstance - > GetComponentInstanceRef ( ) , & Transform ) ;
// Don't bake translation
Transform . values [ 12 ] = 0 ;
Transform . values [ 13 ] = 0 ;
Transform . values [ 14 ] = 0 ;
// and uniform scale
Transform . values [ 15 ] = 1 ;
// There's no need to bake translation and uniform scale into mesh and keeping those (at least translation) at actor transform is more convenient
return Transform ;
}
void FComponentDefinition : : GetLocalTransform ( FComponentInstance & ComponentInstance , SUTransformation & SComponentInstanceLocalTransform )
{
if ( ShouldBakeTransformIntoMesh ( ) )
{
SUTransformation SComponentInstanceTransform ;
SUComponentInstanceGetTransform ( ComponentInstance . GetComponentInstanceRef ( ) , & SComponentInstanceTransform ) ;
// Local instance transform rotation part is baked into mesh, scale and translation aren't
double UniformScale = SComponentInstanceTransform . values [ 15 ] ;
const SUVector3D Translation { SComponentInstanceTransform . values [ 12 ] , SComponentInstanceTransform . values [ 13 ] , SComponentInstanceTransform . values [ 14 ] } ;
SUTransformation Transform ;
SUTransformationTranslation ( & Transform , & Translation ) ;
Transform . values [ 15 ] = UniformScale ;
SComponentInstanceLocalTransform = Transform ;
}
else
{
SUComponentInstanceGetTransform ( ComponentInstance . GetComponentInstanceRef ( ) , & SComponentInstanceLocalTransform ) ;
}
}
void FComponentDefinition : : ComponentInstancePropertiesInvalidated ( )
{
// If mesh transform baking is affecting geometry then invalidate geometry
// i.e. if transform should be baked or need to bake it has changed
bool bShouldBakeTransformIntoMesh = ShouldBakeTransformIntoMesh ( ) ;
if ( bShouldBakeTransformIntoMesh | | ( bShouldBakeTransformIntoMesh ! = bBakeTransformIntoMesh ) )
{
InvalidateDefinitionGeometry ( ) ;
}
}
2021-03-18 15:20:03 -04:00
FString FComponentDefinition : : GetSketchupSourceGUID ( )
{
// Retrieve the SketchUp component definition IFC GUID.
return SuGetString ( SUComponentDefinitionGetGuid , ComponentDefinitionRef ) ;
}
2021-04-08 14:32:07 -04:00
void FComponentDefinition : : LinkComponentInstance ( FComponentInstance * ComponentInstance )
{
Instances . Add ( ComponentInstance ) ;
}
2021-03-18 15:20:03 -04:00
2021-04-08 14:32:07 -04:00
void FComponentDefinition : : UnlinkComponentInstance ( FComponentInstance * ComponentInstance )
{
Instances . Remove ( ComponentInstance ) ;
}
2021-05-25 02:43:26 -04:00
void FComponentDefinition : : RemoveComponentDefinition ( FExportContext & Context )
{
// Remove ComponentDefinition that doesn't have tracked instances
ensure ( ! Instances . Num ( ) ) ;
// todo: might better keep in the Definition's Entities all ComponentInstanceIDs of the tracked entities
// this way we don't need to check whether we are tracking them (inside RemoveComponentInstance)
for ( SUComponentInstanceRef ComponentInstanceRef : GetEntities ( ) . GetComponentInstances ( ) )
{
Context . ComponentInstances . RemoveComponentInstance (
DatasmithSketchUpUtils : : GetComponentID ( ComponentDefinitionRef ) ,
DatasmithSketchUpUtils : : GetComponentInstanceID ( ComponentInstanceRef ) ) ;
}
for ( SUGroupRef GroupRef : GetEntities ( ) . GetGroups ( ) )
{
Context . ComponentInstances . RemoveComponentInstance (
DatasmithSketchUpUtils : : GetComponentID ( ComponentDefinitionRef ) ,
DatasmithSketchUpUtils : : GetGroupID ( GroupRef ) ) ;
}
2021-10-25 20:05:28 -04:00
Context . Materials . UnregisterGeometry ( GetEntities ( ) . EntitiesGeometry . Get ( ) ) ;
2021-05-25 02:43:26 -04:00
Context . EntitiesObjects . UnregisterEntities ( GetEntities ( ) ) ;
}
2021-04-08 14:32:07 -04:00
void FComponentDefinition : : AddInstance ( FExportContext & Context , TSharedPtr < FComponentInstance > Instance )
{
for ( FComponentInstance * ParentInstance : Instances )
{
for ( FNodeOccurence * ParentOccurrence : ParentInstance - > Occurrences )
{
2022-07-20 10:54:47 -04:00
FNodeOccurence & ChildNode = Instance - > CreateNodeOccurrence ( Context , * ParentOccurrence ) ;
Instance - > ParseNode ( Context , ChildNode ) ;
2021-04-08 14:32:07 -04:00
}
}
}
2021-03-18 15:20:03 -04:00
2022-07-20 10:54:47 -04:00
void FComponentDefinition : : AddImage ( FExportContext & Context , TSharedPtr < FImage > Image )
{
for ( FComponentInstance * ParentInstance : Instances )
{
for ( FNodeOccurence * ParentOccurrence : ParentInstance - > Occurrences )
{
// todo: remove djupcacation with FComponentDefinition::AddInstance
Image - > CreateNodeOccurrence ( Context , * ParentOccurrence ) ;
}
}
}
void FEntityWithEntities : : UpdateOccurrence ( FExportContext & Context , FNodeOccurence & Node )
2021-03-05 19:27:14 -04:00
{
2021-11-07 23:43:01 -05:00
if ( Node . MaterialOverride )
{
Node . MaterialOverride - > UnregisterInstance ( Context , & Node ) ;
}
2021-03-05 19:27:14 -04:00
FString EffectiveLayerName = SuGetString ( SULayerGetName , Node . EffectiveLayerRef ) ;
FDefinition * EntityDefinition = GetDefinition ( ) ;
DatasmithSketchUp : : FEntitiesGeometry & EntitiesGeometry = * EntityDefinition - > GetEntities ( ) . EntitiesGeometry ;
2021-04-08 14:32:07 -04:00
// Set the effective inherited material ID.
if ( ! GetAssignedMaterial ( Node . InheritedMaterialID ) )
{
Node . InheritedMaterialID = Node . ParentNode - > InheritedMaterialID ;
}
2022-06-10 12:47:33 -04:00
FString MeshActorLabel = Node . GetActorLabel ( ) ;
2021-03-05 19:27:14 -04:00
// Update Datasmith Mesh Actors
for ( int32 MeshIndex = 0 ; MeshIndex < Node . MeshActors . Num ( ) ; + + MeshIndex )
{
const TSharedPtr < IDatasmithMeshActorElement > & MeshActor = Node . MeshActors [ MeshIndex ] ;
2022-06-10 12:47:33 -04:00
MeshActor - > SetLabel ( * MeshActorLabel ) ;
2022-08-19 00:48:28 -04:00
MeshActor - > SetLayer ( * FDatasmithUtils : : SanitizeObjectName ( EffectiveLayerName ) ) ;
2021-03-05 19:27:14 -04:00
// Update Override(Inherited) Material
// todo: set inherited material only on mesh actors that have faces with default material, right now setting on every mesh, hot harmful but excessive
if ( EntitiesGeometry . IsMeshUsingInheritedMaterial ( MeshIndex ) )
{
2022-07-04 11:04:35 -04:00
Context . Materials . SetMeshActorOverrideMaterial ( Node , EntitiesGeometry , MeshActor ) ;
2021-03-05 19:27:14 -04:00
}
}
}
2022-07-20 10:54:47 -04:00
FNodeOccurence & FEntity : : CreateNodeOccurrence ( FExportContext & Context , FNodeOccurence & ParentNode )
{
FNodeOccurence * Occurrence = new FNodeOccurence ( & ParentNode , * this ) ;
ParentNode . Children . Add ( Occurrence ) ;
Occurrences . Add ( Occurrence ) ;
return * Occurrence ;
}
void FEntity : : DeleteOccurrence ( FExportContext & Context , FNodeOccurence * Node )
{
EntityOccurrenceVisible ( Node , false ) ;
Occurrences . Remove ( Node ) ;
delete Node ;
}
void FEntity : : RemoveOccurrences ( FExportContext & Context )
{
TArray < FNodeOccurence * > OccurencesCopy = Occurrences ; // Copy RemoveOccurrence modifies the array
for ( FNodeOccurence * Occurrence : OccurencesCopy )
{
Occurrence - > RemoveOccurrence ( Context ) ;
}
}
2021-04-08 14:32:07 -04:00
void FEntity : : UpdateEntityGeometry ( FExportContext & Context )
{
if ( bGeometryInvalidated )
{
InvalidateOccurrencesGeometry ( Context ) ;
bGeometryInvalidated = false ;
}
}
void FEntity : : UpdateEntityProperties ( FExportContext & Context )
{
if ( bPropertiesInvalidated )
{
// We can't just update Occurrence properties
// When transform changes each node needs its parent transform to be already calculated
// So we postpone occurrence nodes updates until we do update with respect to hierarchy(top first)
InvalidateOccurrencesProperties ( Context ) ;
2021-05-11 01:10:20 -04:00
UpdateMetadata ( Context ) ;
2021-04-08 14:32:07 -04:00
bPropertiesInvalidated = false ;
}
}
void FEntity : : EntityOccurrenceVisible ( FNodeOccurence * Node , bool bVisible )
{
if ( bVisible )
{
VisibleNodes . Add ( Node ) ;
}
else
{
if ( VisibleNodes . Contains ( Node ) )
{
VisibleNodes . Remove ( Node ) ;
}
}
2022-07-20 10:54:47 -04:00
}
void FEntity : : SetParentDefinition ( FExportContext & Context , FDefinition * InParent )
{
if ( ! IsParentDefinition ( InParent ) ) // Changing parent
{
// If we are re-parenting(i.e. entity was previously owned by another Definition - this happens
// when say a ComponentInstance was selected in UI and "Make Group" was performed.
if ( Parent )
{
RemoveOccurrences ( Context ) ;
Occurrences . Reset ( ) ; // Clear occurrences - RemoveOccurrences doesn't do it(not needed during ComponentInstance removal)
}
Parent = InParent ;
}
}
void FEntityWithEntities : : EntityOccurrenceVisible ( FNodeOccurence * Node , bool bVisible )
{
Super : : EntityOccurrenceVisible ( Node , bVisible ) ;
2021-04-08 14:32:07 -04:00
GetDefinition ( ) - > EntityVisible ( this , VisibleNodes . Num ( ) > 0 ) ;
}
2022-07-20 10:54:47 -04:00
FComponentInstance : : ~ FComponentInstance ( )
{
}
2021-03-05 19:27:14 -04:00
FDefinition * FComponentInstance : : GetDefinition ( )
{
return & Definition ;
}
bool FComponentInstance : : GetAssignedMaterial ( FMaterialIDType & MaterialId )
{
2021-04-08 14:32:07 -04:00
SUComponentInstanceRef ComponentInstanceRef = GetComponentInstanceRef ( ) ;
2021-03-05 19:27:14 -04:00
SUMaterialRef MaterialRef = DatasmithSketchUpUtils : : GetMaterial ( ComponentInstanceRef ) ;
// Set the effective inherited material ID.
if ( SUIsValid ( MaterialRef ) )
{
// Get the material ID of the SketckUp component instance material.
MaterialId = DatasmithSketchUpUtils : : GetMaterialID ( MaterialRef ) ;
return true ;
}
return false ;
}
2021-03-18 15:20:03 -04:00
void FComponentInstance : : UpdateOccurrence ( FExportContext & Context , FNodeOccurence & Node )
2021-03-05 19:27:14 -04:00
{
2022-07-04 11:04:35 -04:00
Node . EffectiveLayerRef = DatasmithSketchUpUtils : : GetEffectiveLayer ( GetComponentInstanceRef ( ) , Node . ParentNode - > EffectiveLayerRef ) ;
2022-06-10 12:47:33 -04:00
if ( FDefinition * EntityDefinition = GetDefinition ( ) )
{
2022-08-19 00:48:28 -04:00
BuildNodeNames ( Node ) ;
2022-06-10 12:47:33 -04:00
}
2021-03-05 19:27:14 -04:00
// Set the actor label used in the Unreal UI.
2022-08-19 00:48:28 -04:00
Node . DatasmithActorElement - > SetLabel ( * Node . GetActorLabel ( ) ) ;
2021-03-05 19:27:14 -04:00
// Retrieve the SketchUp component instance effective layer name.
FString SEffectiveLayerName ;
SEffectiveLayerName = SuGetString ( SULayerGetName , Node . EffectiveLayerRef ) ;
// Set the Datasmith actor layer name.
Node . DatasmithActorElement - > SetLayer ( * FDatasmithUtils : : SanitizeObjectName ( SEffectiveLayerName ) ) ;
2022-06-16 00:24:28 -04:00
// Compute the world transform of the SketchUp component instance.
SUTransformation LocalTransform ;
Definition . GetLocalTransform ( * this , LocalTransform ) ;
SUTransformation WorldTransform ;
SUTransformationMultiply ( & Node . ParentNode - > WorldTransform , & LocalTransform , & WorldTransform ) ;
Node . WorldTransform = WorldTransform ; // Store world transform to be used by children to compute its
2021-03-05 19:27:14 -04:00
// Set the Datasmith actor world transform.
2022-06-16 00:24:28 -04:00
DatasmithSketchUpUtils : : SetActorTransform ( Node . DatasmithActorElement , Node . WorldTransform ) ;
2021-03-05 19:27:14 -04:00
2021-05-11 01:10:20 -04:00
Node . ResetMetadataElement ( Context ) ; // todo: can enable/disable metadata export by toggling this code
FillOccurrenceActorMetadata ( Node ) ;
2021-03-18 15:20:03 -04:00
// Update Datasmith Mesh Actors
for ( int32 MeshIndex = 0 ; MeshIndex < Node . MeshActors . Num ( ) ; + + MeshIndex )
{
const TSharedPtr < IDatasmithMeshActorElement > & MeshActor = Node . MeshActors [ MeshIndex ] ;
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
// Set mesh actor transform after node transform
MeshActor - > SetScale ( Node . DatasmithActorElement - > GetScale ( ) ) ;
MeshActor - > SetRotation ( Node . DatasmithActorElement - > GetRotation ( ) ) ;
MeshActor - > SetTranslation ( Node . DatasmithActorElement - > GetTranslation ( ) ) ;
}
Super : : UpdateOccurrence ( Context , Node ) ;
2021-03-05 19:27:14 -04:00
}
int64 FComponentInstance : : GetPersistentId ( )
{
2021-04-08 14:32:07 -04:00
SUComponentInstanceRef ComponentInstanceRef = GetComponentInstanceRef ( ) ;
2021-03-05 19:27:14 -04:00
return DatasmithSketchUpUtils : : GetComponentPID ( ComponentInstanceRef ) ;
}
FString FComponentInstance : : GetName ( )
{
2021-04-08 14:32:07 -04:00
SUComponentInstanceRef InSComponentInstanceRef = GetComponentInstanceRef ( ) ;
2021-03-05 19:27:14 -04:00
FString SComponentInstanceName ;
return SuGetString ( SUComponentInstanceGetName , InSComponentInstanceRef ) ;
}
2021-05-11 01:10:20 -04:00
void FComponentInstance : : UpdateMetadata ( FExportContext & Context )
{
ParsedMetadata = MakeUnique < FMetadata > ( SUComponentInstanceToEntity ( GetComponentInstanceRef ( ) ) ) ;
}
2022-06-16 00:24:28 -04:00
void FComponentInstance : : UpdateEntityProperties ( FExportContext & Context )
{
if ( bPropertiesInvalidated )
{
Definition . ComponentInstancePropertiesInvalidated ( ) ;
}
FEntity : : UpdateEntityProperties ( Context ) ;
}
2022-08-19 00:48:28 -04:00
void FComponentInstance : : UpdateOccurrenceMeshActors ( FExportContext & Context , FNodeOccurence & Node )
{
BuildNodeNames ( Node ) ;
FEntityWithEntities : : UpdateOccurrenceMeshActors ( Context , Node ) ;
if ( Node . Children . IsEmpty ( ) & & ! Node . MeshActors . IsEmpty ( ) ) // Don't make extra actor when geometry node has no children
{
TSharedPtr < IDatasmithMeshActorElement > MeshActor = Node . MeshActors [ 0 ] ;
Node . DatasmithActorElement = MeshActor ;
SetupActor ( Context , Node ) ;
}
else
{
Node . DatasmithActorElement = FDatasmithSceneFactory : : CreateActor ( * Node . GetActorName ( ) ) ;
SetupActor ( Context , Node ) ;
for ( TSharedPtr < IDatasmithMeshActorElement > MeshActor : Node . MeshActors )
{
Node . DatasmithActorElement - > AddChild ( MeshActor ) ;
}
for ( FNodeOccurence * Child : Node . Children )
{
if ( Child - > DatasmithActorElement )
{
Node . DatasmithActorElement - > AddChild ( Child - > DatasmithActorElement ) ;
}
}
}
}
void FComponentInstance : : ResetOccurrenceActors ( FExportContext & Context , FNodeOccurence & Node )
{
Node . ResetNodeActors ( Context ) ;
}
2022-07-20 10:54:47 -04:00
void FComponentInstance : : ParseNode ( FExportContext & Context , FNodeOccurence & Node )
{
FDefinition * EntityDefinition = GetDefinition ( ) ;
if ( ! EntityDefinition )
{
return ;
}
EntityDefinition - > ParseNode ( Context , Node ) ;
}
2021-04-08 14:32:07 -04:00
void FComponentInstance : : InvalidateOccurrencesGeometry ( FExportContext & Context )
{
for ( FNodeOccurence * Node : Occurrences )
{
Node - > InvalidateMeshActors ( ) ;
// Should invalidate transform to trigger transform update for mesh actors
// todo: can simplify this
// - separate Transform invalidation from other properties? If it should give any improvement?
// - or just update mesh actors transforms? we can't do it here though as transform can be invalidated by ancestors change later when occurrences are updated
// - add another flag to invalidate just mesh actors properties and update them separately
Node - > InvalidateProperties ( ) ;
2021-03-18 15:20:03 -04:00
}
}
void FComponentInstance : : InvalidateOccurrencesProperties ( FExportContext & Context )
{
2021-05-25 02:43:26 -04:00
// When ComponentInstance is modified we need to determine if its visibility might have changed foremost
// because this determines whether corresponding node would exist in the Datasmith scene
// Two things affect this - Hidden instance flag and layer(tag):
bool bNewHidden = false ;
SUDrawingElementRef DrawingElementRef = SUComponentInstanceToDrawingElement ( GetComponentInstanceRef ( ) ) ;
SUDrawingElementGetHidden ( DrawingElementRef , & bNewHidden ) ;
SUDrawingElementGetLayer ( DrawingElementRef , & LayerRef ) ;
2022-06-21 00:30:41 -04:00
bool bNewLayerVisible = Context . Layers . IsLayerVisible ( LayerRef ) ;
2021-05-25 02:43:26 -04:00
if ( bHidden ! = bNewHidden | | bLayerVisible ! = bNewLayerVisible )
2021-03-18 15:20:03 -04:00
{
2021-04-08 14:32:07 -04:00
bHidden = bNewHidden ;
2021-05-25 02:43:26 -04:00
bLayerVisible = bNewLayerVisible ;
2021-04-08 14:32:07 -04:00
for ( FNodeOccurence * Node : Occurrences )
2021-03-18 15:20:03 -04:00
{
2021-04-08 14:32:07 -04:00
Node - > bVisibilityInvalidated = true ;
2021-03-18 15:20:03 -04:00
}
}
2021-04-08 14:32:07 -04:00
for ( FNodeOccurence * Node : Occurrences )
{
Node - > InvalidateProperties ( ) ;
}
2021-03-18 15:20:03 -04:00
}
FComponentInstanceIDType FComponentInstance : : GetComponentInstanceId ( )
{
2021-04-08 14:32:07 -04:00
return DatasmithSketchUpUtils : : GetComponentInstanceID ( GetComponentInstanceRef ( ) ) ;
2021-03-18 15:20:03 -04:00
}
2021-04-08 14:32:07 -04:00
SUComponentInstanceRef FComponentInstance : : GetComponentInstanceRef ( )
{
return SUComponentInstanceFromEntity ( EntityRef ) ;
}
2021-05-11 01:10:20 -04:00
void FComponentInstance : : FillOccurrenceActorMetadata ( FNodeOccurence & Node )
{
if ( ! Node . DatasmithMetadataElement )
{
return ;
}
2021-05-25 02:43:26 -04:00
//SUTransformation SComponentInstanceWorldTransform = DatasmithSketchUpUtils::GetComponentInstanceTransform(GetComponentInstanceRef(), Node.ParentNode->WorldTransform);
//double Volume;
//SUComponentInstanceComputeVolume(GetComponentInstanceRef(), &SComponentInstanceWorldTransform, &Volume);
2021-05-11 01:10:20 -04:00
// Add original instance/component names to metadata
TSharedPtr < IDatasmithKeyValueProperty > EntityName = FDatasmithSceneFactory : : CreateKeyValueProperty ( TEXT ( " Instance " ) ) ;
EntityName - > SetValue ( * GetName ( ) ) ;
Node . DatasmithMetadataElement - > AddProperty ( EntityName ) ;
TSharedPtr < IDatasmithKeyValueProperty > DefinitionName = FDatasmithSceneFactory : : CreateKeyValueProperty ( TEXT ( " Definition " ) ) ;
DefinitionName - > SetValue ( * GetDefinition ( ) - > GetSketchupSourceName ( ) ) ;
Node . DatasmithMetadataElement - > AddProperty ( DefinitionName ) ;
2022-07-07 02:05:52 -04:00
TSharedPtr < IDatasmithKeyValueProperty > DefinitionIdName = FDatasmithSceneFactory : : CreateKeyValueProperty ( TEXT ( " DefinitionIdName " ) ) ;
DefinitionIdName - > SetValue ( * GetDefinition ( ) - > GetSketchupSourceId ( ) ) ;
Node . DatasmithMetadataElement - > AddProperty ( DefinitionIdName ) ;
2021-05-11 01:10:20 -04:00
// Add instance metadata
if ( ParsedMetadata )
{
ParsedMetadata - > AddMetadata ( Node . DatasmithMetadataElement ) ;
}
// Add definition metadata
GetDefinition ( ) - > FillOccurrenceActorMetadata ( Node ) ;
}
2022-07-20 10:54:47 -04:00
void FImageCollection : : LayerModified ( FEntityIDType LayerId )
{
for ( const TPair < FEntityIDType , TSharedPtr < FImage > > & IdImage : Images )
{
TSharedPtr < FImage > Image = IdImage . Value ;
if ( SUIsValid ( Image - > LayerRef ) & & ( LayerId = = DatasmithSketchUpUtils : : GetEntityID ( SULayerToEntity ( Image - > LayerRef ) ) ) )
{
Image - > InvalidateEntityProperties ( ) ;
}
}
}
2021-04-08 14:32:07 -04:00
void FComponentInstance : : UpdateOccurrenceVisibility ( FExportContext & Context , FNodeOccurence & Node )
{
// Parent node, component instance and layer - all should be visible to have node visible
2021-09-06 12:23:53 -04:00
Node . SetVisibility ( Node . ParentNode - > bVisible & & ! bHidden & & bLayerVisible ) ;
2021-04-08 14:32:07 -04:00
2022-07-20 10:54:47 -04:00
EntityOccurrenceVisible ( & Node , Node . bVisible ) ;
2021-04-08 14:32:07 -04:00
if ( Node . bVisible )
{
2022-08-19 00:48:28 -04:00
Node . InvalidateProperties ( ) ;
2021-04-08 14:32:07 -04:00
Node . InvalidateMeshActors ( ) ;
}
else
{
2021-05-25 02:43:26 -04:00
Node . RemoveDatasmithActorHierarchy ( Context ) ;
2021-03-18 15:20:03 -04:00
}
2021-04-08 14:32:07 -04:00
for ( FNodeOccurence * ChildNode : Node . Children )
{
ChildNode - > bVisibilityInvalidated = true ;
}
}
void FComponentInstance : : RemoveComponentInstance ( FExportContext & Context )
{
Definition . EntityVisible ( this , false ) ;
Definition . UnlinkComponentInstance ( this ) ;
RemoveOccurrences ( Context ) ;
2021-05-25 02:43:26 -04:00
// If there's no Instances of this removed ComponentInstance we need to stop tracking Definition's Entities
// Details:
// SketchUp api doesn't fire event for those child Entities although they are effectively removed from Model
// Sketchup.active_model.definitions.purge_unused will deallocate those dangling Entities leaving references invalid
// Although SU API tries to notify about this but fails e.g. DefinitionObserver.onComponentInstanceRemoved/onEraseEntity passes already deleted Entity making this callback useless
if ( ! Definition . Instances . Num ( ) )
{
Definition . RemoveComponentDefinition ( Context ) ;
}
2021-03-18 15:20:03 -04:00
}
2021-03-05 19:27:14 -04:00
2022-07-20 10:54:47 -04:00
FModel : : FModel ( FModelDefinition & InDefinition ) : Super ( SU_INVALID ) , Definition ( InDefinition )
2021-03-05 19:27:14 -04:00
{
}
FDefinition * FModel : : GetDefinition ( )
{
return & Definition ;
}
bool FModel : : GetAssignedMaterial ( FMaterialIDType & MaterialId )
{
MaterialId = FMaterial : : INHERITED_MATERIAL_ID ;
return true ;
}
int64 FModel : : GetPersistentId ( )
{
return 0 ;
}
FString FModel : : GetName ( )
{
return " " ;
}
2021-03-18 15:20:03 -04:00
2021-04-08 14:32:07 -04:00
void FModel : : InvalidateOccurrencesGeometry ( FExportContext & Context )
2021-03-18 15:20:03 -04:00
{
2021-04-08 14:32:07 -04:00
Context . RootNode - > InvalidateMeshActors ( ) ;
Context . RootNode - > InvalidateProperties ( ) ;
2021-03-18 15:20:03 -04:00
}
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
void FModel : : InvalidateOccurrencesProperties ( FExportContext & Context )
{
2021-04-08 14:32:07 -04:00
Context . RootNode - > InvalidateProperties ( ) ;
}
void FModel : : UpdateOccurrenceVisibility ( FExportContext & Context , FNodeOccurence & Node )
{
Node . SetVisibility ( true ) ;
2022-07-20 10:54:47 -04:00
EntityOccurrenceVisible ( & Node , true ) ;
2021-04-08 14:32:07 -04:00
}
2021-05-11 01:10:20 -04:00
void FModel : : UpdateMetadata ( FExportContext & Context )
{
}
2022-08-19 00:48:28 -04:00
void FModel : : UpdateOccurrenceMeshActors ( FExportContext & Context , FNodeOccurence & Node )
{
FEntityWithEntities : : UpdateOccurrenceMeshActors ( Context , Node ) ;
for ( TSharedPtr < IDatasmithMeshActorElement > MeshActor : Node . MeshActors )
{
Context . DatasmithScene - > AddActor ( MeshActor ) ;
}
}
void FModel : : ResetOccurrenceActors ( FExportContext & Context , FNodeOccurence & Node )
{
// Model actors are MeshActors added to DatasmithScene root
for ( TSharedPtr < IDatasmithMeshActorElement > MeshActor : Node . MeshActors )
{
Context . DatasmithScene - > RemoveActor ( MeshActor , EDatasmithActorRemovalRule : : RemoveChildren ) ;
}
}
2022-07-20 10:54:47 -04:00
FDefinition : : FDefinition ( ) : bMeshesAdded ( false )
, bGeometryInvalidated ( true )
, bPropertiesInvalidated ( true )
{ }
FDefinition : : ~ FDefinition ( )
{ }
2021-04-08 14:32:07 -04:00
void FDefinition : : EntityVisible ( FEntity * Entity , bool bVisible )
{
if ( bVisible )
{
VisibleEntities . Add ( Entity ) ;
}
else
{
if ( VisibleEntities . Contains ( Entity ) )
{
VisibleEntities . Remove ( Entity ) ;
}
}
}
void FDefinition : : UpdateDefinition ( FExportContext & Context )
{
if ( VisibleEntities . Num ( ) )
{
if ( bGeometryInvalidated )
{
UpdateGeometry ( Context ) ;
InvalidateInstancesGeometry ( Context ) ; // Make sure instances keep up with definition changes
bMeshesAdded = false ;
bGeometryInvalidated = false ;
}
2021-05-11 01:10:20 -04:00
if ( bPropertiesInvalidated )
{
2022-07-20 10:54:47 -04:00
// Currently SketchUp has no Observer for Component Definition attributes.
// So this code is only executed on export
// todo: implement attributes sync once api is available
2021-05-11 01:10:20 -04:00
UpdateMetadata ( Context ) ;
InvalidateInstancesMetadata ( Context ) ; // Make sure instances keep up with definition changes
bPropertiesInvalidated = false ;
}
2021-04-08 14:32:07 -04:00
if ( ! bMeshesAdded )
{
GetEntities ( ) . AddMeshesToDatasmithScene ( Context ) ;
bMeshesAdded = true ;
}
}
else
{
if ( bMeshesAdded )
{
// Without references meshes will be cleaned from datasmith scene
// bMeshesAdded = false; // todo: SceneCleanUp - do maintenance myself?
GetEntities ( ) . RemoveMeshesFromDatasmithScene ( Context ) ;
bMeshesAdded = false ;
}
}
2021-03-18 15:20:03 -04:00
}