PyObject *py_ue_skeletal_mesh_register_morph_target(ue_PyUObject *self, PyObject * args) { ue_py_check(self); PyObject *py_morph; if (!PyArg_ParseTuple(args, "O:skeletal_mesh_register_morph_target", &py_morph)) { return nullptr; } USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a SkeletalMesh"); UMorphTarget *morph = ue_py_check_type<UMorphTarget>(py_morph); if (!morph) return PyErr_Format(PyExc_Exception, "argument is not a MorphTarget"); #if ENGINE_MINOR_VERSION > 16 if (!morph->HasValidData()) return PyErr_Format(PyExc_Exception, "the MorphTarget has no valid data"); #endif mesh->PreEditChange(nullptr); mesh->RegisterMorphTarget(morph); mesh->PostEditChange(); mesh->MarkPackageDirty(); Py_RETURN_NONE; }
void SAnimationSegmentViewport::InitSkeleton() { UObject *Object = NULL; AnimRefPropertyHandle->GetValue(Object); UAnimSequenceBase *AnimSequence = Cast<UAnimSequenceBase>(Object); USkeleton *Skeleton = NULL; if(AnimSequence != NULL) { Skeleton = AnimSequence->GetSkeleton(); } if( PreviewComponent != NULL && Skeleton != NULL ) { USkeletalMesh* PreviewMesh = Skeleton->GetAssetPreviewMesh(AnimSequence); if (PreviewMesh) { UAnimSingleNodeInstance * Preview = PreviewComponent->PreviewInstance; if((Preview == NULL || Preview->GetCurrentAsset() != AnimSequence) || (PreviewComponent->SkeletalMesh != PreviewMesh)) { PreviewComponent->SetSkeletalMesh(PreviewMesh); PreviewComponent->EnablePreview(true, AnimSequence, NULL); PreviewComponent->PreviewInstance->SetLooping(true); //Place the camera at a good viewer position FVector NewPosition = LevelViewportClient->GetViewLocation(); NewPosition.Normalize(); LevelViewportClient->SetViewLocation(NewPosition * (PreviewMesh->GetImportedBounds().SphereRadius*1.5f)); } } } TargetSkeleton = Skeleton; }
void FLODUtilities::RemoveLOD(FSkeletalMeshUpdateContext& UpdateContext, int32 DesiredLOD ) { USkeletalMesh* SkeletalMesh = UpdateContext.SkeletalMesh; FSkeletalMeshResource* SkelMeshResource = SkeletalMesh->GetImportedResource(); if( SkelMeshResource->LODModels.Num() == 1 ) { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "NoLODToRemove", "No LODs to remove!") ); return; } // Now display combo to choose which LOD to remove. TArray<FString> LODStrings; LODStrings.AddZeroed( SkelMeshResource->LODModels.Num()-1 ); for(int32 i=0; i<SkelMeshResource->LODModels.Num()-1; i++) { LODStrings[i] = FString::Printf( TEXT("%d"), i+1 ); } check( SkeletalMesh->LODInfo.Num() == SkelMeshResource->LODModels.Num() ); // If its a valid LOD, kill it. if( DesiredLOD > 0 && DesiredLOD < SkelMeshResource->LODModels.Num() ) { //We'll be modifying the skel mesh data so reregister //TODO - do we need to reregister something else instead? FMultiComponentReregisterContext ReregisterContext(UpdateContext.AssociatedComponents); // Release rendering resources before deleting LOD SkelMeshResource->ReleaseResources(); // Block until this is done FlushRenderingCommands(); SkelMeshResource->LODModels.RemoveAt(DesiredLOD); SkeletalMesh->LODInfo.RemoveAt(DesiredLOD); SkeletalMesh->InitResources(); RefreshLODChange(SkeletalMesh); // Set the forced LOD to Auto. for(auto Iter = UpdateContext.AssociatedComponents.CreateIterator(); Iter; ++Iter) { USkinnedMeshComponent* SkinnedComponent = Cast<USkinnedMeshComponent>(*Iter); if(SkinnedComponent) { SkinnedComponent->ForcedLodModel = 0; } } //Notify calling system of change UpdateContext.OnLODChanged.ExecuteIfBound(); // Mark things for saving. SkeletalMesh->MarkPackageDirty(); } }
PyObject *py_ue_skeletal_mesh_lods_num(ue_PyUObject *self, PyObject * args) { ue_py_check(self); USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif return PyLong_FromLong(resource->LODModels.Num()); }
PyObject *py_ue_skeletal_mesh_get_raw_indices(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; if (!PyArg_ParseTuple(args, "|i:skeletal_mesh_get_raw_indices", &lod_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif PyObject *py_list = PyList_New(0); int32 *raw_indices = (int32 *)model.RawPointIndices.Lock(LOCK_READ_ONLY); int32 *indices = (int32 *)FMemory_Alloca(model.RawPointIndices.GetBulkDataSize()); FMemory::Memcpy(indices, raw_indices, model.RawPointIndices.GetBulkDataSize()); model.RawPointIndices.Unlock(); for (int32 index = 0; index < model.RawPointIndices.GetBulkDataSize() / sizeof(int32); index++) { PyList_Append(py_list, PyLong_FromLong(indices[index])); } return py_list; }
PyObject *py_ue_skeletal_mesh_get_soft_vertices(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; int section_index = 0; if (!PyArg_ParseTuple(args, "|ii:skeletal_mesh_get_soft_vertices", &lod_index, §ion_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); PyObject *py_list = PyList_New(0); for (int32 i = 0; i < model.Sections[section_index].SoftVertices.Num(); i++) { PyList_Append(py_list, py_ue_new_fsoft_skin_vertex(model.Sections[section_index].SoftVertices[i])); } return py_list; }
PyObject *py_ue_skeletal_mesh_set_skeleton(ue_PyUObject * self, PyObject * args) { ue_py_check(self); PyObject *py_skeleton; if (!PyArg_ParseTuple(args, "O:skeletal_mesh_set_skeleton", &py_skeleton)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "UObject is not a USkeletalMesh."); USkeleton *skeleton = ue_py_check_type<USkeleton>(py_skeleton); if (!skeleton) return PyErr_Format(PyExc_Exception, "argument is not a USkeleton."); mesh->ReleaseResources(); mesh->ReleaseResourcesFence.Wait(); mesh->Skeleton = skeleton; mesh->RefSkeleton = skeleton->GetReferenceSkeleton(); mesh->RefBasesInvMatrix.Empty(); mesh->CalculateInvRefMatrices(); #if WITH_EDITOR mesh->PostEditChange(); #endif mesh->InitResources(); mesh->MarkPackageDirty(); Py_RETURN_NONE; }
PyObject *py_ue_skeletal_mesh_to_import_vertex_map(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; if (!PyArg_ParseTuple(args, "|i:skeletal_mesh_to_import_vertex_map", &lod_index)) { return nullptr; } USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &LODModel = resource->LODModels[lod_index]; #endif PyObject *py_list = PyList_New(0); for (int32 value : LODModel.MeshToImportVertexMap) { PyList_Append(py_list, PyLong_FromLong(value)); } return py_list; }
PyObject *py_ue_skeletal_mesh_sections_num(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; if (!PyArg_ParseTuple(args, "|i:skeletal_mesh_sections_num", &lod_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); return PyLong_FromLong(resource->LODModels[lod_index].Sections.Num()); }
PyObject *py_ue_skeletal_mesh_get_required_bones(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; if (!PyArg_ParseTuple(args, "|i:skeletal_mesh_get_required_bones", &lod_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif PyObject *py_list = PyList_New(0); for (uint16 index : model.RequiredBones) { PyList_Append(py_list, PyLong_FromUnsignedLong(index)); } return py_list; }
UObject* USpriterImporterFactory::FactoryCreateText(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const TCHAR*& Buffer, const TCHAR* BufferEnd, FFeedbackContext* Warn) { Flags |= RF_Transactional; FEditorDelegates::OnAssetPreImport.Broadcast(this, InClass, InParent, InName, Type); FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools"); bool bLoadedSuccessfully = true; const FString CurrentFilename = UFactory::GetCurrentFilename(); FString CurrentSourcePath; FString FilenameNoExtension; FString UnusedExtension; FPaths::Split(CurrentFilename, CurrentSourcePath, FilenameNoExtension, UnusedExtension); const FString LongPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName()); const FString NameForErrors(InName.ToString()); const FString FileContent(BufferEnd - Buffer, Buffer); TSharedPtr<FJsonObject> DescriptorObject = ParseJSON(FileContent, NameForErrors); UPaperSpriterImportData* Result = nullptr; // Parse the file FSpriterSCON DataModel; if (DescriptorObject.IsValid()) { DataModel.ParseFromJSON(DescriptorObject, NameForErrors, /*bSilent=*/ false, /*bPreParseOnly=*/ false); } // Create the new 'hub' asset and convert the data model over if (DataModel.IsValid()) { const bool bSilent = false; Result = NewObject<UPaperSpriterImportData>(InParent, InName, Flags); Result->Modify(); //@TODO: Do some things here maybe? Result->ImportedData = DataModel; // Import the assets in the folders for (const FSpriterFolder& Folder : DataModel.Folders) { for (const FSpriterFile& File : Folder.Files) { const FString RelativeFilename = File.Name.Replace(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive); const FString SourceSpriterFilePath = FPaths::Combine(*CurrentSourcePath, *RelativeFilename); FString RelativeDestPath; FString JustFilename; FString JustExtension; FPaths::Split(RelativeFilename, /*out*/ RelativeDestPath, /*out*/ JustFilename, /*out*/ JustExtension); if (File.FileType == ESpriterFileType::Sprite) { const FString TargetTexturePath = LongPackagePath / TEXT("Textures") / RelativeDestPath; const FString TargetSpritePath = LongPackagePath / TEXT("Sprites") / RelativeDestPath; // Import the texture UTexture2D* ImportedTexture = ImportTexture(SourceSpriterFilePath, TargetTexturePath); if (ImportTexture == nullptr) { SPRITER_IMPORT_ERROR(TEXT("Failed to import texture '%s' while importing '%s'"), *SourceSpriterFilePath, *CurrentFilename); } // Create a sprite from it UPaperSprite* ImportedSprite = CastChecked<UPaperSprite>(CreateNewAsset(UPaperSprite::StaticClass(), TargetSpritePath, JustFilename, Flags)); const ESpritePivotMode::Type PivotMode = ConvertNormalizedPivotPointToPivotMode(File.PivotX, File.PivotY); const double PivotInPixelsX = File.Width * File.PivotX; const double PivotInPixelsY = File.Height * File.PivotY; ImportedSprite->SetPivotMode(PivotMode, FVector2D((float)PivotInPixelsX, (float)PivotInPixelsY)); FSpriteAssetInitParameters SpriteInitParams; SpriteInitParams.SetTextureAndFill(ImportedTexture); GetDefault<UPaperImporterSettings>()->ApplySettingsForSpriteInit(SpriteInitParams); SpriteInitParams.SetPixelsPerUnrealUnit(1.0f); ImportedSprite->InitializeSprite(SpriteInitParams); } else if (File.FileType == ESpriterFileType::Sound) { // Import the sound const FString TargetAssetPath = LongPackagePath / RelativeDestPath; UObject* ImportedSound = ImportAsset(SourceSpriterFilePath, TargetAssetPath); } else if (File.FileType != ESpriterFileType::INVALID) { ensureMsgf(false, TEXT("Importer was not updated when a new entry was added to ESpriterFileType")); } // TMap<FString, class UTexture2D*> ImportedTextures; // TMap<FString, class UPaperSprite> ImportedSprites; } } for (const FSpriterEntity& Entity : DataModel.Entities) { // Extract the common/shared skeleton FBoneHierarchyBuilder HierarchyBuilder; HierarchyBuilder.ProcessHierarchy(Entity); // Create the skeletal mesh const FString TargetMeshName = Entity.Name + TEXT("_SkelMesh"); const FString TargetMeshPath = LongPackagePath; USkeletalMesh* SkeletalMesh = CastChecked<USkeletalMesh>(CreateNewAsset(USkeletalMesh::StaticClass(), TargetMeshPath, TargetMeshName, Flags)); // Create the skeleton const FString TargetSkeletonName = Entity.Name + TEXT("_Skeleton"); const FString TargetSkeletonPath = LongPackagePath; USkeleton* EntitySkeleton = CastChecked<USkeleton>(CreateNewAsset(USkeleton::StaticClass(), TargetSkeletonPath, TargetSkeletonName, Flags)); // Initialize the mesh asset FSkeletalMeshResource* ImportedResource = SkeletalMesh->GetImportedResource(); check(ImportedResource->LODModels.Num() == 0); ImportedResource->LODModels.Empty(); FStaticLODModel& LODModel = *new (ImportedResource->LODModels) FStaticLODModel(); SkeletalMesh->LODInfo.Empty(); SkeletalMesh->LODInfo.AddZeroed(); SkeletalMesh->LODInfo[0].LODHysteresis = 0.02f; FSkeletalMeshOptimizationSettings Settings; // set default reduction settings values SkeletalMesh->LODInfo[0].ReductionSettings = Settings; // Create initial bounding box based on expanded version of reference pose for meshes without physics assets. Can be overridden by artist. // FBox BoundingBox(SkelMeshImportDataPtr->Points.GetData(), SkelMeshImportDataPtr->Points.Num()); // FBox Temp = BoundingBox; // FVector MidMesh = 0.5f*(Temp.Min + Temp.Max); // BoundingBox.Min = Temp.Min + 1.0f*(Temp.Min - MidMesh); // BoundingBox.Max = Temp.Max + 1.0f*(Temp.Max - MidMesh); // // Tuck up the bottom as this rarely extends lower than a reference pose's (e.g. having its feet on the floor). // // Maya has Y in the vertical, other packages have Z. // //BEN const int32 CoordToTuck = bAssumeMayaCoordinates ? 1 : 2; // //BEN BoundingBox.Min[CoordToTuck] = Temp.Min[CoordToTuck] + 0.1f*(Temp.Min[CoordToTuck] - MidMesh[CoordToTuck]); // BoundingBox.Min[2] = Temp.Min[2] + 0.1f*(Temp.Min[2] - MidMesh[2]); // SkeletalMesh->Bounds = FBoxSphereBounds(BoundingBox); // Store whether or not this mesh has vertex colors // SkeletalMesh->bHasVertexColors = SkelMeshImportDataPtr->bHasVertexColors; // Pass the number of texture coordinate sets to the LODModel. Ensure there is at least one UV coord LODModel.NumTexCoords = 1;// FMath::Max<uint32>(1, SkelMeshImportDataPtr->NumTexCoords); // Create the reference skeleton and update LOD0 FReferenceSkeleton& RefSkeleton = SkeletalMesh->RefSkeleton; HierarchyBuilder.CopyToRefSkeleton(RefSkeleton); SkeletalMesh->CalculateRequiredBones(LODModel, RefSkeleton, /*BonesToRemove=*/ nullptr); SkeletalMesh->CalculateInvRefMatrices(); // Initialize the skeleton asset EntitySkeleton->MergeAllBonesToBoneTree(SkeletalMesh); // Point the mesh and skeleton at each other SkeletalMesh->Skeleton = EntitySkeleton; EntitySkeleton->SetPreviewMesh(SkeletalMesh); // Create the animations for (const FSpriterAnimation& Animation : Entity.Animations) { //@TODO: That thing I said... const FString TargetAnimationName = Animation.Name; const FString TargetAnimationPath = LongPackagePath / TEXT("Animations"); UAnimSequence* AnimationAsset = CastChecked<UAnimSequence>(CreateNewAsset(UAnimSequence::StaticClass(), TargetAnimationPath, TargetAnimationName, Flags)); AnimationAsset->SetSkeleton(EntitySkeleton); // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length const float DurationInSeconds = Animation.LengthInMS * 0.001f; AnimationAsset->SequenceLength = FMath::Max<float>(DurationInSeconds, MINIMUM_ANIMATION_LENGTH); const bool bSourceDataExists = (AnimationAsset->SourceRawAnimationData.Num() > 0); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists ? AnimationAsset->SourceRawAnimationData : AnimationAsset->RawAnimationData; int32 TotalNumKeys = 0; for (const FSpriterTimeline& Timeline : Animation.Timelines) { if (Timeline.ObjectType != ESpriterObjectType::Bone) { continue; } const FName BoneName = Entity.Objects[Timeline.ObjectIndex].ObjectName; const int32 RefBoneIndex = EntitySkeleton->GetReferenceSkeleton().FindBoneIndex(BoneName); check(RefBoneIndex != INDEX_NONE); FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); int32 NumKeysForTrack = 0; //@TODO: Quick and dirty resampling code that needs to be replaced (totally ignores curve type, edge cases, etc...) const float ResampleFPS = 30.0f; int32 DesiredNumKeys = FMath::CeilToInt(ResampleFPS * DurationInSeconds); const float TimePerKey = 1.0f / ResampleFPS; float CurrentSampleTime = 0.0f; for (int32 FrameIndex = 0; FrameIndex < DesiredNumKeys; ++FrameIndex) { int32 LowerKeyIndex = 0; for (; LowerKeyIndex < Timeline.Keys.Num(); ++LowerKeyIndex) { if (Timeline.Keys[LowerKeyIndex].TimeInMS * 0.001f > CurrentSampleTime) { --LowerKeyIndex; break; } } if (LowerKeyIndex >= Timeline.Keys.Num()) { LowerKeyIndex = Timeline.Keys.Num() - 1; } int32 UpperKeyIndex = LowerKeyIndex + 1; float UpperKeyTime = 0.0f; if (UpperKeyIndex >= Timeline.Keys.Num()) { UpperKeyTime = DurationInSeconds; if (Animation.bIsLooping) { UpperKeyIndex = 0; } else { UpperKeyIndex = Timeline.Keys.Num() - 1; } } else { UpperKeyTime = Timeline.Keys[UpperKeyIndex].TimeInMS * 0.001f; } const FSpriterFatTimelineKey& TimelineKey0 = Timeline.Keys[LowerKeyIndex]; const FSpriterFatTimelineKey& TimelineKey1 = Timeline.Keys[UpperKeyIndex]; const float LowerKeyTime = TimelineKey0.TimeInMS * 0.001f; const FTransform LocalTransform0 = TimelineKey0.Info.ConvertToTransform(); const FTransform LocalTransform1 = TimelineKey1.Info.ConvertToTransform(); FTransform LocalTransform = LocalTransform0; if (LowerKeyIndex != UpperKeyIndex) { const float Alpha = (CurrentSampleTime - LowerKeyTime) / (UpperKeyTime - LowerKeyTime); LocalTransform.Blend(LocalTransform0, LocalTransform1, Alpha); } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); ++NumKeysForTrack; CurrentSampleTime += TimePerKey; } // // for (const FSpriterFatTimelineKey& TimelineKey : Timeline.Keys) // { // //@TODO: Ignoring TimeInMS // const FTransform LocalTransform = TimelineKey.Info.ConvertToTransform(); // // RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); // RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); // RawTrack.RotKeys.Add(LocalTransform.GetRotation()); // // ++NumKeysForTrack; // } // RawAnimationData.Add(RawTrack); AnimationAsset->AnimationTrackNames.Add(BoneName); // add mapping to skeleton bone track AnimationAsset->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(RefBoneIndex)); TotalNumKeys = FMath::Max(TotalNumKeys, NumKeysForTrack); } AnimationAsset->NumFrames = TotalNumKeys; AnimationAsset->MarkRawDataAsModified(); // compress animation { GWarn->BeginSlowTask(LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if (bSourceDataExists) { AnimationAsset->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress AnimationAsset->PostProcessSequence(); } // run debug mode GWarn->EndSlowTask(); } // NewAnimation = FFbxImporter->ImportAnimations(Skeleton, Outer, SortedLinks, AnimName, TemplateImportData, FBXMeshNodeArray); // // if (NewAnimation) // { // // since to know full path, reimport will need to do same // UFbxAnimSequenceImportData* ImportData = UFbxAnimSequenceImportData::GetImportDataForAnimSequence(NewAnimation, TemplateImportData); // ImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(UFactory::CurrentFilename, NewAnimation); // ImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*UFactory::CurrentFilename).ToString(); // } } } Result->PostEditChange(); } else { // Failed to parse the JSON bLoadedSuccessfully = false; } if (Result != nullptr) { //@TODO: Need to do this // Store the current file path and timestamp for re-import purposes // UAssetImportData* ImportData = UTileMapAssetImportData::GetImportDataForTileMap(Result); // ImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(CurrentFilename, Result); // ImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*CurrentFilename).ToString(); } FEditorDelegates::OnAssetPostImport.Broadcast(this, Result); return Result; }
/** Add a new statistic to the internal map (or update an existing one) from the supplied component */ UPrimitiveStats* Add(UPrimitiveComponent* InPrimitiveComponent, EPrimitiveObjectSets InObjectSet) { // Objects in transient package or transient objects are not part of level. if( InPrimitiveComponent->GetOutermost() == GetTransientPackage() || InPrimitiveComponent->HasAnyFlags( RF_Transient ) ) { return NULL; } // Owned by a default object? Not part of a level either. if(InPrimitiveComponent->GetOuter() && InPrimitiveComponent->GetOuter()->IsDefaultSubobject() ) { return NULL; } UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(InPrimitiveComponent); UModelComponent* ModelComponent = Cast<UModelComponent>(InPrimitiveComponent); USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(InPrimitiveComponent); ULandscapeComponent* LandscapeComponent = Cast<ULandscapeComponent>(InPrimitiveComponent); UObject* Resource = NULL; AActor* ActorOuter = Cast<AActor>(InPrimitiveComponent->GetOuter()); int32 VertexColorMem = 0; int32 InstVertexColorMem = 0; // Calculate number of direct and other lights relevant to this component. int32 LightsLMCount = 0; int32 LightsOtherCount = 0; bool bUsesOnlyUnlitMaterials = InPrimitiveComponent->UsesOnlyUnlitMaterials(); // The static mesh is a static mesh component's resource. if( StaticMeshComponent ) { UStaticMesh* Mesh = StaticMeshComponent->StaticMesh; Resource = Mesh; // Calculate vertex color memory on the actual mesh. if( Mesh && Mesh->RenderData ) { // Accumulate memory for each LOD for( int32 LODIndex = 0; LODIndex < Mesh->RenderData->LODResources.Num(); ++LODIndex ) { VertexColorMem += Mesh->RenderData->LODResources[LODIndex].ColorVertexBuffer.GetAllocatedSize(); } } // Calculate instanced vertex color memory used on the component. for( int32 LODIndex = 0; LODIndex < StaticMeshComponent->LODData.Num(); ++LODIndex ) { // Accumulate memory for each LOD const FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[ LODIndex ]; if( LODInfo.OverrideVertexColors ) { InstVertexColorMem += LODInfo.OverrideVertexColors->GetAllocatedSize(); } } // Calculate the number of lightmap and shadow map lights if( !bUsesOnlyUnlitMaterials ) { if( StaticMeshComponent->LODData.Num() > 0 ) { FStaticMeshComponentLODInfo& ComponentLODInfo = StaticMeshComponent->LODData[0]; if( ComponentLODInfo.LightMap ) { LightsLMCount = ComponentLODInfo.LightMap->LightGuids.Num(); } } } } // A model component is its own resource. else if( ModelComponent ) { // Make sure model component is referenced by level. ULevel* Level = CastChecked<ULevel>(ModelComponent->GetOuter()); if( Level->ModelComponents.Find( ModelComponent ) != INDEX_NONE ) { Resource = ModelComponent->GetModel(); // Calculate the number of lightmap and shadow map lights if( !bUsesOnlyUnlitMaterials ) { const TIndirectArray<FModelElement> Elements = ModelComponent->GetElements(); if( Elements.Num() > 0 ) { if( Elements[0].LightMap ) { LightsLMCount = Elements[0].LightMap->LightGuids.Num(); } } } } } // The skeletal mesh of a skeletal mesh component is its resource. else if( SkeletalMeshComponent ) { USkeletalMesh* Mesh = SkeletalMeshComponent->SkeletalMesh; Resource = Mesh; // Calculate vertex color usage for skeletal meshes if( Mesh ) { FSkeletalMeshResource* SkelMeshResource = Mesh->GetResourceForRendering(); for( int32 LODIndex = 0; LODIndex < SkelMeshResource->LODModels.Num(); ++LODIndex ) { const FStaticLODModel& LODModel = SkelMeshResource->LODModels[ LODIndex ]; VertexColorMem += LODModel.ColorVertexBuffer.GetVertexDataSize(); } } } // The landscape of a landscape component is its resource. else if (LandscapeComponent) { Resource = LandscapeComponent->GetLandscapeProxy(); if (LandscapeComponent->LightMap) { LightsLMCount = LandscapeComponent->LightMap->LightGuids.Num(); } } UWorld* World = InPrimitiveComponent->GetWorld(); // check(World); // @todo: re-instate this check once the GWorld migration has completed /// If we should skip the actor. Skip if the actor has no outer or if we are only showing selected actors and the actor isn't selected const bool bShouldSkip = World == NULL || ActorOuter == NULL || (ActorOuter != NULL && InObjectSet == PrimitiveObjectSets_SelectedObjects && ActorOuter->IsSelected() == false ); // Dont' care about components without a resource. if( Resource // Require actor association for selection and to disregard mesh emitter components. The exception being model components. && (!bShouldSkip || (ModelComponent && InObjectSet != PrimitiveObjectSets_SelectedObjects ) ) // Only list primitives in visible levels && IsInVisibleLevel( InPrimitiveComponent, World ) // Don't list pending kill components. && !InPrimitiveComponent->IsPendingKill() ) { // Retrieve relevant lights. TArray<const ULightComponent*> RelevantLights; World->Scene->GetRelevantLights( InPrimitiveComponent, &RelevantLights ); // Only look for relevant lights if we aren't unlit. if( !bUsesOnlyUnlitMaterials ) { // Lightmap and shadow map lights are calculated above, per component type, infer the "other" light count here LightsOtherCount = RelevantLights.Num() >= LightsLMCount ? RelevantLights.Num() - LightsLMCount : 0; } // Figure out memory used by light and shadow maps and light/ shadow map resolution. int32 LightMapWidth = 0; int32 LightMapHeight = 0; InPrimitiveComponent->GetLightMapResolution( LightMapWidth, LightMapHeight ); int32 LMSMResolution = FMath::Sqrt( LightMapHeight * LightMapWidth ); int32 LightMapData = 0; int32 LegacyShadowMapData = 0; InPrimitiveComponent->GetLightAndShadowMapMemoryUsage( LightMapData, LegacyShadowMapData ); // Check whether we already have an entry for the associated static mesh. UPrimitiveStats** StatsEntryPtr = ResourceToStatsMap.Find( Resource ); if( StatsEntryPtr ) { check(*StatsEntryPtr); UPrimitiveStats* StatsEntry = *StatsEntryPtr; // We do. Update existing entry. StatsEntry->Count++; StatsEntry->Actors.AddUnique(ActorOuter); StatsEntry->RadiusMin = FMath::Min( StatsEntry->RadiusMin, InPrimitiveComponent->Bounds.SphereRadius ); StatsEntry->RadiusMax = FMath::Max( StatsEntry->RadiusMax, InPrimitiveComponent->Bounds.SphereRadius ); StatsEntry->RadiusAvg += InPrimitiveComponent->Bounds.SphereRadius; StatsEntry->LightsLM += LightsLMCount; StatsEntry->LightsOther += LightsOtherCount; StatsEntry->LightMapData += (float)LightMapData / 1024.0f; StatsEntry->LMSMResolution += LMSMResolution; StatsEntry->UpdateNames(); if ( !ModelComponent && !LandscapeComponent ) { // Count instanced sections StatsEntry->InstSections += StatsEntry->Sections; StatsEntry->InstTriangles += StatsEntry->Triangles; } // ... in the case of a model component (aka BSP). if( ModelComponent ) { // If Count represents the Model itself, we do NOT want to increment it now. StatsEntry->Count--; for (const auto& Element : ModelComponent->GetElements()) { StatsEntry->Triangles += Element.NumTriangles; StatsEntry->Sections++; } StatsEntry->InstSections = StatsEntry->Sections; StatsEntry->InstTriangles = StatsEntry->Triangles; } else if( StaticMeshComponent ) { // This stat is used by multiple components so accumulate instanced vertex color memory. StatsEntry->InstVertexColorMem += (float)InstVertexColorMem / 1024.0f; } else if (LandscapeComponent) { // If Count represents the Landscape itself, we do NOT want to increment it now. StatsEntry->Count--; } } else { // We don't. Create new base entry. UPrimitiveStats* NewStatsEntry = NewObject<UPrimitiveStats>(); NewStatsEntry->AddToRoot(); NewStatsEntry->Object = Resource; NewStatsEntry->Actors.AddUnique(ActorOuter); NewStatsEntry->Count = 1; NewStatsEntry->Triangles = 0; NewStatsEntry->InstTriangles = 0; NewStatsEntry->ResourceSize = (float)(FArchiveCountMem(Resource).GetNum() + Resource->GetResourceSize(EResourceSizeMode::Exclusive)) / 1024.0f; NewStatsEntry->Sections = 0; NewStatsEntry->InstSections = 0; NewStatsEntry->RadiusMin = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->RadiusAvg = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->RadiusMax = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->LightsLM = LightsLMCount; NewStatsEntry->LightsOther = (float)LightsOtherCount; NewStatsEntry->LightMapData = (float)LightMapData / 1024.0f; NewStatsEntry->LMSMResolution = LMSMResolution; NewStatsEntry->VertexColorMem = (float)VertexColorMem / 1024.0f; NewStatsEntry->InstVertexColorMem = (float)InstVertexColorMem / 1024.0f; NewStatsEntry->UpdateNames(); // Fix up triangle and section count... // ... in the case of a static mesh component. if( StaticMeshComponent ) { UStaticMesh* StaticMesh = StaticMeshComponent->StaticMesh; if( StaticMesh && StaticMesh->RenderData ) { for( int32 SectionIndex=0; SectionIndex<StaticMesh->RenderData->LODResources[0].Sections.Num(); SectionIndex++ ) { const FStaticMeshSection& StaticMeshSection = StaticMesh->RenderData->LODResources[0].Sections[SectionIndex]; NewStatsEntry->Triangles += StaticMeshSection.NumTriangles; NewStatsEntry->Sections++; } } } // ... in the case of a model component (aka BSP). else if( ModelComponent ) { TIndirectArray<FModelElement> Elements = ModelComponent->GetElements(); for( int32 ElementIndex=0; ElementIndex<Elements.Num(); ElementIndex++ ) { const FModelElement& Element = Elements[ElementIndex]; NewStatsEntry->Triangles += Element.NumTriangles; NewStatsEntry->Sections++; } } // ... in the case of skeletal mesh component. else if( SkeletalMeshComponent ) { USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh; if( SkeletalMesh ) { FSkeletalMeshResource* SkelMeshResource = SkeletalMesh->GetResourceForRendering(); if (SkelMeshResource->LODModels.Num()) { const FStaticLODModel& BaseLOD = SkelMeshResource->LODModels[0]; for( int32 SectionIndex=0; SectionIndex<BaseLOD.Sections.Num(); SectionIndex++ ) { const FSkelMeshSection& Section = BaseLOD.Sections[SectionIndex]; NewStatsEntry->Triangles += Section.NumTriangles; NewStatsEntry->Sections++; } } } } else if (LandscapeComponent) { TSet<UTexture2D*> UniqueTextures; for (auto ItComponents = LandscapeComponent->GetLandscapeProxy()->LandscapeComponents.CreateConstIterator(); ItComponents; ++ItComponents) { const ULandscapeComponent* CurrentComponent = *ItComponents; // count triangles and sections in the landscape NewStatsEntry->Triangles += FMath::Square(CurrentComponent->ComponentSizeQuads) * 2; NewStatsEntry->Sections += FMath::Square(CurrentComponent->NumSubsections); // count resource usage of landscape bool bNotUnique = false; UniqueTextures.Add(CurrentComponent->HeightmapTexture, &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += CurrentComponent->HeightmapTexture->GetResourceSize(EResourceSizeMode::Exclusive); } if (CurrentComponent->XYOffsetmapTexture) { UniqueTextures.Add(CurrentComponent->XYOffsetmapTexture, &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += CurrentComponent->XYOffsetmapTexture->GetResourceSize(EResourceSizeMode::Exclusive); } } for (auto ItWeightmaps = CurrentComponent->WeightmapTextures.CreateConstIterator(); ItWeightmaps; ++ItWeightmaps) { UniqueTextures.Add((*ItWeightmaps), &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += (*ItWeightmaps)->GetResourceSize(EResourceSizeMode::Exclusive); } } } } NewStatsEntry->InstTriangles = NewStatsEntry->Triangles; NewStatsEntry->InstSections = NewStatsEntry->Sections; // Add to map. ResourceToStatsMap.Add( Resource, NewStatsEntry ); return NewStatsEntry; } } return NULL; }
PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyObject * kwargs) { ue_py_check(self); PyObject *py_ss_vertex; int lod_index = 0; PyObject *py_compute_normals = nullptr; PyObject *py_compute_tangents = nullptr; PyObject *py_use_mikk = nullptr; static char *kw_names[] = { (char *)"soft_vertices", (char *)"lod", (char *)"compute_normals", (char *)"compute_tangents", (char *)"use_mikk", nullptr }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iOOO:skeletal_mesh_build_lod", kw_names, &py_ss_vertex, &lod_index, &py_compute_normals, &py_compute_tangents, &py_use_mikk)) { return nullptr; } USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a SkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); mesh->PreEditChange(nullptr); if (lod_index == resource->LODModels.Num()) { #if ENGINE_MINOR_VERSION < 19 resource->LODModels.Add(new FStaticLODModel()); #else resource->LODModels.Add(new FSkeletalMeshLODModel()); #endif mesh->LODInfo.AddZeroed(); } else { // reinitialized already existent LOD #if ENGINE_MINOR_VERSION < 19 new(&resource->LODModels[lod_index]) FStaticLODModel(); #else new(&resource->LODModels[lod_index]) FSkeletalMeshLODModel(); #endif } #if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel& LODModel = resource->LODModels[lod_index]; #endif mesh->LODInfo[lod_index].LODHysteresis = 0.02; FSkeletalMeshOptimizationSettings settings; mesh->LODInfo[lod_index].ReductionSettings = settings; LODModel.NumTexCoords = 1; IMeshUtilities & MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities"); PyObject *py_iter = PyObject_GetIter(py_ss_vertex); if (!py_iter) { return PyErr_Format(PyExc_Exception, "argument is not an iterable of FSoftSkinVertex"); } TArray<FSoftSkinVertex> soft_vertices; TArray<FVector> points; TArray<FMeshWedge> wedges; TArray<FMeshFace> faces; TArray<FVertInfluence> influences; TArray<int32> points_to_map; TArray<FVector> tangentsX; TArray<FVector> tangentsY; TArray<FVector> tangentsZ; TArray<uint16> material_indices; TArray<uint32> smoothing_groups; while (PyObject *py_item = PyIter_Next(py_iter)) { ue_PyFSoftSkinVertex *ss_vertex = py_ue_is_fsoft_skin_vertex(py_item); if (!ss_vertex) { Py_DECREF(py_iter); return PyErr_Format(PyExc_Exception, "argument is not an iterable of FSoftSkinVertex"); } int32 vertex_index = points.Add(ss_vertex->ss_vertex.Position); points_to_map.Add(vertex_index); FMeshWedge wedge; wedge.iVertex = vertex_index; wedge.Color = ss_vertex->ss_vertex.Color; for (int32 i = 0; i < MAX_TEXCOORDS; i++) { wedge.UVs[i] = ss_vertex->ss_vertex.UVs[i]; } int32 wedge_index = wedges.Add(wedge); for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; i++) { FVertInfluence influence; influence.VertIndex = wedge_index; influence.BoneIndex = ss_vertex->ss_vertex.InfluenceBones[i]; influence.Weight = ss_vertex->ss_vertex.InfluenceWeights[i] / 255.f; influences.Add(influence); } tangentsX.Add(ss_vertex->ss_vertex.TangentX); tangentsY.Add(ss_vertex->ss_vertex.TangentY); tangentsZ.Add(ss_vertex->ss_vertex.TangentZ); material_indices.Add(ss_vertex->material_index); smoothing_groups.Add(ss_vertex->smoothing_group); } Py_DECREF(py_iter); if (wedges.Num() % 3 != 0) return PyErr_Format(PyExc_Exception, "invalid number of FSoftSkinVertex, must be a multiple of 3"); for (int32 i = 0; i < wedges.Num(); i += 3) { FMeshFace face; face.iWedge[0] = i; face.iWedge[1] = i + 1; face.iWedge[2] = i + 2; face.MeshMaterialIndex = material_indices[i]; face.SmoothingGroups = smoothing_groups[i]; face.TangentX[0] = tangentsX[i]; face.TangentX[1] = tangentsX[i + 1]; face.TangentX[2] = tangentsX[i + 2]; face.TangentY[0] = tangentsY[i]; face.TangentY[1] = tangentsY[i + 1]; face.TangentY[2] = tangentsY[i + 2]; face.TangentZ[0] = tangentsZ[i]; face.TangentZ[1] = tangentsZ[i + 1]; face.TangentZ[2] = tangentsZ[i + 2]; faces.Add(face); } #if ENGINE_MINOR_VERSION < 19 FStaticLODModel & lod_model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel & lod_model = resource->LODModels[lod_index]; #endif IMeshUtilities::MeshBuildOptions build_settings; build_settings.bUseMikkTSpace = (py_use_mikk && PyObject_IsTrue(py_use_mikk)); build_settings.bComputeNormals = (py_compute_normals && PyObject_IsTrue(py_compute_normals)); build_settings.bComputeTangents = (py_compute_tangents && PyObject_IsTrue(py_compute_tangents)); build_settings.bRemoveDegenerateTriangles = true; bool success = MeshUtilities.BuildSkeletalMesh(lod_model, mesh->RefSkeleton, influences, wedges, faces, points, points_to_map, build_settings); if (!success) { return PyErr_Format(PyExc_Exception, "unable to create new Skeletal LOD"); } #if ENGINE_MINOR_VERSION < 19 for (int32 i = 0; i < lod_model.Sections.Num(); i++) { mesh->LODInfo[lod_index].TriangleSortSettings.AddZeroed(); } #endif mesh->CalculateRequiredBones(LODModel, mesh->RefSkeleton, nullptr); mesh->CalculateInvRefMatrices(); mesh->Skeleton->RecreateBoneTree(mesh); mesh->Skeleton->SetPreviewMesh(mesh); mesh->Skeleton->PostEditChange(); mesh->Skeleton->MarkPackageDirty(); mesh->PostEditChange(); mesh->MarkPackageDirty(); Py_RETURN_NONE; }
PyObject *py_ue_skeletal_mesh_set_soft_vertices(ue_PyUObject *self, PyObject * args) { ue_py_check(self); PyObject *py_ss_vertex; int lod_index = 0; int section_index = 0; if (!PyArg_ParseTuple(args, "O|ii:skeletal_mesh_set_soft_vertices", &py_ss_vertex, &lod_index, §ion_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); PyObject *py_iter = PyObject_GetIter(py_ss_vertex); if (!py_iter) { return PyErr_Format(PyExc_Exception, "argument is not an iterable of FSoftSkinVertex"); } TArray<FSoftSkinVertex> soft_vertices; while (PyObject *py_item = PyIter_Next(py_iter)) { ue_PyFSoftSkinVertex *ss_vertex = py_ue_is_fsoft_skin_vertex(py_item); if (!ss_vertex) { Py_DECREF(py_iter); return PyErr_Format(PyExc_Exception, "argument is not an iterable of FSoftSkinVertex"); } soft_vertices.Add(ss_vertex->ss_vertex); } Py_DECREF(py_iter); // temporarily disable all USkinnedMeshComponent's TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext; mesh->ReleaseResources(); mesh->ReleaseResourcesFence.Wait(); model.Sections[section_index].SoftVertices = soft_vertices; model.Sections[section_index].NumVertices = soft_vertices.Num(); model.Sections[section_index].CalcMaxBoneInfluences(); mesh->RefBasesInvMatrix.Empty(); mesh->CalculateInvRefMatrices(); #if WITH_EDITOR mesh->PostEditChange(); #endif mesh->InitResources(); mesh->MarkPackageDirty(); Py_RETURN_NONE; }
PyObject *py_ue_skeletal_mesh_set_required_bones(ue_PyUObject *self, PyObject * args) { ue_py_check(self); PyObject *py_map; int lod_index = 0; if (!PyArg_ParseTuple(args, "O|i:skeletal_mesh_set_required_bones", &py_map, &lod_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif PyObject *py_iter = PyObject_GetIter(py_map); if (!py_iter) { return PyErr_Format(PyExc_Exception, "argument is not an iterable of numbers"); } TArray<FBoneIndexType> required_bones; while (PyObject *py_item = PyIter_Next(py_iter)) { if (!PyNumber_Check(py_item)) { Py_DECREF(py_iter); return PyErr_Format(PyExc_Exception, "argument is not an iterable of numbers"); } PyObject *py_num = PyNumber_Long(py_item); uint16 index = PyLong_AsUnsignedLong(py_num); Py_DECREF(py_num); required_bones.Add(index); } Py_DECREF(py_iter); // temporarily disable all USkinnedMeshComponent's TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext; mesh->ReleaseResources(); mesh->ReleaseResourcesFence.Wait(); model.RequiredBones = required_bones; model.RequiredBones.Sort(); mesh->RefBasesInvMatrix.Empty(); mesh->CalculateInvRefMatrices(); #if WITH_EDITOR mesh->PostEditChange(); #endif mesh->InitResources(); mesh->MarkPackageDirty(); Py_RETURN_NONE; }
void USkelModel::Serialize(FArchive &Ar) { guard(USkelModel::Serialize); assert(Ar.IsLoading); // no saving ... Super::Serialize(Ar); // USkelModel data int nummeshes; int numjoints; int numframes; int numsequences; int numskins; int rootjoint; FVector PosOffset; // Offset of creature relative to base FRotator RotOffset; // Offset of creatures rotation TArray<RMesh> meshes; TArray<RJoint> joints; TArray<FRSkelAnimSeq> AnimSeqs; // Compressed animation data for sequence TArray<RAnimFrame> frames; Ar << nummeshes << numjoints << numframes << numsequences << numskins << rootjoint; Ar << meshes << joints << AnimSeqs << frames << PosOffset << RotOffset; int modelIdx; // create all meshes first, then fill them (for better view order) for (modelIdx = 0; modelIdx < meshes.Num(); modelIdx++) { // create new USkeletalMesh // use "CreateClass()" instead of "new USkeletalMesh" to allow this object to be // placed in GObjObjects array and be browsable in a viewer USkeletalMesh *sm = static_cast<USkeletalMesh*>(CreateClass("SkeletalMesh")); char nameBuf[256]; appSprintf(ARRAY_ARG(nameBuf), "%s_%d", Name, modelIdx); const char *name = appStrdupPool(nameBuf); Meshes.Add(sm); // setup UOnject sm->Name = name; sm->Package = Package; sm->PackageIndex = INDEX_NONE; // not really exported sm->Outer = NULL; } // create animation Anim = static_cast<UMeshAnimation*>(CreateClass("MeshAnimation")); Anim->Name = Name; Anim->Package = Package; Anim->PackageIndex = INDEX_NONE; // not really exported Anim->Outer = NULL; ConvertRuneAnimations(*Anim, joints, AnimSeqs); Anim->ConvertAnims(); //?? second conversion // get baseframe assert(strcmp(Anim->AnimSeqs[0].Name, "baseframe") == 0); const TArray<AnalogTrack> &BaseAnim = Anim->Moves[0].AnimTracks; // compute bone coordinates TArray<CCoords> BoneCoords; BuildSkeleton(BoneCoords, joints, BaseAnim); // setup meshes for (modelIdx = 0; modelIdx < meshes.Num(); modelIdx++) { int i, j; const RMesh &src = meshes[modelIdx]; USkeletalMesh *sm = Meshes[modelIdx]; sm->Animation = Anim; // setup ULodMesh sm->RotOrigin = RotOffset; sm->MeshScale.Set(1, 1, 1); sm->MeshOrigin = PosOffset; // copy skeleton sm->RefSkeleton.Empty(joints.Num()); for (i = 0; i < joints.Num(); i++) { const RJoint &J = joints[i]; FMeshBone *B = new(sm->RefSkeleton) FMeshBone; B->Name = J.name; B->Flags = 0; B->ParentIndex = (J.parent > 0) ? J.parent : 0; // -1 -> 0 // copy bone orientations from base animation frame B->BonePos.Orientation = BaseAnim[i].KeyQuat[0]; B->BonePos.Position = BaseAnim[i].KeyPos[0]; } // copy vertices int VertexCount = sm->VertexCount = src.verts.Num(); sm->Points.Empty(VertexCount); for (i = 0; i < VertexCount; i++) { const RVertex &v1 = src.verts[i]; FVector *V = new(sm->Points) FVector; // transform point from local bone space to model space BoneCoords[v1.joint1].UnTransformPoint(CVT(v1.point1), CVT(*V)); } // copy triangles and create wedges // here we create 3 wedges for each triangle. // it is possible to reduce number of wedges by finding duplicates, but we don't // need it here ... int TrisCount = src.tris.Num(); sm->Triangles.Empty(TrisCount); sm->Wedges.Empty(TrisCount * 3); int numMaterials = 0; // should detect real material count for (i = 0; i < TrisCount; i++) { const RTriangle &tri = src.tris[i]; // create triangle VTriangle *T = new(sm->Triangles) VTriangle; T->MatIndex = tri.polygroup; if (numMaterials <= tri.polygroup) numMaterials = tri.polygroup+1; // create wedges for (j = 0; j < 3; j++) { T->WedgeIndex[j] = sm->Wedges.Num(); FMeshWedge *W = new(sm->Wedges) FMeshWedge; W->iVertex = tri.vIndex[j]; W->TexUV = tri.tex[j]; } // reverse order of triangle vertices Exchange(T->WedgeIndex[0], T->WedgeIndex[1]); } // build influences for (i = 0; i < VertexCount; i++) { const RVertex &v1 = src.verts[i]; FVertInfluence *Inf = new(sm->VertInfluences) FVertInfluence; Inf->PointIndex = i; Inf->BoneIndex = v1.joint1; Inf->Weight = v1.weight1; if (Inf->Weight != 1.0f) { // influence for 2nd bone Inf = new(sm->VertInfluences) FVertInfluence; Inf->PointIndex = i; Inf->BoneIndex = v1.joint2; Inf->Weight = 1.0f - v1.weight1; } } // create materials for (i = 0; i < numMaterials; i++) { const char *texName = src.PolyGroupSkinNames[i]; FMeshMaterial *M1 = new(sm->Materials) FMeshMaterial; M1->PolyFlags = src.GroupFlags[i]; M1->TextureIndex = sm->Textures.Num(); if (strcmp(texName, "None") == 0) { // texture should be set from script sm->Textures.Add(NULL); continue; } // find texture in object's package int texExportIdx = Package->FindExport(texName); if (texExportIdx == INDEX_NONE) { appPrintf("ERROR: unable to find export \"%s\" for mesh \"%s\" (%d)\n", texName, Name, modelIdx); continue; } // load and remember texture UMaterial *Tex = static_cast<UMaterial*>(Package->CreateExport(texExportIdx)); sm->Textures.Add(Tex); } // setup UPrimitive properties using 1st animation frame // note: this->BoundingBox and this->BoundingSphere are null const RAnimFrame &F = frames[0]; assert(strcmp(AnimSeqs[0].Name, "baseframe") == 0 && AnimSeqs[0].StartFrame == 0); CVec3 mins, maxs; sm->BoundingBox = F.bounds; mins = CVT(F.bounds.Min); maxs = CVT(F.bounds.Max); CVec3 ¢er = CVT(sm->BoundingSphere); for (i = 0; i < 3; i++) center[i] = (mins[i] + maxs[i]) / 2; sm->BoundingSphere.R = VectorDistance(center, mins); // create CSkeletalMesh sm->ConvertMesh(); } unguard; }
PyObject *py_ue_skeletal_mesh_get_lod(ue_PyUObject *self, PyObject * args) { ue_py_check(self); int lod_index = 0; if (!PyArg_ParseTuple(args, "|i:skeletal_mesh_get_lod", &lod_index)) return nullptr; USkeletalMesh *mesh = ue_py_check_type<USkeletalMesh>(self); if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); #if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); #else FSkeletalMeshModel *resource = mesh->GetImportedModel(); #endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); #if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; #else FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; #endif PyObject *py_list = PyList_New(0); TArray<uint32> indices; #if ENGINE_MINOR_VERSION > 18 indices = model.IndexBuffer; #else model.MultiSizeIndexContainer.GetIndexBuffer(indices); #endif for (int32 index = 0; index < indices.Num(); index++) { int32 section_index; int32 vertex_index; #if ENGINE_MINOR_VERSION > 18 model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index); #else bool has_extra_influences; model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index, has_extra_influences); #endif FSoftSkinVertex ssv = model.Sections[section_index].SoftVertices[vertex_index]; // fix bone id for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; i++) { if (ssv.InfluenceBones[i] < model.Sections[section_index].BoneMap.Num()) { ssv.InfluenceBones[i] = model.Sections[section_index].BoneMap[ssv.InfluenceBones[i]]; } else { UE_LOG(LogPython, Warning, TEXT("unable to retrieve bone mapping for index %d, forcing to 0"), ssv.InfluenceBones[i]); ssv.InfluenceBones[i] = 0; } } ue_PyFSoftSkinVertex *py_ss_vertex = (ue_PyFSoftSkinVertex *)py_ue_new_fsoft_skin_vertex(ssv); py_ss_vertex->material_index = section_index; PyList_Append(py_list, (PyObject *)py_ss_vertex); } return py_list; }