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_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 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_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_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; }
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; }