3d出来后的卡片交互修改

This commit is contained in:
X1627315083@163.com
2026-04-14 13:03:18 +08:00
parent b16b41f44e
commit e3593f9e96
15 changed files with 499 additions and 621 deletions

View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1959 2.6173C12.4159 1.8333 11.1479 1.8333 10.3679 2.6173L3.01592 9.9693C2.23192 10.7493 2.23192 12.0173 3.01592 12.7973C3.79592 13.5813 5.06392 13.5813 5.84392 12.7973L13.1959 5.4453C13.9799 4.6653 13.9799 3.3973 13.1959 2.6173ZM12.5319 4.5213L11.1159 5.9333C11.0399 6.0133 10.9119 6.0133 10.8359 5.9333L10.2679 5.3693C10.1919 5.2893 10.1919 5.1613 10.2679 5.0853L11.6839 3.6693C11.7599 3.5933 11.8879 3.5933 11.9679 3.6693L12.5319 4.2373C12.6079 4.3133 12.6079 4.4413 12.5319 4.5213Z" fill="black"/>
<path d="M7.59998 3.6C8.04181 3.6 8.39998 3.24183 8.39998 2.8C8.39998 2.35817 8.04181 2 7.59998 2C7.15815 2 6.79998 2.35817 6.79998 2.8C6.79998 3.24183 7.15815 3.6 7.59998 3.6Z" fill="#FF7A51"/>
<path d="M11.624 9.412C11.752 9.064 12.244 9.064 12.376 9.412L12.8 10.56C12.84 10.668 12.928 10.756 13.036 10.796L14.184 11.22C14.532 11.348 14.532 11.84 14.184 11.972L13.036 12.396C12.928 12.436 12.84 12.524 12.8 12.632L12.376 13.78C12.248 14.128 11.756 14.128 11.624 13.78L11.2 12.632C11.16 12.524 11.072 12.436 10.964 12.396L9.81598 11.972C9.46798 11.844 9.46798 11.352 9.81598 11.22L10.964 10.796C11.072 10.756 11.16 10.668 11.2 10.56L11.624 9.412Z" fill="#FF7A51"/>
<path d="M3.62398 3.012C3.75198 2.664 4.24398 2.664 4.37598 3.012L4.58398 3.576C4.62398 3.684 4.71198 3.772 4.81998 3.812L5.38398 4.02C5.73198 4.148 5.73198 4.64 5.38398 4.772L4.81998 4.98C4.71198 5.02 4.62398 5.108 4.58398 5.216L4.37598 5.78C4.24798 6.128 3.75598 6.128 3.62398 5.78L3.41598 5.216C3.37598 5.108 3.28798 5.02 3.17998 4.98L2.61598 4.772C2.26798 4.644 2.26798 4.152 2.61598 4.02L3.17998 3.812C3.28798 3.772 3.37598 3.684 3.41598 3.576L3.62398 3.012Z" fill="#FF7A51"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,6 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4956 3.27113C15.5206 2.29113 13.9356 2.29113 12.9606 3.27113L3.77064 12.4611C2.79064 13.4361 2.79064 15.0211 3.77064 15.9961C4.74564 16.9761 6.33064 16.9761 7.30564 15.9961L16.4956 6.80613C17.4756 5.83113 17.4756 4.24613 16.4956 3.27113ZM15.6656 5.65113L13.8956 7.41613C13.8006 7.51613 13.6406 7.51613 13.5456 7.41613L12.8356 6.71113C12.7406 6.61113 12.7406 6.45113 12.8356 6.35613L14.6056 4.58613C14.7006 4.49113 14.8606 4.49113 14.9606 4.58613L15.6656 5.29613C15.7606 5.39113 15.7606 5.55113 15.6656 5.65113Z" fill="white"/>
<path d="M9.50059 4.5C10.0529 4.5 10.5006 4.05228 10.5006 3.5C10.5006 2.94772 10.0529 2.5 9.50059 2.5C8.9483 2.5 8.50059 2.94772 8.50059 3.5C8.50059 4.05228 8.9483 4.5 9.50059 4.5Z" fill="white"/>
<path d="M14.5306 11.765C14.6906 11.33 15.3056 11.33 15.4706 11.765L16.0006 13.2C16.0506 13.335 16.1606 13.445 16.2956 13.495L17.7306 14.025C18.1656 14.185 18.1656 14.8 17.7306 14.965L16.2956 15.495C16.1606 15.545 16.0506 15.655 16.0006 15.79L15.4706 17.225C15.3106 17.66 14.6956 17.66 14.5306 17.225L14.0006 15.79C13.9506 15.655 13.8406 15.545 13.7056 15.495L12.2706 14.965C11.8356 14.805 11.8356 14.19 12.2706 14.025L13.7056 13.495C13.8406 13.445 13.9506 13.335 14.0006 13.2L14.5306 11.765Z" fill="white"/>
<path d="M4.53059 3.765C4.69059 3.33 5.30559 3.33 5.47059 3.765L5.73059 4.47C5.78059 4.605 5.89059 4.715 6.02559 4.765L6.73059 5.025C7.16559 5.185 7.16559 5.8 6.73059 5.965L6.02559 6.225C5.89059 6.275 5.78059 6.385 5.73059 6.52L5.47059 7.225C5.31059 7.66 4.69559 7.66 4.53059 7.225L4.27059 6.52C4.22059 6.385 4.11059 6.275 3.97559 6.225L3.27059 5.965C2.83559 5.805 2.83559 5.19 3.27059 5.025L3.97559 4.765C4.11059 4.715 4.22059 4.605 4.27059 4.47L4.53059 3.765Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,7 +1,16 @@
<template>
<!-- 高级工具选择 -->
<div class="cards-select">
<div v-for="v in (node.data?.secondaryMenu?.selectList || list)" :key="v.type" @click="onClickItem(v)" v-show="v.tier === tier">
<div
v-for="v in (node.data?.secondaryMenu?.selectList || list)"
:key="v.type"
@click="onClickItem(v)"
v-show="
(v.tier === tier) &&
((v.type == NODE_DATATYPE.TO_REAL_VARIANTS && (superiorNode?.data?.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL && props.tier == NODE_DATATIER.TO_REAL_VARIANTS))||
(v.type != NODE_DATATYPE.TO_REAL_VARIANTS && (superiorNode?.data?.superiorNodeType != NODE_DATATYPE.TO_3D_MODEL || props.tier != NODE_DATATIER.TO_REAL_VARIANTS)))
"
>
<span class="icon">
<svg-icon :name="v.type + '-2'" size="15" size-unit="px" />
</span>
@@ -19,12 +28,20 @@
node: { required: true, type: Object },
tier: { default: 1, type: Number }
})
const superiorNode = computed(() => {
return stateManager.getNodeById(props.node.data.superiorID)
})
const list = ref([
{
tier: NODE_DATATIER.TO_REAL_STYLE,
type: NODE_DATATYPE.TO_REAL_STYLE,
title: 'To Real Style'
},
{
tier: NODE_DATATIER.TO_REAL_VARIANTS,
type: NODE_DATATYPE.TO_REAL_VARIANTS,
title: 'To Real Variants'
},
{
tier: NODE_DATATIER.SURFACE_EDIT,
type: NODE_DATATYPE.SURFACE_EDIT,

View File

@@ -36,6 +36,7 @@
import { computed, ref, useAttrs, onMounted, inject, watch } from 'vue'
import CardsSelect from './cards-select.vue'
import ToRealStyle from './to-real-style.vue'
import ToRealVariants from './to-real-variants.vue'
import SurfaceEdit from './surface-edit.vue'
import FastMode from './fast-mode.vue'
import SceneComposition from './scene-composition.vue'
@@ -69,6 +70,13 @@
component: ToRealStyle,
api: toRealStyleApi
},
{
tier: NODE_DATATIER.TO_REAL_VARIANTS,
type: NODE_DATATYPE.TO_REAL_VARIANTS,
title: 'To Real Variants',
component: ToRealVariants,
api: toRealStyleApi
},
{
tier: NODE_DATATIER.Fast_MODE,
type: NODE_DATATYPE.Fast_MODE,
@@ -197,6 +205,7 @@
data: {
superiorID: attrs.node.id,
superiorNodeType: attrs.node?.data?.type,
superiorGenerateImg: superiorNodeUrl || null,
createIndexPosition: index + subordNodes.length,
tier: tier,
isActive: index == 0 && subordNodes.length == 0,

View File

@@ -0,0 +1,98 @@
<template>
<!-- 转换为真实图 -->
<div class="to-real-style">
<p class="label">Prompt</p>
<my-textarea v-model="data.prompt" :placeholder="$t('flowCanvas.toRealVariantsPlaceholder')" />
<div class="shortcut-list">
<div
class="item"
v-for="v in shortcutList"
:key="v.value"
@click="data.prompt = v.value"
>
{{ v.label }}
</div>
</div>
<p class="label">Mode</p>
<my-select v-model="data.mode" :list="modeList" />
<p class="label">Size</p>
<pixel-ratio-selection v-model="data.pixelRatio" />
</div>
</template>
<script setup lang="ts">
import { inject, ref, reactive, useAttrs } from 'vue'
import myTextarea from '../../tools/my-textarea.vue'
import mySelect from '../../tools/my-select.vue'
import pixelRatioSelection from '../../tools/pixel-ratio-selection.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const shortcutList = ref([
{
label: t('flowCanvas.toRealVariantsShortcut1Label'),
value: t('flowCanvas.toRealVariantsShortcut1Value')
},
{
label: t('flowCanvas.toRealVariantsShortcut2Label'),
value: t('flowCanvas.toRealVariantsShortcut2Value')
},
{
label: t('flowCanvas.toRealVariantsShortcut3Label'),
value: t('flowCanvas.toRealVariantsShortcut3Value')
},
{
label: t('flowCanvas.toRealVariantsShortcut4Label'),
value: t('flowCanvas.toRealVariantsShortcut4Value')
},
{
label: t('flowCanvas.toRealVariantsShortcut5Label'),
value: t('flowCanvas.toRealVariantsShortcut5Value')
}
])
const stateManager = inject('stateManager') as any
const attrs = useAttrs()
const modeList = ref([
{ value: 'Advanced', label: 'Advanced' },
{ value: 'Normal', label: 'Normal' }
])
const data = reactive({
prompt: '',
pixelRatio: '1:1',
mode: 'Advanced',
})
const getApiData = ()=>{
let superior = stateManager.getNodeById(attrs.node?.data?.superiorID)
// let {superiorGenerateImg} = attrs
return {
mode: data.mode,
size: data.pixelRatio,
userPrompt: data.prompt,
aaa: superior?.data?.superiorGenerateImg,
}
}
defineExpose({ data, getApiData })
</script>
<style lang="less" scoped>
.to-real-style {
> .shortcut-list {
display: flex;
flex-wrap: wrap;
gap: 10px 4px;
user-select: none;
> .item {
display: flex;
align-items: center;
padding: 5px 3px;
font-family: Medium;
border-radius: 3px;
font-size: 10px;
border: 1px solid #e4e4e7;
background: #f0f0f0;
cursor: pointer;
}
}
}
</style>

View File

@@ -17,7 +17,7 @@
<span class="icon" @click="onDownload(item)">
<svg-icon name="download" size="20" size-unit="px" />
</span>
<button class="edit" @click="onEdit(item)" v-if="node.data.superiorNodeType !== NODE_DATATYPE.TO_3D_MODEL">
<button class="edit" @click="onEdit(item)" v-if="node.data.superiorNodeType !== NODE_DATATYPE.TO_3D_MODEL || node.data.tier == 0">
<span class="icon"><svg-icon name="edit" size="13" /></span>
<span class="text">Edit</span>
</button>
@@ -185,7 +185,13 @@
])
const onPreview = (item: any) => {
if(data.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL){
openThreeModelPreview({glbPath:item?.glbPath,glbInfoObj:item?.glbInfoObj,nodeId:props.node.id})
openThreeModelPreview({
glbPath:item?.glbPath,
glbInfoObj:item?.glbInfoObj,
nodeId:props.node?.id,
nodeType:props.node.data?.superiorNodeType,
superiorGenerateImg:props.node.data?.superiorGenerateImg,
})
}else{
openImagePreview(item.url)
}

View File

@@ -29,10 +29,11 @@ const captureView = async ()=>{
formData.append('file', file)
const minioUrl = await uploadImage(formData, true)
ElMessage.warning('Your new view has been captured.')
emit('captureView', {
minioUrl,
nodeId: props?.currentData?.nodeId
nodeId: props?.currentData?.nodeId,
nodeType: props?.currentData?.nodeType,
superiorGenerateImg: props?.currentData?.superiorGenerateImg,
})
}
onMounted(()=>{

View File

@@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n'
import gsap from 'gsap';
import * as THREE from 'three';
import { ThreeManager } from './threeTool copy'
import { ThreeManager } from './threeTool'
//const props = defineProps({
//})

View File

@@ -1,351 +0,0 @@
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
import hdri from '@/assets/images/three/hdri.hdr'
interface ModelInfo {
box: THREE.Box3;
center: THREE.Vector3;
size: THREE.Vector3;
maxSize: number;
}
const CONFIG = {
hdriIntensity: 7.4,
exposureBase: 0.92,
backlightBoost: { min: 2.1, max: 4.8 }, // 背光增强系数
hdriUrl: hdri,
};
export class ThreeManager {
threeDom: HTMLElement;
scene: THREE.Scene;//场景对象
camera: THREE.PerspectiveCamera;//相机对象
renderer: THREE.WebGLRenderer;//渲染器对象
controls: OrbitControls;//轨道控制器对象
pointLight: THREE.AmbientLight;//环境光对象
studioLights: any;//工作室光对象数组
v1: THREE.Vector3;//相机前向向量
camDir: THREE.Vector3;//相机前向向量
camForward: THREE.Vector3;//相机前向向量
camToTarget: THREE.Vector3;//相机目标向量
currentModel: any;//当前模型对象
animate: any;//动画对象
modelInfo: ModelInfo;//模型信息对象
defaultSoftboxPositions: Array<{ x: number; y: number; z: number; intensity: number; w: number; h: number }> = [
{ x: 0, y: 5.8, z: 3.5, intensity: 3.5, w: 6, h: 4.8 }, // 主光
{ x: 0, y: 2.8, z: 7.5, intensity: 2.2, w: 5, h: 4 }, // 前光
{ x: 0, y: 2.8, z: -7.5, intensity: 6.5, w: 5, h: 4 }, // 背光
{ x: -7.5, y: 2.8, z: 0, intensity: 10.8, w: 5, h: 4 }, // 侧光
{ x: 7.5, y: 2.8, z: 0, intensity: 2.0, w: 5, h: 4 }, // 侧光
{ x: 0, y: -2.2, z: 3, intensity: 0.8, w: 9, h: 4 } // 地面反光板
];
constructor(threeDom: HTMLElement,config:any = {}){
this.threeDom = threeDom;
this.studioLights = []
//创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xffffff);
//创建相机
this.camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 10000);
this.camera.position.set(0, 1.5, 6); //设置相机位置
this.v1 = new THREE.Vector3();
this.camDir = new THREE.Vector3();
this.camForward = new THREE.Vector3();
this.camToTarget = new THREE.Vector3();
//设置渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance", preserveDrawingBuffer: true });
//设置环境光
this.scene.add(new THREE.AmbientLight(0xffffff, 0.15));
// 关键优化:物理光照与色彩管理
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer.toneMappingExposure = CONFIG.exposureBase;
this.renderer.physicallyCorrectLights = true; // 开启物理光照模式,光衰减更真实
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(threeDom.offsetWidth, threeDom.offsetHeight); //设置渲染区域尺寸
this.renderer.setClearColor(0xffffff, 1); //设置背景颜色
threeDom.innerHTML = '';
threeDom.appendChild(this.renderer.domElement);
RectAreaLightUniformsLib.init();
//设置轨道控制器
this.controls = new OrbitControls(this.camera,this.renderer.domElement)//监听鼠标、键盘事件;
// controls.minDistance = 500; // 设置相机与焦点的最小距离
// controls.maxDistance = 4000; // 设置相机与焦点的最大距离
this.controls.mouseButtons = {
// LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转ROTATE)
LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转ROTATE)
MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
RIGHT:THREE.MOUSE.PAN // 右键 旋转默认拖动PAN
// RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转默认拖动PAN
}
this.controls.enableDamping = true;
}
/**
* 根据模型大小计算自适应配置
*/
calculateAdaptiveConfig(modelInfo: ModelInfo) {
const { size, maxSize, center } = modelInfo;
// 基础距离系数(可根据需要调整)
let distanceFactor = 1.5;
// 根据模型形状调整距离系数
const aspectRatio = size.x / size.y;
if (aspectRatio > 2) {
// 扁平模型,拉远一点
distanceFactor = 3.0;
} else if (aspectRatio < 0.5) {
// 高瘦模型,稍微拉近
distanceFactor = 2.0;
}
// 计算相机距离
const fov = this.camera.fov * (Math.PI / 180);
const cameraDistance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
// 灯光缩放系数
const lightScale = Math.max(0.5, Math.min(2.0, maxSize / 2.5));
// 强度缩放系数(模型越大,灯光需要越强)
const intensityScale = Math.max(0.6, Math.min(2.5, maxSize / 2));
console.log('自适应配置:', {
相机距离: cameraDistance,
灯光缩放: lightScale,
强度缩放: intensityScale,
目标中心: center
});
return {
cameraDistance,
lightScale,
intensityScale,
targetCenter: center.clone()
};
}
/**
* 获取模型信息(包围盒、中心点、尺寸等)
*/
getModelInfo(model: THREE.Object3D): ModelInfo {
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
console.log('模型信息:', {
中心点: center,
: { x: size.x, y: size.y, z: size.z },
最大尺寸: maxSize
});
return { box, center, size, maxSize };
}
/**
* 根据模型大小自适应创建柔光箱灯光
*/
createAdaptiveSoftboxes(modelInfo: ModelInfo) {
const { lightScale, intensityScale, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
// 清除现有灯光
this.studioLights.forEach(item => {
this.scene.remove(item.light);
});
this.studioLights = [];
// 根据模型大小创建自适应灯光
this.defaultSoftboxPositions.forEach(pos => {
const scaledX = pos.x * lightScale;
const scaledY = pos.y * lightScale;
const scaledZ = pos.z * lightScale;
const scaledW = pos.w * lightScale;
const scaledH = pos.h * lightScale;
const scaledIntensity = pos.intensity * intensityScale;
const light = new THREE.RectAreaLight(0xffffff, scaledIntensity, scaledW, scaledH);
light.position.set(
targetCenter.x + scaledX,
targetCenter.y + scaledY,
targetCenter.z + scaledZ
);
light.lookAt(targetCenter);
this.scene.add(light);
this.studioLights.push({
light,
offset: new THREE.Vector3(scaledX, scaledY, scaledZ),
baseIntensity: scaledIntensity
});
});
console.log('自适应灯光已创建,缩放系数:', lightScale, '强度系数:', intensityScale);
}
/**
* 根据模型大小调整相机位置
*/
adjustCameraToModel(modelInfo: ModelInfo) {
const { cameraDistance, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
// 正面俯视角度:相机在模型正前方,稍高于模型中心
// X 轴:模型中心(正面视角,不左右偏移)
// Y 轴:相机高度 = 模型中心高度 + 模型高度的 0.3 倍(俯视效果)
// Z 轴:相机距离 = 根据模型大小计算的距离
const x = targetCenter.x;
const y = targetCenter.y + modelInfo.size.y * 0.3; // 相机高度为模型中心的 0.3 倍
const z = targetCenter.z + cameraDistance;
this.camera.position.set(x, y, z);
this.controls.target.copy(targetCenter);
this.controls.update();
}
createSoftbox(x, y, z, intensity, w = 5, h = 4) {
const light = new THREE.RectAreaLight(0xffffff, intensity, w, h);
light.position.set(x, y, z);
light.lookAt(0, 0, 0);
this.scene.add(light);
// 存储 offset 向量副本,避免后续重复创建
this.studioLights.push({
light,
offset: new THREE.Vector3(x, y, z),
baseIntensity: intensity
});
}
async setHDRI(){
const rgbeLoader = new RGBELoader()
await rgbeLoader.load(CONFIG.hdriUrl, (texture)=>{
texture.mapping = THREE.EquirectangularReflectionMapping;
const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
this.scene.environment = envMap;
this.scene.environmentIntensity = CONFIG.hdriIntensity;
pmremGenerator.dispose();
texture.dispose();
console.log('✅ HDRI Loaded');
});
this.createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
this.createSoftbox(0, 2.8, 7.5, 2.2); // 前光
this.createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
this.createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
this.createSoftbox(7.5, 2.8, 0, 2.0);
this.createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
}
// 更新工作室光位置
updateStudioLighting(){
this.camera.getWorldDirection(this.camDir);
this.studioLights.forEach(item => {
// 使用 applyQuaternion 同步灯光位置
this.v1.copy(item.offset).applyQuaternion(this.camera.quaternion);
item.light.position.copy(this.controls.target).add(this.v1);
item.light.lookAt(this.controls.target);
// 动态背光逻辑
if (item.offset.z < -2) {
this.v1.copy(item.light.position).sub(this.controls.target).normalize();
const dot = this.v1.dot(this.camDir);
const factor = THREE.MathUtils.lerp(CONFIG.backlightBoost.min, CONFIG.backlightBoost.max, Math.max(0, 1 - dot));
item.light.intensity = item.baseIntensity * factor;
}
});
}
//动态设置曝光
updateImagePipeline() {
let exposure = 1.0;
this.camera.getWorldDirection(this.camForward);
this.camToTarget.copy(this.controls.target).sub(this.camera.position).normalize();
const facing = Math.abs(this.camForward.dot(this.camToTarget));
const targetExposure = THREE.MathUtils.lerp(1.25, 0.90, facing);
exposure = THREE.MathUtils.lerp(exposure, targetExposure, 0.08);
this.renderer.toneMappingExposure = CONFIG.exposureBase * exposure;
}
async setModel(modelUrl,load){
await new Promise((resolve, reject) => {
const drac = new DRACOLoader()
drac.setDecoderPath('/draco/')
const loader = new GLTFLoader().setDRACOLoader(drac);
if (this.currentModel) {
this.scene.remove(this.currentModel);
this.dispose(this.currentModel); // 核心优化:释放显存
}
loader.load(modelUrl,
(gltf) => {
this.currentModel = gltf.scene;
// 遍历模型:增强材质表现
this.currentModel.traverse(child => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
if (child.material) {
child.material.envMapIntensity = 1.2; // 增强 HDRI 反射强度
}
}
});
const box = new THREE.Box3().setFromObject(this.currentModel);
const center = box.getCenter(new THREE.Vector3());
// this.currentModel.position.sub(center);
this.scene.add(this.currentModel);
this.modelInfo = this.getModelInfo(this.currentModel)
// 根据模型大小调整相机位置
this.adjustCameraToModel(this.modelInfo);
// 根据模型大小创建自适应灯光
this.createAdaptiveSoftboxes(this.modelInfo);
resolve('')
},
(xhr: any) => { // 加载进度回调
const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
load.value.progress = percent
console.log('模型加载进度:', percent);
},
(error: any) => { // 加载失败回调
console.error('模型加载失败:', error);
resolve('')
}
);
})
}
operation(){
let this_ = this
const animate = () => {
requestAnimationFrame(animate);
this.controls.update();
this.updateStudioLighting();
this.updateImagePipeline();
this.renderer.render(this.scene, this.camera);
}
animate();
}
// 释放模型资源
dispose(obj){
if (!obj) return;
obj.traverse(node => {
if (node.isMesh) {
if (node.geometry) node.geometry.dispose();
if (node.material) {
if (Array.isArray(node.material)) {
node.material.forEach(m => m.dispose());
} else {
node.material.dispose();
}
}
}
});
}
exportAsImage(){
return this.renderer.domElement.toDataURL('image/png');
}
}

View File

@@ -3,291 +3,349 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
import hdri from '@/assets/images/three/hdri.hdr'
export const initThree = async (threeDom)=>{
const scene = new THREE.Scene();
const group = new THREE.Group()
scene.add(group)
const studioLights = []
//创建相机对象
// this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
const camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 1000);
camera.position.set(0, 1.5, 5);
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
/**
* 创建渲染器对象
*/
interface ModelInfo {
box: THREE.Box3;
center: THREE.Vector3;
size: THREE.Vector3;
maxSize: number;
}
const CONFIG = {
hdriIntensity: 7.4,
exposureBase: 0.92,
backlightBoost: { min: 2.1, max: 4.8 }, // 背光增强系数
hdriUrl: hdri,
};
export class ThreeManager {
threeDom: HTMLElement;
scene: THREE.Scene;//场景对象
camera: THREE.PerspectiveCamera;//相机对象
renderer: THREE.WebGLRenderer;//渲染器对象
controls: OrbitControls;//轨道控制器对象
const width = threeDom.offsetWidth; //窗口宽度
const height = threeDom.offsetHeight; //窗口高度
const renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,//深度缓存 防止模型闪烁重影
});
pointLight: THREE.AmbientLight;//环境光对象
studioLights: any;//工作室光对象数组
v1: THREE.Vector3;//相机前向向量
camDir: THREE.Vector3;//相机前向向量
camForward: THREE.Vector3;//相机前向向量
camToTarget: THREE.Vector3;//相机目标向量
renderer.toneMapping = THREE.ACESFilmicToneMapping;//设置色调
renderer.toneMappingExposure = 1.3;
currentModel: any;//当前模型对象
animate: any;//动画对象
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height); //设置渲染区域尺寸
renderer.setClearColor(0xffffff, 1); //设置背景颜色
threeDom.innerHTML = '';
threeDom.appendChild(renderer.domElement);
modelInfo: ModelInfo;//模型信息对象
defaultSoftboxPositions: Array<{ x: number; y: number; z: number; intensity: number; w: number; h: number }> = [
{ x: 0, y: 5.8, z: 3.5, intensity: 3.5, w: 6, h: 4.8 }, // 主光
{ x: 0, y: 2.8, z: 7.5, intensity: 2.2, w: 5, h: 4 }, // 前光
{ x: 0, y: 2.8, z: -7.5, intensity: 6.5, w: 5, h: 4 }, // 背光
{ x: -7.5, y: 2.8, z: 0, intensity: 10.8, w: 5, h: 4 }, // 侧光
{ x: 7.5, y: 2.8, z: 0, intensity: 2.0, w: 5, h: 4 }, // 侧光
{ x: 0, y: -2.2, z: 3, intensity: 0.8, w: 9, h: 4 } // 地面反光板
];
constructor(threeDom: HTMLElement,config:any = {}){
// 设置渲染器大小
this.threeDom = threeDom;
this.studioLights = []
//创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xffffff);
//创建相机
this.camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 10000);
this.camera.position.set(0, 1.5, 6); //设置相机位置
this.v1 = new THREE.Vector3();
this.camDir = new THREE.Vector3();
this.camForward = new THREE.Vector3();
this.camToTarget = new THREE.Vector3();
//设置渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance", preserveDrawingBuffer: true });
//设置环境光
this.scene.add(new THREE.AmbientLight(0xffffff, 0.15));
// 关键优化:物理光照与色彩管理
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer.toneMappingExposure = CONFIG.exposureBase;
this.renderer.physicallyCorrectLights = true; // 开启物理光照模式,光衰减更真实
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(threeDom.offsetWidth, threeDom.offsetHeight); //设置渲染区域尺寸
this.renderer.setClearColor(0xffffff, 1); //设置背景颜色
threeDom.innerHTML = '';
threeDom.appendChild(this.renderer.domElement);
RectAreaLightUniformsLib.init();
//设置轨道控制器
this.controls = new OrbitControls(this.camera,this.renderer.domElement)//监听鼠标、键盘事件;
// controls.minDistance = 500; // 设置相机与焦点的最小距离
// controls.maxDistance = 4000; // 设置相机与焦点的最大距离
this.controls.mouseButtons = {
// LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转ROTATE)
LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转ROTATE)
MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
RIGHT:THREE.MOUSE.PAN // 右键 旋转默认拖动PAN
// RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转默认拖动PAN
}
this.controls.enableDamping = true;
const controls = new OrbitControls(camera,renderer.domElement)//监听鼠标、键盘事件;
// controls.minDistance = 500; // 设置相机与焦点的最小距离
// controls.maxDistance = 4000; // 设置相机与焦点的最大距离
controls.mouseButtons = {
// LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转ROTATE)
LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转ROTATE)
MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
RIGHT:THREE.MOUSE.PAN // 右键 旋转默认拖动PAN
// RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转默认拖动PAN
}
//使用hdri文件
try {
const rgbeLoader = new RGBELoader()
const hdrTexture = await rgbeLoader.loadAsync(hdri)
hdrTexture.mapping = THREE.EquirectangularMapping
/**
* 根据模型大小计算自适应配置
*/
calculateAdaptiveConfig(modelInfo: ModelInfo) {
const { size, maxSize, center } = modelInfo;
// 基础距离系数(可根据需要调整)
let distanceFactor = 1.5;
// 根据模型形状调整距离系数
const aspectRatio = size.x / size.y;
if (aspectRatio > 2) {
// 扁平模型,拉远一点
distanceFactor = 3.0;
} else if (aspectRatio < 0.5) {
// 高瘦模型,稍微拉近
distanceFactor = 2.0;
}
// 计算相机距离
const fov = this.camera.fov * (Math.PI / 180);
const cameraDistance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
// 灯光缩放系数
const lightScale = Math.max(0.5, Math.min(2.0, maxSize / 2.5));
// 强度缩放系数(模型越大,灯光需要越强)
const intensityScale = Math.max(0.6, Math.min(2.5, maxSize / 2));
console.log('自适应配置:', {
相机距离: cameraDistance,
灯光缩放: lightScale,
强度缩放: intensityScale,
目标中心: center
});
return {
cameraDistance,
lightScale,
intensityScale,
targetCenter: center.clone()
};
}
/**
* 获取模型信息(包围盒、中心点、尺寸等)
*/
getModelInfo(model: THREE.Object3D): ModelInfo {
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
console.log('模型信息:', {
中心点: center,
: { x: size.x, y: size.y, z: size.z },
最大尺寸: maxSize
});
return { box, center, size, maxSize };
}
/**
* 根据模型大小自适应创建柔光箱灯光
*/
createAdaptiveSoftboxes(modelInfo: ModelInfo) {
const { lightScale, intensityScale, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
// 清除现有灯光
this.studioLights.forEach(item => {
this.scene.remove(item.light);
});
this.studioLights = [];
// 设置环境贴图(影响材质反射)
scene.environment = hdrTexture
// 可选:同时设置为背景
scene.background = hdrTexture
// 根据模型大小创建自适应灯光
this.defaultSoftboxPositions.forEach(pos => {
const scaledX = pos.x * lightScale;
const scaledY = pos.y * lightScale;
const scaledZ = pos.z * lightScale;
const scaledW = pos.w * lightScale;
const scaledH = pos.h * lightScale;
const scaledIntensity = pos.intensity * intensityScale;
const light = new THREE.RectAreaLight(0xffffff, scaledIntensity, scaledW, scaledH);
light.position.set(
targetCenter.x + scaledX,
targetCenter.y + scaledY,
targetCenter.z + scaledZ
);
light.lookAt(targetCenter);
this.scene.add(light);
this.studioLights.push({
light,
offset: new THREE.Vector3(scaledX, scaledY, scaledZ),
baseIntensity: scaledIntensity
});
});
console.log('自适应灯光已创建,缩放系数:', lightScale, '强度系数:', intensityScale);
}
/**
* 根据模型大小调整相机位置
*/
adjustCameraToModel(modelInfo: ModelInfo) {
const { cameraDistance, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
// 正面俯视角度:相机在模型正前方,稍高于模型中心
// X 轴:模型中心(正面视角,不左右偏移)
// Y 轴:相机高度 = 模型中心高度 + 模型高度的 0.3 倍(俯视效果)
// Z 轴:相机距离 = 根据模型大小计算的距离
const x = targetCenter.x;
const y = targetCenter.y + modelInfo.size.y * 0.3; // 相机高度为模型中心的 0.3 倍
const z = targetCenter.z + cameraDistance;
console.log('HDR 环境贴图加载成功:', hdri)
} catch (error) {
console.error('HDR 加载失败:', error)
// 降级方案:使用环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
scene.add(ambientLight)
this.camera.position.set(x, y, z);
this.controls.target.copy(targetCenter);
this.controls.update();
}
/**
* 光源设置
*/
//点光源
/**
* AmbientLight 环境光
PointLight 点光源
DirectionalLight 平行光,比如太阳光
SpotLight 聚光源
*/
//设置环境光全亮
//环境光
const pointLight = new THREE.AmbientLight(0xffffff,.8);
// scene.add(pointLight);
function createSoftbox(x, y, z, intensity, w = 5, h = 4) {
createSoftbox(x, y, z, intensity, w = 5, h = 4) {
const light = new THREE.RectAreaLight(0xffffff, intensity, w, h);
light.position.set(x, y, z);
light.lookAt(0, 0, 0);
scene.add(light);
this.scene.add(light);
// 存储 offset 向量副本,避免后续重复创建
studioLights.push({
this.studioLights.push({
light,
offset: new THREE.Vector3(x, y, z),
baseIntensity: intensity
});
}
// 灯光布局优化
createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
createSoftbox(0, 2.8, 7.5, 2.2); // 前光
createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
createSoftbox(7.5, 2.8, 0, 2.0);
createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
// const pointLight = new THREE.AmbientLight(0xffffff,1.0);
// pointLight.intensity = 1.2//光源强度
// pointLight.castShadow = true//开启阴影
// pointLight.shadow.mapSize = new THREE.Vector2(width, height)
// scene.add(pointLight); //点光源添加到场景中
// pointLight.position.set(400, 200, 300); //点光源位置
// pointLight.position.y = 100;
// pointLight.position.z = 50;
// pointLight.position.x = 100;
// let floorGeometry = new THREE.PlaneGeometry(5000, 3000)//地板大小
// let floorMaterial = new THREE.MeshPhongMaterial({ color: "#7e7ab0", })
// let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
// floorMesh.rotation.x = -0.5 * Math.PI;
// floorMesh.receiveShadow = true;
// floorMesh.position.y = -0.001;
// scene.add(floorMesh);
const textureLoader = new THREE.TextureLoader();
// const texture = textureLoader.load('/3dModel/sketch-thick.jpg');
scene.background = new THREE.Color("#fff");
return {scene,group,camera,renderer,controls,pointLight,studioLights}
}
export const clearModel = (group,scene)=>{
const oldGroup:any = group.value;
group.value = new THREE.Group();
scene.value.add(group.value);
scene.value.remove(oldGroup);
}
// 计算模型包围盒
export const getModelInfo = (model: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
return {
box,
center,
size,
maxSize
};
};
// 根据模型信息计算相机位置
export const calculateCameraPosition = (
modelInfo: ReturnType<typeof getModelInfo>,
camera: THREE.PerspectiveCamera,
options?: {
distanceFactor?: number; // 距离系数默认1.5
heightFactor?: number; // 高度偏移系数默认0.3
angle?: number; // 观察角度,默认正前方
}
) => {
const { center, size, maxSize } = modelInfo;
const fov = camera.fov * (Math.PI / 180);
const distanceFactor = options?.distanceFactor ?? 1.5;
const heightFactor = options?.heightFactor ?? 0.3;
// 计算合适的相机距离
const distance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
// 根据角度计算相机位置
const angle = options?.angle ?? 0; // 0表示正前方
return {
position: new THREE.Vector3(
center.x + distance * Math.sin(angle),
center.y + size.y * heightFactor,
center.z + distance * Math.cos(angle)
),
target: center.clone(),
distance,
center,
size
};
};
// 根据模型信息计算光源位置
export const calculateLightPosition = (
modelInfo: ReturnType<typeof getModelInfo>,
options?: {
xFactor?: number; // X轴偏移系数默认0.5
yFactor?: number; // Y轴偏移系数默认0.8
zFactor?: number; // Z轴偏移系数默认0.5
}
) => {
const { center, size } = modelInfo;
const xFactor = options?.xFactor ?? 0.5;
const yFactor = options?.yFactor ?? 0.8;
const zFactor = options?.zFactor ?? 0.5;
return new THREE.Vector3(
center.x + size.x * xFactor,
center.y + size.y * yFactor,
center.z + size.z * zFactor
);
};
export const addModel = async (
url: any,
controls: OrbitControls,
camera: THREE.PerspectiveCamera,
pointLight: THREE.DirectionalLight,
group: THREE.Group,
load: any
) => {
await new Promise((resolve, reject) => {
const fbxLoader = new GLTFLoader();
const drac = new DRACOLoader()
drac.setDecoderPath('/draco/')
fbxLoader.setDRACOLoader(drac)
fbxLoader.load(url,
(obj: any) => {
const scene = obj.scene;
scene.traverse((child: any) => {
if (child.isMesh) {
// 如果是基础材质,转换为标准材质
if (child.material instanceof THREE.MeshBasicMaterial) {
const oldMat = child.material;
child.material = new THREE.MeshStandardMaterial({
map: oldMat.map,
color: oldMat.color,
roughness: 0.4,
metalness: 0
});
}
// 如果是标准材质,调整粗糙度
else if (child.material instanceof THREE.MeshStandardMaterial) {
child.material.roughness = 0.4;
child.material.metalness = 0;
}
}
});
// 获取模型信息
const modelInfo = getModelInfo(scene);
const { position: cameraPos, target } = calculateCameraPosition(
modelInfo,
camera.value,
{
distanceFactor: 1.5,
heightFactor: 0.3,
angle: 0 // 正前方
}
);
// 设置相机位置
camera.value.position.copy(cameraPos);
// 设置控制器目标点
controls.value.target.copy(target);
// 计算并设置光源位置
const lightPos = calculateLightPosition(modelInfo);
pointLight.value.position.copy(lightPos);
// 将模型添加到场景
group.value.add(scene);
// 可选:将模型信息存储在模型上,方便后续使用
(scene as any).userData.modelInfo = modelInfo;
resolve('')
},
(xhr: any) => { // 加载进度回调
const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
load.value.progress = percent
console.log('模型加载进度:', percent);
},
(error: any) => { // 加载失败回调
console.error('模型加载失败:', error);
reject('')
}
)
})
}
// 导出当前视图为图片
export const exportAsImage = (renderer, camera, scene, filename = 'model.png')=>{
// 渲染当前场景
renderer.render(scene, camera)
async setHDRI(){
const rgbeLoader = new RGBELoader()
await rgbeLoader.load(CONFIG.hdriUrl, (texture)=>{
texture.mapping = THREE.EquirectangularReflectionMapping;
const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
// 获取 canvas 数据
const canvas = renderer.domElement
const dataURL = canvas.toDataURL('image/png')
return dataURL
this.scene.environment = envMap;
this.scene.environmentIntensity = CONFIG.hdriIntensity;
pmremGenerator.dispose();
texture.dispose();
console.log('✅ HDRI Loaded');
});
this.createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
this.createSoftbox(0, 2.8, 7.5, 2.2); // 前光
this.createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
this.createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
this.createSoftbox(7.5, 2.8, 0, 2.0);
this.createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
}
// 更新工作室光位置
updateStudioLighting(){
this.camera.getWorldDirection(this.camDir);
this.studioLights.forEach(item => {
// 使用 applyQuaternion 同步灯光位置
this.v1.copy(item.offset).applyQuaternion(this.camera.quaternion);
item.light.position.copy(this.controls.target).add(this.v1);
item.light.lookAt(this.controls.target);
// 动态背光逻辑
if (item.offset.z < -2) {
this.v1.copy(item.light.position).sub(this.controls.target).normalize();
const dot = this.v1.dot(this.camDir);
const factor = THREE.MathUtils.lerp(CONFIG.backlightBoost.min, CONFIG.backlightBoost.max, Math.max(0, 1 - dot));
item.light.intensity = item.baseIntensity * factor;
}
});
}
//动态设置曝光
updateImagePipeline() {
let exposure = 1.0;
this.camera.getWorldDirection(this.camForward);
this.camToTarget.copy(this.controls.target).sub(this.camera.position).normalize();
const facing = Math.abs(this.camForward.dot(this.camToTarget));
const targetExposure = THREE.MathUtils.lerp(1.25, 0.90, facing);
exposure = THREE.MathUtils.lerp(exposure, targetExposure, 0.08);
this.renderer.toneMappingExposure = CONFIG.exposureBase * exposure;
}
async setModel(modelUrl,load){
await new Promise((resolve, reject) => {
const drac = new DRACOLoader()
drac.setDecoderPath('/draco/')
const loader = new GLTFLoader().setDRACOLoader(drac);
if (this.currentModel) {
this.scene.remove(this.currentModel);
this.dispose(this.currentModel); // 核心优化:释放显存
}
loader.load(modelUrl,
(gltf) => {
this.currentModel = gltf.scene;
// 遍历模型:增强材质表现
this.currentModel.traverse(child => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
if (child.material) {
child.material.envMapIntensity = 1.2; // 增强 HDRI 反射强度
}
}
});
const box = new THREE.Box3().setFromObject(this.currentModel);
const center = box.getCenter(new THREE.Vector3());
// this.currentModel.position.sub(center);
this.scene.add(this.currentModel);
this.modelInfo = this.getModelInfo(this.currentModel)
// 根据模型大小调整相机位置
this.adjustCameraToModel(this.modelInfo);
// 根据模型大小创建自适应灯光
this.createAdaptiveSoftboxes(this.modelInfo);
resolve('')
},
(xhr: any) => { // 加载进度回调
const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
load.value.progress = percent
console.log('模型加载进度:', percent);
},
(error: any) => { // 加载失败回调
console.error('模型加载失败:', error);
resolve('')
}
);
})
}
operation(){
let this_ = this
const animate = () => {
requestAnimationFrame(animate);
this.controls.update();
this.updateStudioLighting();
this.updateImagePipeline();
this.renderer.render(this.scene, this.camera);
}
animate();
}
// 释放模型资源
dispose(obj){
if (!obj) return;
obj.traverse(node => {
if (node.isMesh) {
if (node.geometry) node.geometry.dispose();
if (node.material) {
if (Array.isArray(node.material)) {
node.material.forEach(m => m.dispose());
} else {
node.material.dispose();
}
}
}
});
}
exportAsImage(){
return this.renderer.domElement.toDataURL('image/png');
}
}

View File

@@ -258,6 +258,8 @@
nodeManager.createResultNode({
data: {
superiorID_: captureData.nodeId,
superiorNodeType: captureData.nodeType,
superiorGenerateImg: captureData.superiorGenerateImg,
data: {
selectable: false,
imageProcessTasks: [

View File

@@ -9,6 +9,7 @@ interface NodeData {
superiorID?: string// 上级节点ID有连接线
superiorID_?: string// 上级节点ID没有连接线
superiorNodeType?: string// 上级节点类型
superiorGenerateImg?: string// 上级生成节点图片
disableDelete?: boolean// 是否禁用删除
disableCopy?: boolean// 是否禁用复制
createIndexPosition?: number// 创建索引位置
@@ -97,6 +98,7 @@ export class NodeManager {
}
/** 创建结果节点 */
createResultNode(options?: NodeOptions) {
console.log(options)
const options_ = {
...(options ? options : {}),
component: NODE_COMPONENT.RESULT_IMAGE,

View File

@@ -24,6 +24,7 @@ export const NODE_DATATYPE = {
RESULT_IMAGE: 'result-image',
CARDS_SELECT: 'cards-select',
TO_REAL_STYLE: 'to-real-style',
TO_REAL_VARIANTS: 'to-real-variants',
SURFACE_EDIT: 'surface-edit',
CANVAS_MODE: 'canvas-mode',
Fast_MODE: 'fast-mode',
@@ -39,6 +40,7 @@ export const NODE_DATATIER = {
RESULT_IMAGE: 0,
CARDS_SELECT: 0,
TO_REAL_STYLE: 1,
TO_REAL_VARIANTS: 1,
SURFACE_EDIT: 1,
CANVAS_MODE: 1,
Fast_MODE: 1,

View File

@@ -214,7 +214,18 @@ export default {
threeModelDesignAssistant:
"🔄 I'll turn your render into a 3D model you can rotate and look at from any angle. I'd recommend paying close attention to the corner joints, leg proportions, and seat depth — these are the spots that are easy to miss in a sketch but tend to cause the most trouble during prototyping. Better to catch them now while it's easy to fix.",
to3DViewDesignAssistant:
"📐 We're at the final step! I'll export your 3D model as front, side, and top view!"
"📐 We're at the final step! I'll export your 3D model as front, side, and top view!",
toRealVariantsShortcut1Label: 'Change the...',
toRealVariantsShortcut1Value: 'Change the sofa backrest shape.',
toRealVariantsShortcut2Label: 'The chair legs...',
toRealVariantsShortcut2Value: 'The chair legs were modified.',
toRealVariantsShortcut3Label: 'Adjust the...',
toRealVariantsShortcut3Value: "Adjust the chair's armrest design.",
toRealVariantsShortcut4Label: 'Replace...',
toRealVariantsShortcut4Value: 'Replace tabletop shape with drawn shape. ',
toRealVariantsShortcut5Label: 'Modified the outer...',
toRealVariantsShortcut5Value: 'Modified the outer contour of the chair back.',
toRealVariantsPlaceholder: 'Enter the furniture details you modified...',
},
assistant: {
inputPlaceholder: 'Ask anything'

View File

@@ -209,7 +209,18 @@ export default {
threeModelDesignAssistant:
'🔄 我把你的效果图变成可以转着看的立体模型,你可以从各个角度检查一下结构。我建议重点看看转角、腿脚比例和座面厚度——这几个地方在草图里不容易发现问题,但打样的时候最容易出偏差,现在发现比较好改。',
to3DViewDesignAssistant:
'📐 我们到最后一步了!我来帮你把 3D 模型导出为前视图、侧视图和俯视图!'
'📐 我们到最后一步了!我来帮你把 3D 模型导出为前视图、侧视图和俯视图!',
toRealVariantsShortcut1Label: '修改...',
toRealVariantsShortcut1Value: '修改了沙发靠背的形状.',
toRealVariantsShortcut2Label: '椅子腿...',
toRealVariantsShortcut2Value: '修改了椅子腿的形状.',
toRealVariantsShortcut3Label: '更换...',
toRealVariantsShortcut3Value: "更换了椅子的扶手设计.",
toRealVariantsShortcut4Label: '替换...',
toRealVariantsShortcut4Value: '桌面形状完全换成画成的形状.',
toRealVariantsShortcut5Label: '修改...',
toRealVariantsShortcut5Value: '修改了椅背的外轮廓.',
toRealVariantsPlaceholder: '请输入修改后的家具详情...',
},
assistant: {
inputPlaceholder: '请输入'