// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License. #include "VrmConvert.h" #include #include #include #include #include #include #include "UObject/Package.h" //static void assimpDummy() { //volatile float f = Assimp::Math::PI(); //} static bool bImportMode = false; bool VRMConverter::IsImportMode() { return bImportMode; } void VRMConverter::SetImportMode(bool b) { bImportMode = b; } UPackage *VRMConverter::CreatePackageFromImportMode(UPackage *p, const FString &name) { FString n = p->GetName(); n += TEXT("/") + name; #if UE_VERSION_OLDER_THAN(4,26,0) return CreatePackage(nullptr, *n); #else return CreatePackage(*n); #endif } FString VRMConverter::NormalizeFileName(const char *str) { FString ret = UTF8_TO_TCHAR(str); return NormalizeFileName(ret); } FString VRMConverter::NormalizeFileName(const FString &str) { FString ret = str; FText error; if (!FName::IsValidXName(*ret, INVALID_OBJECTNAME_CHARACTERS INVALID_LONGPACKAGE_CHARACTERS, &error)) { FString s = INVALID_OBJECTNAME_CHARACTERS; s += INVALID_LONGPACKAGE_CHARACTERS; auto a = s.GetCharArray(); for (int i = 0; i < a.Num(); ++i) { FString tmp; tmp.AppendChar(a[i]); ret = ret.Replace(tmp.GetCharArray().GetData(), TEXT("_")); //int ind; //if (ret.FindChar(s[i], ind)) { //} } } return ret; } static bool hasInvalidBoneName(const aiString &s) { for (uint32_t i = 0; i < s.length; ++i) { if (s.data[i] >= 0 && s.data[i] <= 0x7e) { continue; } return true; } return false; } static int totalCount = 0; static bool AddReplaceList(const aiNode *node, TMap &map) { if (node == nullptr) { return true; } if (hasInvalidBoneName(node->mName)) { FString s = TEXT("replace_") + FString::FromInt(totalCount); if (map.Find(UTF8_TO_TCHAR(node->mName.C_Str())) == nullptr) { map.Add(UTF8_TO_TCHAR(node->mName.C_Str()), s); totalCount++; } } for (uint32_t i = 0; i < node->mNumChildren; ++i) { AddReplaceList(node->mChildren[i], map); } return true; } static bool ReplaceNodeName(aiNode *node, const TMap &map) { if (node == nullptr) { return true; } auto a = map.Find(UTF8_TO_TCHAR(node->mName.C_Str())); if (a) { node->mName.Set(TCHAR_TO_ANSI(**a)); } for (uint32_t i = 0; i < node->mNumChildren; ++i) { ReplaceNodeName(node->mChildren[i], map); } return true; } static bool LocalMakeName(aiNode* node) { for (uint32_t i = 0; i < node->mNumChildren; ++i) { LocalMakeName(node->mChildren[i]); } auto s = VRMUtil::MakeName(UTF8_TO_TCHAR(node->mName.C_Str()), true); node->mName = TCHAR_TO_UTF8(*s); return true; } static bool LocalNormalizeBone(aiNode *node) { for (uint32_t i = 0; i < node->mNumChildren; ++i) { LocalNormalizeBone(node->mChildren[i]); } auto s = VRMUtil::GetSafeNewName(UTF8_TO_TCHAR(node->mName.C_Str())); node->mName = TCHAR_TO_UTF8(*s); return true; } bool VRMConverter::NormalizeBoneName(const aiScene *mScenePtr) { if (mScenePtr == nullptr) { return false; } if (VRMConverter::Options::Get().IsForceOriginalBoneName()) { }else{ LocalMakeName(mScenePtr->mRootNode); // aiBone name for (uint32 m = 0; m < mScenePtr->mNumMeshes; ++m) { auto* aiM = mScenePtr->mMeshes[m]; for (uint32 b = 0; b < aiM->mNumBones; ++b) { auto* aiB = aiM->mBones[b]; auto s = VRMUtil::MakeName(UTF8_TO_TCHAR(aiB->mName.C_Str()), true); aiB->mName = TCHAR_TO_UTF8(*s); } } // spring, collision name VRM::VRMMetadata* meta = reinterpret_cast(mScenePtr->mVRMMeta); if (meta) { for (int s = 0; s < meta->springNum; ++s) { for (int b = 0; b < meta->springs[s].boneNum; ++b) { auto str = VRMUtil::MakeName(UTF8_TO_TCHAR(meta->springs[s].bones_name[b].C_Str()), true); meta->springs[s].bones_name[b] = TCHAR_TO_UTF8(*str); } } for (int c = 0; c < meta->colliderGroupNum; ++c) { auto str = VRMUtil::MakeName(UTF8_TO_TCHAR(meta->colliderGroups[c].node_name.C_Str()), true); meta->colliderGroups[c].node_name = TCHAR_TO_UTF8(*str); } } } return true; /* //auto p = const_cast(mScenePtr); //if (p == nullptr) return false; TMap replaceTable; totalCount = 0; //mScenePtr->mMeshes[0]->bon AddReplaceList(mScenePtr->mRootNode, replaceTable); for (uint32_t m = 0; m < mScenePtr->mNumMeshes; ++m) { if (hasInvalidBoneName(mScenePtr->mMeshes[m]->mName)) { FString s = TEXT("replace_") + FString::FromInt(totalCount); if (replaceTable.Find(UTF8_TO_TCHAR(mScenePtr->mMeshes[m]->mName.C_Str())) == nullptr) { replaceTable.Add(UTF8_TO_TCHAR(mScenePtr->mMeshes[m]->mName.C_Str()), s); totalCount++; } } } // replace! ReplaceNodeName(mScenePtr->mRootNode, replaceTable); for (uint32_t m = 0; m < mScenePtr->mNumMeshes; ++m) { if (auto a = replaceTable.Find(UTF8_TO_TCHAR(mScenePtr->mMeshes[m]->mName.C_Str()))){ mScenePtr->mMeshes[m]->mName.Set(TCHAR_TO_ANSI(**a)); } auto &aiM = mScenePtr->mMeshes[m]; for (uint32_t b = 0; b < aiM->mNumBones; ++b) { if (auto a = replaceTable.Find(UTF8_TO_TCHAR(aiM->mBones[b]->mName.C_Str()))) { aiM->mBones[b]->mName.Set(TCHAR_TO_ANSI(**a)); } } } { VRM::VRMMetadata *meta = reinterpret_cast(mScenePtr->mVRMMeta); for (int s = 0; s < meta->springNum; ++s) { auto &a = meta->springs[s]; if (a.bones_name == nullptr) { continue; } for (int b = 0; b < a.boneNum; ++b) { if (auto str = replaceTable.Find(UTF8_TO_TCHAR(a.bones_name[b].C_Str()))) { a.bones_name[b].Set(TCHAR_TO_ANSI(**str)); } } } for (int c = 0; c < meta->colliderGroupNum; ++c) { auto &a = meta->colliderGroups[c]; if (auto str = replaceTable.Find(UTF8_TO_TCHAR(a.node_name.C_Str()))) { a.node_name.Set(TCHAR_TO_ANSI(**str)); } } for (auto &a : meta->humanoidBone) { if (auto str = replaceTable.Find(UTF8_TO_TCHAR(a.nodeName.C_Str()))) { a.nodeName.Set(TCHAR_TO_ANSI(**str)); } } } return true; */ } //// VRMConverter::Options& VRMConverter::Options::Get(){ static VRMConverter::Options o; return o; } USkeleton *VRMConverter::Options::GetSkeleton() { if (ImportOption == nullptr) return nullptr; return ImportOption->Skeleton; } bool VRMConverter::Options::IsSkipNoMeshBone() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bSkipNoMeshBone; #else return false; #endif } bool VRMConverter::Options::IsSkipMorphTarget() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bSkipMorphTarget || IsDebugNoMesh(); #else return false; #endif } bool VRMConverter::Options::IsEnableMorphTargetNormal() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bEnableMorphTargetNormal; #else return false; #endif } bool VRMConverter::Options::IsForceOriginalMorphTargetName() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bForceOriginalMorphTargetName; #else return false; #endif } bool VRMConverter::Options::IsRemoveBlendShapeGroupPrefix() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bRemoveBlendShapeGroupPrefix; #else return false; #endif } bool VRMConverter::Options::IsForceOriginalBoneName() const { if (ImportOption == nullptr) return false; return ImportOption->bForceOriginalBoneName; } bool VRMConverter::Options::IsGenerateHumanoidRenamedMesh() const { if (ImportOption == nullptr) return false; return ImportOption->bGenerateHumanoidRenamedMesh; } bool VRMConverter::Options::IsGenerateIKBone() const { if (ImportOption == nullptr) return false; return ImportOption->bGenerateIKBone; } bool VRMConverter::Options::IsGenerateRigIK() const { #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bGenerateRigIK; #else return false; #endif } bool VRMConverter::Options::IsMobileBone() const { bool ret = false; #if (PLATFORM_WINDOWS) #else ret = true; #endif #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bMobileBone; #else return ret; #endif } int VRMConverter::Options::GetBoneWeightInfluenceNum() const { int ret = 8; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return FMath::Clamp(ImportOption->BoneWeightInfluenceNum, 1, 8); #else return ret; #endif } bool VRMConverter::Options::IsDebugIgnoreVRMValidation() const { bool ret = false; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bIgnoreVRMValidation; #else return ret; #endif } bool VRMConverter::Options::IsDebugOneBone() const { bool ret = false; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bDebugOneBone; #else return ret; #endif } bool VRMConverter::Options::IsDebugNoMesh() const { bool ret = false; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bDebugNoMesh; #else return ret; #endif } bool VRMConverter::Options::IsDebugNoMaterial() const { bool ret = false; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bDebugNoMaterial; #else return ret; #endif } bool VRMConverter::Options::IsSimpleRootBone() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bSimpleRoot; #else return ret; #endif } bool VRMConverter::Options::IsActiveBone() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bActiveBone; #else return ret; #endif } bool VRMConverter::Options::IsSkipPhysics() const { bool ret = true; if (IsDebugOneBone()) { return true; } if (ImportOption == nullptr) return ret; if (ImportOption->bUEFN) return true; return ImportOption->bSkipPhysics; } bool VRMConverter::Options::IsSkipRetargeter() const { bool ret = false; if (ImportOption == nullptr) return ret; return ImportOption->bSkipRetargeter; } bool VRMConverter::Options::IsForceOpaque() const { if (ImportOption == nullptr) return false; return ImportOption->bForceOpaque; } bool VRMConverter::Options::IsForceTwoSided() const { if (ImportOption == nullptr) return false; return ImportOption->bForceTwoSided; } bool VRMConverter::Options::IsSingleUAssetFile() const { if (ImportOption == nullptr) return false; if (ImportOption->bUEFN) return false; return ImportOption->bSingleUAssetFile; } bool VRMConverter::Options::IsDefaultGridTextureMode() const { bool ret = false; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bDefaultGridTextureMode; #else return ret; #endif } bool VRMConverter::Options::IsBC7Mode() const { if (ImportOption == nullptr) return true; return ImportOption->bBC7Mode; } bool VRMConverter::Options::IsMipmapGenerateMode() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bMipmapGenerateMode; #else return ret; #endif } bool VRMConverter::Options::IsGenerateOutlineMaterial() const { bool ret = true; if (ImportOption == nullptr) return true; return ImportOption->bGenerateOutlineMaterial; } bool VRMConverter::Options::IsMergeMaterial() const { bool ret = true; if (ImportOption == nullptr) return true; return ImportOption->bMergeMaterial; } bool VRMConverter::Options::IsMergePrimitive() const { if (ImportOption == nullptr) return true; return ImportOption->bMergePrimitive; } bool VRMConverter::Options::IsOptimizeVertex() const { return false; //if (ImportOption == nullptr) return true; //return ImportOption->bOptimizeVertex; } bool VRMConverter::Options::IsRemoveDegenerateTriangles() const { if (ImportOption == nullptr) return true; return ImportOption->bRemoveDegenerateTriangles; } bool VRMConverter::Options::IsUE5Material() const { if (ImportOption == nullptr) return false; return ImportOption->bUseUE5Material; } static bool bbVRM0 = false; static bool bbVRM10 = false; void VRMConverter::Options::SetVRM0Model(bool bVRM) { bbVRM0 = bVRM; bbVRM10 = !bVRM; } void VRMConverter::Options::SetVRM10Model(bool bVRM) { bbVRM10 = bVRM; bbVRM0 = !bVRM; } bool VRMConverter::Options::IsVRM0Model() const { return bbVRM0; } bool VRMConverter::Options::IsVRM10Model() const { return bbVRM10; } bool VRMConverter::Options::IsRemoveRootBoneRotation() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bRemoveRootBoneRotation; #else return ret; #endif } bool VRMConverter::Options::IsRemoveRootBonePosition() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bRemoveRootBonePosition; #else return ret; #endif } bool VRMConverter::Options::IsVRM10RemoveLocalRotation() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bVrm10RemoveLocalRotation; #else return ret; #endif } bool VRMConverter::Options::IsVRM10BindToRestPose() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bVrm10UseBindToRestPose; #else return ret; #endif } bool VRMConverter::Options::IsVRM10Bindpose() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bVrm10Bindpose; #else return ret; #endif } bool VRMConverter::Options::IsVRMModel() const { return IsVRM0Model() || IsVRM10Model(); } static bool bbVRMA = false; void VRMConverter::Options::SetVRMAModel(bool bVRMA) { bbVRMA = bVRMA; } bool VRMConverter::Options::IsVRMAModel() const { return bbVRMA; } static bool bbBVH = false; void VRMConverter::Options::SetBVHModel(bool bBVH) { bbBVH = bBVH; } bool VRMConverter::Options::IsBVHModel() const { return bbBVH; } static bool bbPMX = false; void VRMConverter::Options::SetPMXModel(bool bVRM) { bbPMX = bVRM; } bool VRMConverter::Options::IsPMXModel() const { return bbPMX; } static bool bbNoMesh = false; void VRMConverter::Options::SetNoMesh(bool bNoMesh) { bbNoMesh = bNoMesh; } bool VRMConverter::Options::IsNoMesh() const { return bbNoMesh; } void VRMConverter::Options::ClearModelType() { bbVRM0 = false; bbVRM10 = false; bbVRMA = false; bbBVH = false; bbPMX = false; bbNoMesh = false; } bool VRMConverter::Options::IsForceOverride() const { bool ret = true; #if WITH_EDITOR if (ImportOption == nullptr) return ret; return ImportOption->bForceOverride; #else return ret; #endif } float VRMConverter::Options::GetModelScale() const { if (ImportOption == nullptr) return 1.f; return ImportOption->ModelScale; } float VRMConverter::Options::GetAnimationTranslateScale() const { if (ImportOption == nullptr) return 1.f; return ImportOption->AnimationTranslateScale; } float VRMConverter::Options::GetAnimationPlayRateScale() const { if (ImportOption == nullptr) return 1.f; return ImportOption->PlayRateScale; } bool VRMConverter::Options::IsAPoseRetarget() const { if (IsDebugOneBone()) { return false; } #if WITH_EDITOR if (ImportOption == nullptr) return false; return ImportOption->bAPoseRetarget; #else return false; #endif } static EVRMImportMaterialType mType = EVRMImportMaterialType::VRMIMT_Auto; void VRMConverter::Options::SetMaterialType(EVRMImportMaterialType t) { mType = t; } EVRMImportMaterialType VRMConverter::Options::GetMaterialType() const { if (ImportOption == nullptr) return mType; return ImportOption->MaterialType; } //// VrmConvert::VrmConvert() { } VrmConvert::~VrmConvert() { } FString VRM4U_GetPackagePath(UPackage* Outer) { if (Outer == nullptr) return ""; if (Outer != GetTransientPackage()) { FString s = Outer->GetPathName(); FString s1, s2; s.Split(TEXT("/"), &s1, &s2, ESearchCase::IgnoreCase, ESearchDir::FromEnd); return s1 + TEXT("/"); } return ""; } UPackage* VRM4U_CreatePackage(UPackage* Outer, FName Name) { if (Outer == nullptr) return nullptr; UPackage* pkg = Outer; if (Outer != GetTransientPackage()) { FString s = Outer->GetPathName(); FString s1, s2; s.Split(TEXT("/"), &s1, &s2, ESearchCase::IgnoreCase, ESearchDir::FromEnd); #if UE_VERSION_OLDER_THAN(4,26,0) pkg = CreatePackage(nullptr, *(s1 + TEXT("/") + Name.ToString())); #else pkg = CreatePackage(*(s1 + TEXT("/") + Name.ToString())); #endif } return pkg; } UObject* VRM4U_StaticDuplicateObject(UObject const* SourceObject, UObject* DestOuter, const FName DestName, EObjectFlags FlagMask, UClass* DestClass, EDuplicateMode::Type DuplicateMode, EInternalObjectFlags InternalFlagsMask) { if (VRMConverter::Options::Get().IsSingleUAssetFile() == false) { DestOuter = VRM4U_CreatePackage(Cast(DestOuter), DestName); } return StaticDuplicateObject(SourceObject, DestOuter, DestName, FlagMask, DestClass, DuplicateMode, InternalFlagsMask); } /// template static void copyVector(VRM::vec4 &v, const T1& t) { v[3] = 1.f; if (t.Size() > 0) v[0] = t[0].GetFloat(); if (t.Size() > 1) v[1] = t[1].GetFloat(); if (t.Size() > 2) v[2] = t[2].GetFloat(); if (t.Size() > 3) v[3] = t[3].GetFloat(); } int VRMConverter::GetThumbnailTextureIndex() const { if (VRMConverter::Options::Get().IsVRM0Model()) { return -1; } auto& meta = jsonData.doc["extensions"]["VRMC_vrm"]["meta"]; for (auto m = meta.MemberBegin(); m != meta.MemberEnd(); ++m) { FString key = UTF8_TO_TCHAR((*m).name.GetString()); if (key == "thumbnailImage") { return (*m).value.GetInt(); } } return -1; } bool VRMConverter::GetMatParam(VRM::VRMMaterial &m, int matNo) const { if (VRMConverter::Options::Get().IsVRM0Model()) { const VRM::VRMMetadata* meta = static_cast(aiData->mVRMMeta); if (meta == nullptr) return false; if (matNo >= meta->materialNum) { return false; } m.CopyFrom(meta->material[matNo]); return true; } memset(&m.floatProperties, 0, sizeof(m.floatProperties)); memset(&m.textureProperties, 0, sizeof(m.textureProperties)); memset(&m.vectorProperties, 0, sizeof(m.vectorProperties)); m.vectorProperties._Color[0] = 1.f; m.vectorProperties._Color[1] = 1.f; m.vectorProperties._Color[2] = 1.f; m.vectorProperties._Color[3] = 1.f; m.vectorProperties._ShadeColor[0] = 1.f; m.vectorProperties._ShadeColor[1] = 1.f; m.vectorProperties._ShadeColor[2] = 1.f; m.vectorProperties._ShadeColor[3] = 1.f; m.vectorProperties._OutlineColor[0] = 0.f; m.vectorProperties._OutlineColor[1] = 0.f; m.vectorProperties._OutlineColor[2] = 0.f; m.vectorProperties._OutlineColor[3] = 1.f; m.floatProperties._BumpScale = 1.f; m.floatProperties._ReceiveShadowRate = 1.f; m.floatProperties._OutlineLightingMix = 1.f; m.floatProperties._OutlineWidth = 0.1f; m.floatProperties._OutlineWidthMode = 1.f; m.textureProperties._MainTex = -1; m.textureProperties._ShadeTexture = -1; m.textureProperties._BumpMap = -1; m.textureProperties._ReceiveShadowTexture = -1; m.textureProperties._ShadingGradeTexture = -1; m.textureProperties._RimTexture = -1; m.textureProperties._SphereAdd = -1; m.textureProperties._EmissionMap = -1; m.textureProperties._OutlineWidthTexture = -1; m.textureProperties._UvAnimMaskTexture = -1; if (jsonData.doc.HasMember("materials") == false) { return true; } if (jsonData.doc["materials"].IsArray() == false) { return true; } auto& mat = jsonData.doc["materials"].GetArray()[matNo]; m.name = mat["name"].GetString(); // texture { if (mat["extensions"]["VRMC_materials_mtoon"]["matcapTexture"]["index"].IsInt()) { m.textureProperties._SphereAdd = mat["extensions"]["VRMC_materials_mtoon"]["matcapTexture"]["index"].GetInt(); } if (mat["extensions"]["VRMC_materials_mtoon"]["rimMultiplyTexture"]["index"].IsInt()) { m.textureProperties._RimTexture = mat["extensions"]["VRMC_materials_mtoon"]["rimMultiplyTexture"]["index"].GetInt(); } } // float { //m.floatProperties._Cutoff; m.floatProperties._BumpScale = 1.f; m.floatProperties._ReceiveShadowRate = 1.f; m.floatProperties._ShadeShift = mat["extensions"]["VRMC_materials_mtoon"]["shadingShiftFactor"].GetFloat(); m.floatProperties._ShadeToony = mat["extensions"]["VRMC_materials_mtoon"]["shadingToonyFactor"].GetFloat(); //m.floatProperties._LightColorAttenuation; //m.floatProperties._IndirectLightIntensity; m.floatProperties._RimLightingMix = mat["extensions"]["VRMC_materials_mtoon"]["rimLightingMixFactor"].GetFloat(); m.floatProperties._RimFresnelPower = mat["extensions"]["VRMC_materials_mtoon"]["parametricRimFresnelPowerFactor"].GetFloat(); m.floatProperties._RimLift = mat["extensions"]["VRMC_materials_mtoon"]["parametricRimLiftFactor"].GetFloat(); m.floatProperties._OutlineWidth = mat["extensions"]["VRMC_materials_mtoon"]["outlineWidthFactor"].GetFloat() * 100.f; //m.floatProperties._OutlineScaledMaxDistance; m.floatProperties._OutlineLightingMix = mat["extensions"]["VRMC_materials_mtoon"]["outlineLightingMixFactor"].GetFloat(); m.floatProperties._UvAnimScrollX = mat["extensions"]["VRMC_materials_mtoon"]["uvAnimationScrollXSpeedFactor"].GetFloat(); m.floatProperties._UvAnimScrollY = mat["extensions"]["VRMC_materials_mtoon"]["uvAnimationScrollYSpeedFactor"].GetFloat(); m.floatProperties._UvAnimRotation = mat["extensions"]["VRMC_materials_mtoon"]["uvAnimationRotationSpeedFactor"].GetFloat(); //m.floatProperties._MToonVersion; //m.floatProperties._DebugMode; //m.floatProperties._BlendMode; m.floatProperties._OutlineWidthMode = 1.f; { FString s = mat["extensions"]["VRMC_materials_mtoon"]["outlineWidthMode"].GetString(); if (s == "none") { m.floatProperties._OutlineWidthMode = 0.f; m.floatProperties._OutlineWidth = 0; } } //m.floatProperties._OutlineColorMode; //m.floatProperties._CullMode; //m.floatProperties._OutlineCullMode; //m.floatProperties._SrcBlend; //m.floatProperties._DstBlend; m.floatProperties._ZWrite = mat["extensions"]["VRMC_materials_mtoon"]["transparentWithZWrite"].GetBool() ? 1.f : 0.f; } // vector { { auto t = mat["pbrMetallicRoughness"]["baseColorFactor"].GetArray(); copyVector(m.vectorProperties._Color, t); } { auto t = mat["extensions"]["VRMC_materials_mtoon"]["shadeColorFactor"].GetArray(); if (t.Size()) { copyVector(m.vectorProperties._ShadeColor, t); m.vectorProperties._ShadeColor[3] = m.vectorProperties._Color[3]; } } { auto t = mat["extensions"]["VRMC_materials_mtoon"]["parametricRimColorFactor"].GetArray(); if (t.Size()) { copyVector(m.vectorProperties._RimColor, t); } } { auto t = mat["emissiveFactor"].GetArray(); if (t.Size()) { copyVector(m.vectorProperties._EmissionColor, t); } } { auto t = mat["extensions"]["VRMC_materials_mtoon"]["outlineColorFactor"].GetArray(); if (t.Size()) { copyVector(m.vectorProperties._OutlineColor, t); } } { auto t = mat["extensions"]["VRMC_materials_mtoon"]["matcapFactor"].GetArray(); if (t.Size()) { //copyVector(m.vectorProperties._OutlineColor, t); } } } return true; }