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