star_mod_renderer
Advanced tools
Comparing version 1.0.43 to 1.0.44
@@ -155,2 +155,5 @@ import * as THREE from 'star_mod_custom_three'; | ||
export const FEMALE_HEAD_MATERIAL_KEY = 1403; | ||
export const MALE_HEAD_MATERIAL_KEY = 1401; | ||
export const FEMALE_BODY_KEY = 404; | ||
@@ -160,23 +163,27 @@ export const MALE_BODY_KEY = 402; | ||
export const MALE_HAIR_KEY = 401; | ||
export const FEMALE_HEAD_MATERIAL_KEY = 1403; | ||
export const MALE_HEAD_MATERIAL_KEY = 1401; | ||
export const MALE_FASHION_ANIMATION_KEY = 501; | ||
export const FEMALE_FASHION_ANIMATION_KEY = 502; | ||
export const isFashion = (key) => { | ||
return key === FEMALE_BODY_KEY || key === FEMALE_HAIR_KEY || key === MALE_HAIR_KEY || key === MALE_BODY_KEY; | ||
return Number(key) === FEMALE_BODY_KEY || Number(key) === FEMALE_HAIR_KEY || Number(key) === MALE_HAIR_KEY || Number(key) === MALE_BODY_KEY; | ||
}; | ||
export const isFashionHair = (key) => { | ||
return key === FEMALE_HAIR_KEY || key === MALE_HAIR_KEY; | ||
return Number(key) === FEMALE_HAIR_KEY || Number(key) === MALE_HAIR_KEY; | ||
} | ||
export const isFashionBody = (key) => { | ||
return key === FEMALE_BODY_KEY || key === MALE_BODY_KEY; | ||
return Number(key) === FEMALE_BODY_KEY || Number(key) === MALE_BODY_KEY; | ||
} | ||
export const isFashionAnimation = (key) => { | ||
return Number(key) === MALE_FASHION_ANIMATION_KEY || Number(key) === FEMALE_FASHION_ANIMATION_KEY; | ||
} | ||
export const isFashionFemale = (key) => { | ||
return key === FEMALE_BODY_KEY || key === FEMALE_HAIR_KEY; | ||
return Number(key) === FEMALE_BODY_KEY || Number(key) === FEMALE_HAIR_KEY || Number(key) === FEMALE_FASHION_ANIMATION_KEY; | ||
} | ||
export const isFashionMale = (key) => { | ||
return key === MALE_HAIR_KEY || key === MALE_BODY_KEY; | ||
return Number(key) === MALE_HAIR_KEY || Number(key) === MALE_BODY_KEY || Number(key) === MALE_FASHION_ANIMATION_KEY; | ||
} | ||
@@ -218,3 +225,13 @@ | ||
export const fashionFemaleAnimationModel = { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1677122095-mesh.fbx', | ||
abroadPath: 'https://xqhw-1255691311.cos.ap-singapore.myqcloud.com/mod/modOfficial/1677122257-mesh.fbx' | ||
}; | ||
export const fashionMaleAnimationModel = { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1677122095-mesh.fbx', | ||
abroadPath: 'https://xqhw-1255691311.cos.ap-singapore.myqcloud.com/mod/modOfficial/1677122257-mesh.fbx' | ||
}; | ||
export const defaultModel = { | ||
@@ -225,3 +242,3 @@ 401: { | ||
fbx: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1684145245_M_CSZ_head_01.FBX' | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1695037543_M_CSZ_head_01.FBX' | ||
}, | ||
@@ -233,3 +250,3 @@ }, | ||
fbx: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1684145150_F_CSZ_01_Head.FBX' | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1695301452_nvluomo.FBX' | ||
}, | ||
@@ -240,16 +257,20 @@ }, | ||
export const defaultModelAnimation = { | ||
401: { | ||
name: '男', | ||
dancing: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1684224202_Male_Celebrate.FBX', | ||
}, | ||
male: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1695037867_Male_Ivankof_Stand.fbx' | ||
}, | ||
403: { | ||
name: '女', | ||
dancing: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1684224183_Female_BellyDancing.FBX', | ||
}, | ||
female: { | ||
domesticPath: 'https://mod-img.westar.qq.com/mod/modOfficial/1694511493_Female_Jambudiv_Stand.FBX', | ||
}, | ||
} | ||
// 默认骨骼数据 | ||
export const defaultSkeletonData = { | ||
female: { | ||
domesticPath: '' | ||
}, | ||
male: { | ||
domesticPath: '' | ||
} | ||
} | ||
// 玻璃、男性头部皮肤、男性身体皮肤、女性头部皮肤、女性身体皮肤 | ||
@@ -256,0 +277,0 @@ export const specificMaterial = { |
@@ -8,2 +8,9 @@ import { FBXLoader } from "./FBXLoader"; | ||
import { getSceneCoordinateByModel } from "../parser"; | ||
// import { loadFile } from '../filesHelper'; | ||
import { | ||
isFashionFemale, | ||
isFashionMale, | ||
// isFashionHair, | ||
// isFashionBody | ||
} from '../../default' | ||
@@ -68,3 +75,2 @@ export async function detectFile(key, file, detectRule) { | ||
const object = loader.parse(await file.arrayBuffer()); | ||
console.log('object is :', object); | ||
const model = loader.parse(await file.arrayBuffer()).sceneGraph; | ||
@@ -597,3 +603,2 @@ const coordinate = getSceneCoordinateByModel(object); | ||
async function detectFBXFashionFile(file, detectRule) { | ||
console.log('detectRule is :', detectRule); | ||
// 格式要求 | ||
@@ -610,16 +615,44 @@ const res = file.name.split("."); | ||
const loader = new FBXLoader(); | ||
// TODO: 补充错误处理 | ||
const object = loader.parse(await file.arrayBuffer()); | ||
// const model = loader.parse(await file.arrayBuffer()).sceneGraph; | ||
const model = loader.parse(await file.arrayBuffer()).sceneGraph; | ||
const coordinate = getSceneCoordinateByModel(object); | ||
console.log('object is :', object); | ||
// 坐标系验证: TODO: +Y 轴朝上的模型 | ||
if (Number(coordinate.coord.value) !== 0) { | ||
// 坐标系验证: +Y 轴朝上的模型 | ||
if (Number(coordinate.coord.value) !== 0 || Number(coordinate.up.value) !== 1) { | ||
return { | ||
value: false, | ||
message: "模型坐标系不正确, 请重新导出, 注意使用 +Y +Z 作为 up 轴, 并确保 front 轴为 +Z、-Z、-Y、+Y", | ||
message: "模型坐标系不正确, 请重新导出, 注意使用 +Y 作为 up 轴, 并确保 front 轴为 +Z 或 -Z", | ||
}; | ||
} | ||
let uniqueSkeleton = null; | ||
model.traverse(child => { | ||
if (child.name === "pelvis" && (child.children[0].name === 'spine_01' || child.children[0].name === 'thigh_l' || child.children[0].name === 'thigh_r')) { | ||
uniqueSkeleton = child; | ||
} | ||
}); | ||
if (!uniqueSkeleton) { | ||
return { | ||
value: false, | ||
message: "骨骼信息缺失", | ||
} | ||
} | ||
if (uniqueSkeleton.position.y > 0 && isFashionFemale(detectRule.goodSecondaryClassification)) { | ||
return { | ||
value: false, | ||
message: "女性模型骨骼不正确, 或时装性别选择错误", | ||
}; | ||
} | ||
if (uniqueSkeleton.position.y < 0 && isFashionMale(detectRule.goodSecondaryClassification)) { | ||
return { | ||
value: false, | ||
message: "男性模型骨骼不正确, 或时装性别选择错误", | ||
}; | ||
} | ||
return { | ||
@@ -640,4 +673,3 @@ value: true, | ||
// 动画 fbx 文件校验 | ||
async function detectFBXAnimationFile(file, detectRule) { | ||
console.log('detectRule is :', detectRule); | ||
async function detectFBXAnimationFile(file) { | ||
// 格式要求 | ||
@@ -654,17 +686,36 @@ const res = file.name.split("."); | ||
const loader = new FBXLoader(); | ||
// TODO: 补充错误处理 | ||
const object = loader.parse(await file.arrayBuffer()); | ||
// const model = loader.parse(await file.arrayBuffer()).sceneGraph; | ||
const model = loader.parse(await file.arrayBuffer()).sceneGraph; | ||
const coordinate = getSceneCoordinateByModel(object); | ||
console.log('object is :', object); | ||
// 坐标系验证: TODO: +Y 轴朝上的模型 | ||
if (Number(coordinate.coord.value) !== 0) { | ||
// 坐标系验证: +Y 轴朝上的模型 | ||
if (Number(coordinate.coord.value) !== 0 || Number(coordinate.up.value) !== 1) { | ||
return { | ||
value: false, | ||
message: "模型坐标系不正确, 请重新导出, 注意使用 +Y +Z 作为 up 轴, 并确保 front 轴为 +Z、-Z、-Y、+Y", | ||
message: "模型坐标系不正确, 请重新导出, 注意使用 +Y 作为 up 轴, 并确保 front 轴为 +Z 或 -Z", | ||
}; | ||
} | ||
// 骨骼校验,数据当与默认模型骨骼一致 | ||
let uniqueSkeleton = null; | ||
model.traverse(child => { | ||
if (child.name === "pelvis" && (child.children[0].name === 'spine_01' || child.children[0].name === 'thigh_l' || child.children[0].name === 'thigh_r')) { | ||
uniqueSkeleton = child; | ||
} | ||
}); | ||
if (!uniqueSkeleton) { | ||
return { | ||
value: false, | ||
message: "骨骼信息缺失", | ||
} | ||
} | ||
if (!(model.animations && model.animations.length)) { | ||
return { | ||
value: false, | ||
message: "动画信息缺失", | ||
} | ||
} | ||
return { | ||
@@ -671,0 +722,0 @@ value: true, |
@@ -61,3 +61,2 @@ import * as THREE from 'star_mod_custom_three'; | ||
this.transModelByCoord(assembleModel, trans); | ||
console.log('assembleModel', assembleModel); | ||
return assembleModel; | ||
@@ -263,3 +262,8 @@ } | ||
const face = this.model.children[1]; | ||
let face = null; | ||
this.model.children.forEach(item => { | ||
if (item.type === "SkinnedMesh") { | ||
face = item; | ||
} | ||
}); | ||
const material = face.material; | ||
@@ -312,3 +316,8 @@ // 眉毛与睫毛 | ||
const face = this.model.children[1]; | ||
let face = null; | ||
this.model.children.forEach(item => { | ||
if (item.type === "SkinnedMesh") { | ||
face = item; | ||
} | ||
}); | ||
const material = face.material; | ||
@@ -322,6 +331,6 @@ | ||
// 眉毛与睫毛 | ||
material[1] = new THREE.MeshPhysicalMaterial(specificMaterial.femaleHead.materialChannelList[1].materialParams); | ||
material[1].map = files[4]; | ||
material[1].transparent = true; | ||
material[1].needsUpdate = true; | ||
material[3] = new THREE.MeshPhysicalMaterial(specificMaterial.femaleHead.materialChannelList[1].materialParams); | ||
material[3].map = files[4]; | ||
material[3].transparent = true; | ||
material[3].needsUpdate = true; | ||
@@ -328,0 +337,0 @@ // 皮肤 |
@@ -0,0 +0,0 @@ import * as THREE from 'star_mod_custom_three'; |
{ | ||
"name": "star_mod_renderer", | ||
"version": "1.0.43", | ||
"version": "1.0.44", | ||
"description": "star mod project render auxiliary ", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -101,2 +101,4 @@ /** | ||
currSelectModel = null; | ||
mixer = null; | ||
clock = new THREE.Clock(); | ||
options = { | ||
@@ -194,8 +196,2 @@ mode: RENDER_MODE_PREVIEW, | ||
} | ||
if (this.options.isNeedScreenshot) { | ||
this.setAnimationLoop(() => { | ||
this.customUpdate(); | ||
}); | ||
} | ||
// this.initAnimation(); | ||
} | ||
@@ -210,3 +206,3 @@ | ||
this.scene = new THREE.Scene(); | ||
this.scene.background = new THREE.Color(SCENE_BACKGROUND_COLOR); | ||
this.setSceneEnvironment(); | ||
} | ||
@@ -292,7 +288,2 @@ | ||
// TODO: 补充动画 | ||
initAnimation() { | ||
} | ||
// TODO: 页面与元素显隐 | ||
@@ -791,11 +782,4 @@ initSignal() { | ||
this.renderer.autoClear = true; | ||
} | ||
} | ||
// preview 模式特供 update 用于截取当前帧数据 | ||
customUpdate() { | ||
if (this.renderer) { | ||
this.update(); | ||
if (this.mixer !== null) { | ||
// this.mixer.update(this.clock.getDelta()); | ||
this.mixer.update(this.clock.getDelta()); | ||
} | ||
@@ -802,0 +786,0 @@ if (this.generateCurrFrameFlag) { |
678
render.js
@@ -66,2 +66,3 @@ /** | ||
import { RenderBase } from "./render.base"; | ||
import * as THREE from "star_mod_custom_three"; | ||
import { | ||
@@ -71,3 +72,2 @@ getEnvMapList, | ||
// defaultModel, | ||
// defaultModelAnimation, | ||
// FEMALE_HAIR_KEY, | ||
@@ -77,7 +77,13 @@ // MALE_HAIR_KEY, | ||
// MALE_HEAD_MATERIAL_KEY, | ||
RENDER_MODE_PREVIEW | ||
RENDER_MODE_PREVIEW, | ||
isFashion, | ||
isFashionAnimation, | ||
isFashionFemale, | ||
isFashionMale, | ||
// defaultSkeletonData, | ||
defaultModelAnimation | ||
} from "./default"; | ||
import { ModelProcessor } from "./helper/modelHelper/index"; | ||
// import { calculateVerticesAndFace } from "./helper/calculate"; | ||
// import { loadFile } from "./helper/filesHelper"; | ||
import { loadFile } from "./helper/filesHelper"; | ||
import { | ||
@@ -96,3 +102,4 @@ MainModelAuxiliary, // 主模型辅助类 | ||
export class Render extends RenderBase { | ||
mainModels = []; // 所有模型 | ||
mainModels = []; // 所有主模型 | ||
extraModels = []; // 所有辅助模型 | ||
modelsLoader = []; // 请求列表,在 start 时同时执行所有请求内容 | ||
@@ -110,10 +117,11 @@ /** | ||
maleHair: null, // 男性时装头发 | ||
maleHeader: null, // 男性头部 | ||
maleHead: null, // 男性头部 | ||
maleBody: null, // 男性时装身体 | ||
femaleHair: null, // 女性时装头发 | ||
femaleHeader: null, // 女性头部 | ||
femaleHead: null, // 女性头部 | ||
femaleBody: null, // 女性时装身体 | ||
}; | ||
previewData = null; // 预览数据 | ||
animations = []; // 所有动画: 从配置中请求获取: | ||
animations = []; // 所有动画: 从配置中请求获取 | ||
defaultAnimation = null; // 默认动作 | ||
@@ -152,4 +160,4 @@ constructor({dom, options}) { | ||
extraMaterialList, | ||
// goodSecondaryClassification, | ||
// fashionConfig, | ||
goodSecondaryClassification, | ||
fashionConfig, | ||
previewData, | ||
@@ -165,2 +173,3 @@ }) { | ||
const modelRender = new ModelProcessor(modelOriginalData); | ||
this.mainModels.push(modelRender); | ||
// channelMaterialHelper | ||
@@ -173,3 +182,2 @@ const channelMaterialAuxiliary = new ChannelMaterialAuxiliary([]); | ||
mainModelAuxiliary.setMainModelData(model); | ||
this.mainModels.push(modelRender); | ||
@@ -218,2 +226,182 @@ const channelTextureLoadSuccess = (textureList) => { | ||
// 如果是时装单品 | ||
if (fashionConfig && isFashion(goodSecondaryClassification)) { | ||
if (fashionConfig.defaultHead) { | ||
const defaultHeadModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultHead.fbx.domesticPath); | ||
const defaultHeadModelOriginalData = await defaultHeadModelAuxiliary.loadModel(); | ||
const defaultHeadModelRender = new ModelProcessor(defaultHeadModelOriginalData); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHead = defaultHeadModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleHead = defaultHeadModelRender; | ||
} | ||
this.extraModels.push(defaultHeadModelRender); | ||
this.modelsLoader.push( | ||
defaultHeadModelRender.createSpecificMaterial(fashionConfig.defaultHead.key) | ||
); | ||
} | ||
if (fashionConfig.defaultHair) { | ||
const defaultHairModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultHair.mesh); | ||
const defaultHairModelOriginalData = await defaultHairModelAuxiliary.loadModel(); | ||
const defaultHairModelRender = new ModelProcessor(defaultHairModelOriginalData); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHair = defaultHairModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleHair = defaultHairModelRender; | ||
} | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHair = defaultHairModelRender; | ||
const femaleHairModel = this.auxiliaryModels.femaleHair.getModel(); | ||
const femaleHeadModel = this.auxiliaryModels.femaleHead.getModel(); | ||
let headBones = null; | ||
femaleHeadModel.traverse((item) => { | ||
if(item.name === 'root') { | ||
headBones = item; | ||
} | ||
}); | ||
femaleHairModel.add(headBones.clone(true)); | ||
} | ||
this.extraModels.push(defaultHairModelRender); | ||
const loadDefaultHairModelChannelTexture = async () => { | ||
const loaderList = []; | ||
if (fashionConfig.defaultHair.diffuseTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.diffuseTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultHair.normalTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.normalTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultHair.ormeTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.ormeTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (loaderList.length) { | ||
const files = await Promise.all(loaderList); | ||
defaultHairModelRender.updateModelMaterialTexture(0, { | ||
diffuseTexture: files[0], | ||
normalTexture: files[1], | ||
ormeTexture: files[2], | ||
}); | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultHairModelChannelTexture() | ||
); | ||
} | ||
if (fashionConfig.defaultBody) { | ||
const defaultBodyModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultBody.mesh); | ||
const defaultBodyModelOriginalData = await defaultBodyModelAuxiliary.loadModel(); | ||
const defaultBodyModelRender = new ModelProcessor(defaultBodyModelOriginalData); | ||
this.extraModels.push(defaultBodyModelRender); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleBody = defaultBodyModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleBody = defaultBodyModelRender; | ||
} | ||
const loadDefaultBodyModelChannelTexture = async () => { | ||
const loaderList = []; | ||
if (fashionConfig.defaultBody.diffuseTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.diffuseTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultBody.normalTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.normalTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultBody.ormeTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.ormeTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (loaderList.length) { | ||
const files = await Promise.all(loaderList); | ||
defaultBodyModelRender.updateModelMaterialTexture(0, { | ||
diffuseTexture: files[0], | ||
normalTexture: files[1], | ||
ormeTexture: files[2], | ||
}); | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultBodyModelChannelTexture() | ||
); | ||
} | ||
// TODO: 默认动作 | ||
if (fashionConfig.animations) { | ||
const loadDefaultAnimations = async () => { | ||
const loaderList = []; | ||
fashionConfig.animations.forEach(item => { | ||
loaderList.push(loadFile({ | ||
url: item.fbx, | ||
type: 'fbx' | ||
})); | ||
}); | ||
const files = await Promise.all(loaderList); | ||
const animations = files.map(item => { | ||
const animation = item.sceneGraph.animations[0]; | ||
return animation; | ||
}); | ||
const group = this.mainModels.map(item => { | ||
const object = item.getModel(); | ||
return object; | ||
}); | ||
Object.keys(this.auxiliaryModels).forEach(key => { | ||
const item = this.auxiliaryModels[key]; | ||
if (item && key !== 'floor') { | ||
group.push(item.getModel()); | ||
} | ||
}); | ||
const flock = new THREE.AnimationObjectGroup(...group); | ||
this.mixer = new THREE.AnimationMixer(flock); | ||
animations.forEach(item => { | ||
const animation = this.mixer.clipAction(item); | ||
this.animations.push(animation); | ||
}); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.female.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.male.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultAnimations() | ||
); | ||
} | ||
} | ||
// 设置预览数据并请求天空盒资源 | ||
@@ -242,2 +430,5 @@ const previewHelper = new PreviewDataHelper(); | ||
modelRender.setModelMaterialEnvMap(environment.data); | ||
this.extraModels.forEach(modelRender => { | ||
modelRender.setModelMaterialEnvMap(environment.data); | ||
}); | ||
} | ||
@@ -251,13 +442,420 @@ resolve(); | ||
); | ||
} | ||
// 根据 goodSecondaryClassification 与 fashionConfig 请求对应辅助模型的数据 | ||
/* | ||
组合模型参数 | ||
{ | ||
models: [ | ||
{ | ||
model, | ||
modelLocalCache, | ||
modelData, | ||
materialMap, | ||
extraMaterialList, | ||
goodSecondaryClassification | ||
}, | ||
{ | ||
model, | ||
modelLocalCache, | ||
modelData, | ||
materialMap, | ||
extraMaterialList, | ||
goodSecondaryClassification | ||
} | ||
], | ||
previewData, | ||
fashionConfig, | ||
} | ||
*/ | ||
async setCombinationModels({ | ||
standardGoods, | ||
previewData, | ||
}) { | ||
for (let index = 0; index < standardGoods.length; index++) { | ||
const { | ||
mainModel, | ||
mainModelLocalCache, | ||
mainModelData, | ||
mainMaterialMap, | ||
extraMaterialList | ||
} = standardGoods[index]; | ||
const modelAuxiliary = new MainModelAuxiliary( | ||
mainModel, | ||
mainModelLocalCache, | ||
mainModelData | ||
); | ||
const modelOriginalData = await modelAuxiliary.loadModel(); | ||
const modelRender = new ModelProcessor(modelOriginalData); | ||
this.mainModels.push(modelRender); | ||
// channelMaterialHelper | ||
const channelMaterialAuxiliary = new ChannelMaterialAuxiliary([]); | ||
// 所有通道纹理全部加载完成并设置对应材质后返回成功 | ||
const loadModelChannelTexture = (model) => { | ||
return new Promise((resolve) => { | ||
modelAuxiliary.setMainModelData(model); | ||
} | ||
const channelTextureLoadSuccess = (textureList) => { | ||
const loaderList = []; | ||
return new Promise((resolve) => { | ||
// 更新模型材质类型 | ||
for (let index = 0; index < channelMaterialAuxiliary.channelMaterialParamsList.length; index++) { | ||
const item = channelMaterialAuxiliary.channelMaterialParamsList[index]; | ||
loaderList.push(modelRender.updateModelMaterialType(index, item.materialType)); | ||
} | ||
Promise.all(loaderList).then((res) => { | ||
for(let i = 0; i < textureList.length; i++) { | ||
const texture = textureList[i]; | ||
for (let j = 0; j < texture.length; j++) { | ||
const { index, key, res } = texture[j]; | ||
channelMaterialAuxiliary.setChannelItemTextureData(index, key, res); | ||
// 更新材质与材质列表 | ||
modelRender.updateModelMaterialParam(index, channelMaterialAuxiliary.getChannelItem(index)); | ||
modelRender.updateModelMaterialTexture(index, channelMaterialAuxiliary.getChannelItemTextureData(index)); | ||
} | ||
} | ||
resolve(res); | ||
}); | ||
}); | ||
}; | ||
setCombinationModels() { | ||
channelMaterialAuxiliary.initChannelMaterialParamsList( | ||
getModelMaterialChannelLength(model), | ||
channelMaterialParams, | ||
); | ||
channelMaterialAuxiliary.setMaterialParamsList(mainMaterialMap, extraMaterialList).then(res => { | ||
channelTextureLoadSuccess(res).then(() => { | ||
resolve() | ||
}); | ||
}); | ||
}); | ||
}; | ||
this.modelsLoader.push( | ||
loadModelChannelTexture(modelOriginalData) | ||
); | ||
} | ||
const fashionConfig = standardGoods[0].fashionConfig; | ||
const goodSecondaryClassification = standardGoods[0].goodSecondaryClassification; | ||
const defaultHeadModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultHead.fbx.domesticPath); | ||
const defaultHeadModelOriginalData = await defaultHeadModelAuxiliary.loadModel(); | ||
const defaultHeadModelRender = new ModelProcessor(defaultHeadModelOriginalData); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHead = defaultHeadModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleHead = defaultHeadModelRender; | ||
} | ||
this.extraModels.push(defaultHeadModelRender); | ||
this.modelsLoader.push( | ||
defaultHeadModelRender.createSpecificMaterial(fashionConfig.defaultHead.key) | ||
); | ||
const loadDefaultAnimations = async () => { | ||
const loaderList = []; | ||
fashionConfig.animations.forEach(item => { | ||
loaderList.push(loadFile({ | ||
url: item.fbx, | ||
type: 'fbx' | ||
})); | ||
}); | ||
const files = await Promise.all(loaderList); | ||
const animations = files.map(item => { | ||
const animation = item.sceneGraph.animations[0]; | ||
return animation; | ||
}); | ||
const group = this.mainModels.map(item => { | ||
const object = item.getModel(); | ||
return object; | ||
}); | ||
Object.keys(this.auxiliaryModels).forEach(key => { | ||
const item = this.auxiliaryModels[key]; | ||
if (item && key !== 'floor') { | ||
group.push(item.getModel()); | ||
} | ||
}); | ||
const flock = new THREE.AnimationObjectGroup(...group); | ||
this.mixer = new THREE.AnimationMixer(flock); | ||
animations.forEach(item => { | ||
const animation = this.mixer.clipAction(item); | ||
this.animations.push(animation); | ||
}); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.female.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.male.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultAnimations() | ||
); | ||
// 设置预览数据并请求天空盒资源 | ||
const previewHelper = new PreviewDataHelper(); | ||
this.previewData = previewHelper.transPreviewDataToRender(previewData); | ||
const getCurrEnvMap = () => { | ||
const envMap = this.previewData.envMap || 'sky'; | ||
return new Promise((resolve) => { | ||
let processEnv = "domestic"; | ||
try { | ||
if (this.options.platform === "pc") { | ||
processEnv = process.env.VUE_APP_NAME; | ||
} else if (this.options.platform === "mobile") { | ||
processEnv = import.meta.env.VITE_APP_NAME; | ||
} | ||
} catch (e) { | ||
console.error(e); | ||
processEnv = "domestic"; | ||
} | ||
getEnvMapByName(envMap, processEnv).then((environment) => { | ||
if (environment) { | ||
this.setSceneEnvironment(environment.data); | ||
this.mainModels.forEach(modelRender => { | ||
modelRender.setModelMaterialEnvMap(environment.data); | ||
}) | ||
this.extraModels.forEach(modelRender => { | ||
modelRender.setModelMaterialEnvMap(environment.data); | ||
}); | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
this.modelsLoader.push( | ||
getCurrEnvMap() | ||
); | ||
} | ||
setFashionAnimations() { | ||
/** | ||
时装动画参数 | ||
{ | ||
animations: [], | ||
previewData, | ||
fashionConfig, | ||
} | ||
*/ | ||
async setFashionAnimations({ | ||
mainModel, | ||
mainModelLocalCache, | ||
mainModelData, | ||
goodSecondaryClassification, | ||
fashionConfig, | ||
previewData, | ||
}) { | ||
// 获取个性动作文件 | ||
const mainModelAuxiliary = new MainModelAuxiliary( | ||
mainModel, | ||
mainModelLocalCache, | ||
mainModelData | ||
); | ||
const file = await mainModelAuxiliary.loadModel(); | ||
const fashionAnimation = file.sceneGraph.animations[0]; | ||
// 根据性别设置默认模型并加载对应贴图 | ||
if (fashionConfig && isFashionAnimation(goodSecondaryClassification)) { | ||
if (fashionConfig.defaultHead) { | ||
const defaultHeadModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultHead.fbx.domesticPath); | ||
const defaultHeadModelOriginalData = await defaultHeadModelAuxiliary.loadModel(); | ||
const defaultHeadModelRender = new ModelProcessor(defaultHeadModelOriginalData); | ||
this.extraModels.push(defaultHeadModelRender); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHead = defaultHeadModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleHead = defaultHeadModelRender; | ||
} | ||
this.modelsLoader.push( | ||
defaultHeadModelRender.createSpecificMaterial(fashionConfig.defaultHead.key) | ||
); | ||
} | ||
if (fashionConfig.defaultHair) { | ||
const defaultHairModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultHair.mesh); | ||
const defaultHairModelOriginalData = await defaultHairModelAuxiliary.loadModel(); | ||
const defaultHairModelRender = new ModelProcessor(defaultHairModelOriginalData); | ||
this.extraModels.push(defaultHairModelRender); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleHair = defaultHairModelRender; | ||
const femaleHairModel = this.auxiliaryModels.femaleHair.getModel(); | ||
const femaleHeadModel = this.auxiliaryModels.femaleHead.getModel(); | ||
let headBones = null; | ||
femaleHeadModel.traverse((item) => { | ||
if(item.name === 'root') { | ||
headBones = item; | ||
} | ||
}); | ||
femaleHairModel.add(headBones.clone(true)); | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleHair = defaultHairModelRender; | ||
} | ||
const loadDefaultHairModelChannelTexture = async () => { | ||
const loaderList = []; | ||
if (fashionConfig.defaultHair.diffuseTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.diffuseTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultHair.normalTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.normalTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultHair.ormeTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultHair.ormeTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (loaderList.length) { | ||
const files = await Promise.all(loaderList); | ||
defaultHairModelRender.updateModelMaterialTexture(0, { | ||
diffuseTexture: files[0], | ||
normalTexture: files[1], | ||
ormeTexture: files[2], | ||
}); | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultHairModelChannelTexture() | ||
); | ||
} | ||
if (fashionConfig.defaultBody) { | ||
const defaultBodyModelAuxiliary = new MainModelAuxiliary(fashionConfig.defaultBody.mesh); | ||
const defaultBodyModelOriginalData = await defaultBodyModelAuxiliary.loadModel(); | ||
const defaultBodyModelRender = new ModelProcessor(defaultBodyModelOriginalData); | ||
this.extraModels.push(defaultBodyModelRender); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.femaleBody = defaultBodyModelRender; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
this.auxiliaryModels.maleBody = defaultBodyModelRender; | ||
} | ||
const loadDefaultBodyModelChannelTexture = async () => { | ||
const loaderList = []; | ||
if (fashionConfig.defaultBody.diffuseTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.diffuseTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultBody.normalTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.normalTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (fashionConfig.defaultBody.ormeTexture) { | ||
loaderList.push(loadFile({ | ||
url: fashionConfig.defaultBody.ormeTexture, | ||
type: 'tga' | ||
})) | ||
} | ||
if (loaderList.length) { | ||
const files = await Promise.all(loaderList); | ||
defaultBodyModelRender.updateModelMaterialTexture(0, { | ||
diffuseTexture: files[0], | ||
normalTexture: files[1], | ||
ormeTexture: files[2], | ||
}); | ||
} | ||
}; | ||
this.modelsLoader.push( | ||
loadDefaultBodyModelChannelTexture() | ||
); | ||
} | ||
} | ||
// 设置动画播放 | ||
const animationsModels = []; | ||
Object.keys(this.auxiliaryModels).forEach(key => { | ||
const item = this.auxiliaryModels[key]; | ||
if (item && key !== 'floor') { | ||
animationsModels.push(item); | ||
} | ||
}); | ||
const group = animationsModels.map(item => { | ||
const object = item.getModel(); | ||
return object; | ||
}); | ||
const flock = new THREE.AnimationObjectGroup(...group); | ||
this.mixer = new THREE.AnimationMixer(flock); | ||
const animation = this.mixer.clipAction(fashionAnimation); | ||
// 提供动画播放控制到 renderAuxiliary | ||
this.animations.push(animation); | ||
if (isFashionFemale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.female.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
if (isFashionMale(goodSecondaryClassification)) { | ||
const defaultAnimationFile = await loadFile({ | ||
url: defaultModelAnimation.male.domesticPath, | ||
type: 'fbx' | ||
}); | ||
const animation = this.mixer.clipAction(defaultAnimationFile.sceneGraph.animations[0]); | ||
this.defaultAnimation = animation; | ||
} | ||
// 设置预览与天空盒 | ||
const previewHelper = new PreviewDataHelper(); | ||
this.previewData = previewHelper.transPreviewDataToRender(previewData); | ||
const getCurrEnvMap = () => { | ||
const envMap = this.previewData.envMap || 'sky'; | ||
return new Promise((resolve) => { | ||
let processEnv = "domestic"; | ||
try { | ||
if (this.options.platform === "pc") { | ||
processEnv = process.env.VUE_APP_NAME; | ||
} else if (this.options.platform === "mobile") { | ||
processEnv = import.meta.env.VITE_APP_NAME; | ||
} | ||
} catch (e) { | ||
console.error(e); | ||
processEnv = "domestic"; | ||
} | ||
getEnvMapByName(envMap, processEnv).then((environment) => { | ||
if (environment) { | ||
this.setSceneEnvironment(environment.data); | ||
this.extraModels.forEach(modelRender => { | ||
modelRender.setModelMaterialEnvMap(environment.data); | ||
}); | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
this.modelsLoader.push( | ||
getCurrEnvMap() | ||
); | ||
} | ||
@@ -279,3 +877,3 @@ | ||
return new Promise((resolve) => { | ||
getEnvMapList().then((envMapList) => { | ||
getEnvMapList(processEnv).then((envMapList) => { | ||
resolve(envMapList); | ||
@@ -294,7 +892,15 @@ }); | ||
this.addModel(model); | ||
// 如果有 preview 的话就根据 previewData 执行相机动画,如果没有的话直接 focusModel | ||
this.focusModel(model); | ||
}); | ||
this.extraModels.forEach((modelRender) => { | ||
const model = modelRender.getModel(); | ||
this.addModel(model); | ||
}); | ||
if (this.mainModels.length) { | ||
this.focusModel(this.mainModels[0].getModel()); | ||
} else { | ||
this.focusModel(this.extraModels[this.extraModels.length - 1].getModel()); | ||
} | ||
this.setPreviewData(this.previewData); | ||
@@ -304,4 +910,2 @@ resolve(); | ||
}); | ||
// 如果是时装或个性动画类内容则播放默认动画 | ||
@@ -313,17 +917,21 @@ | ||
const previewHelper = new PreviewDataHelper(); | ||
const previewData = { | ||
cameraPosition: { | ||
x: this.camera.position.x, | ||
y: this.camera.position.y, | ||
z: this.camera.position.z, | ||
}, | ||
previewCenter: { | ||
x: this.cameraControls.center.x, | ||
y: this.cameraControls.center.y, | ||
z: this.cameraControls.center.z, | ||
}, | ||
ambientLight: this.ambientLight, | ||
directionalLight: this.directionalLights, | ||
}; | ||
return previewHelper.transPreviewDataToStore(previewData); | ||
if (this.camera) { | ||
const previewData = { | ||
cameraPosition: { | ||
x: this.camera.position.x, | ||
y: this.camera.position.y, | ||
z: this.camera.position.z, | ||
}, | ||
previewCenter: { | ||
x: this.cameraControls.center.x, | ||
y: this.cameraControls.center.y, | ||
z: this.cameraControls.center.z, | ||
}, | ||
ambientLight: this.ambientLight, | ||
directionalLight: this.directionalLights, | ||
}; | ||
return previewHelper.transPreviewDataToStore(previewData); | ||
} else { | ||
return {}; | ||
} | ||
} | ||
@@ -330,0 +938,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
382163
10462
22