当前位置: 首页 > news >正文

vue vite+three在线编辑模型导入导出

文章目录

  • 一、1.0.0版本
    • 1.新增
    • 2.编辑
    • 3.导出
    • 4.导入
  • 总结

要实现一个类似于数字孪生的场景 可以在线、新增、删除模型 、以及编辑模型的颜色、长宽高
然后还要实现 编辑完后 保存为json数据 记录模型数据 既可以导入也可以导出

一、1.0.0版本

1.新增

先拿建议的立方体来代替模型
点击新增按钮就新增一个立方体
在这里插入图片描述

2.编辑

点击编辑按钮可以修改坐标 长宽高 颜色等等信息
在这里插入图片描述

3.导出

点击导出按钮 可以导出为json数据格式
在这里插入图片描述

在这里插入图片描述

4.导入

选择导入刚才的json文件
在这里插入图片描述
有一个bug 就是导入后颜色丢失了 点击模型 信息面板的颜色显示正常 渲染颜色丢失
在这里插入图片描述


源码

<template><div id="app" @click="onAppClick"><div id="info"><button @click.stop="addBuilding">新增</button><button @click.stop="showEditor">编辑</button><button @click.stop="exportModelData">导出</button><input type="file" @change="importModelData" ref="fileInput" /></div><div id="editor" v-if="editorVisible" @click.stop><h3>Edit Building</h3><label for="color">Color:</label><input type="color" id="color" v-model="selectedObjectProps.color" /><br /><label for="posX">Position X:</label><inputtype="number"id="posX"v-model="selectedObjectProps.posX"step="0.1"/><br /><label for="posY">Position Y:</label><inputtype="number"id="posY"v-model="selectedObjectProps.posY"step="0.1"/><br /><label for="posZ">Position Z:</label><inputtype="number"id="posZ"v-model="selectedObjectProps.posZ"step="0.1"/><br /><label for="scaleX">Scale X:</label><inputtype="number"id="scaleX"v-model="selectedObjectProps.scaleX"step="0.1"/><br /><label for="scaleY">Scale Y:</label><inputtype="number"id="scaleY"v-model="selectedObjectProps.scaleY"step="0.1"/><br /><label for="scaleZ">Scale Z:</label><inputtype="number"id="scaleZ"v-model="selectedObjectProps.scaleZ"step="0.1"/><br /><label for="rotX">Rotation X:</label><inputtype="number"id="rotX"v-model="selectedObjectProps.rotX"step="0.1"/><br /><label for="rotY">Rotation Y:</label><inputtype="number"id="rotY"v-model="selectedObjectProps.rotY"step="0.1"/><br /><label for="rotZ">Rotation Z:</label><inputtype="number"id="rotZ"v-model="selectedObjectProps.rotZ"step="0.1"/><br /><button @click="applyEdit">保存</button><button @click="deleteBuilding">删除</button></div><div ref="canvasContainer" style="width: 100vw; height: 100vh"></div></div>
</template><script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";export default {data() {return {editorVisible: false,selectedObject: null,selectedObjectProps: {color: "#00ff00",posX: 0,posY: 0,posZ: 0,scaleX: 1,scaleY: 1,scaleZ: 1,rotX: 0,rotY: 0,rotZ: 0,},raycaster: null,};},mounted() {this.init();this.animate();window.addEventListener("resize", this.onWindowResize, false);this.loadModelData(); // Load saved model data on page load},methods: {init() {console.log("Initializing Three.js");this.scene = new THREE.Scene();this.scene.background = new THREE.Color(0xcccccc);this.camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,0.1,1000);this.camera.position.set(0, 10, 20);this.renderer = new THREE.WebGLRenderer({ antialias: true });this.renderer.setSize(window.innerWidth, window.innerHeight);this.$refs.canvasContainer.appendChild(this.renderer.domElement);this.controls = new OrbitControls(this.camera, this.renderer.domElement);const light = new THREE.DirectionalLight(0xffffff, 1);light.position.set(5, 10, 7.5);this.scene.add(light);this.raycaster = new THREE.Raycaster();const geometry = new THREE.BoxGeometry(1, 1, 1);const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });this.cube = new THREE.Mesh(geometry, material);this.scene.add(this.cube);},onWindowResize() {this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();this.renderer.setSize(window.innerWidth, window.innerHeight);},onAppClick(event) {const mouse = new THREE.Vector2();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;this.raycaster.setFromCamera(mouse, this.camera);const intersects = this.raycaster.intersectObjects(this.scene.children, true);if (intersects.length > 0) {this.selectedObject = intersects[0].object;console.log("Object selected:", this.selectedObject);this.showEditor();}},addBuilding() {const geometry = new THREE.BoxGeometry(1, 1, 1);const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });const building = new THREE.Mesh(geometry, material);building.position.set(Math.random() * 10 - 5, 0.5, Math.random() * 10 - 5);this.scene.add(building);},showEditor() {if (this.selectedObject) {this.editorVisible = true;this.updateEditor(this.selectedObject);}},updateEditor(object) {this.selectedObjectProps.color = `#${object.material.color.getHexString()}`;this.selectedObjectProps.posX = object.position.x;this.selectedObjectProps.posY = object.position.y;this.selectedObjectProps.posZ = object.position.z;this.selectedObjectProps.scaleX = object.scale.x;this.selectedObjectProps.scaleY = object.scale.y;this.selectedObjectProps.scaleZ = object.scale.z;this.selectedObjectProps.rotX = object.rotation.x;this.selectedObjectProps.rotY = object.rotation.y;this.selectedObjectProps.rotZ = object.rotation.z;},applyEdit() {if (this.selectedObject) {const color = this.selectedObjectProps.color;this.selectedObject.material.color.set(color);this.selectedObject.position.set(parseFloat(this.selectedObjectProps.posX),parseFloat(this.selectedObjectProps.posY),parseFloat(this.selectedObjectProps.posZ));this.selectedObject.scale.set(parseFloat(this.selectedObjectProps.scaleX),parseFloat(this.selectedObjectProps.scaleY),parseFloat(this.selectedObjectProps.scaleZ));this.selectedObject.rotation.set(parseFloat(this.selectedObjectProps.rotX),parseFloat(this.selectedObjectProps.rotY),parseFloat(this.selectedObjectProps.rotZ));}},deleteBuilding() {if (this.selectedObject) {this.scene.remove(this.selectedObject);this.selectedObject = null;this.editorVisible = false;}},animate() {requestAnimationFrame(this.animate);this.renderer.render(this.scene, this.camera);this.controls.update();},exportModelData() {const modelData = {objects: this.scene.children.filter((obj) => obj instanceof THREE.Mesh) // 过滤出是 Mesh 对象的物体.map((obj) => ({position: obj.position.toArray(),scale: obj.scale.toArray(),rotation: obj.rotation.toArray(),color: `#${obj.material.color.getHexString()}`,})),};const jsonData = JSON.stringify(modelData);const blob = new Blob([jsonData], { type: "application/json" });const url = URL.createObjectURL(blob);const a = document.createElement("a");a.style.display = "none";a.href = url;a.download = "model_data.json";document.body.appendChild(a);a.click();URL.revokeObjectURL(url);document.body.removeChild(a);},importModelData(event) {const file = event.target.files[0];if (file) {const reader = new FileReader();reader.onload = () => {try {const data = JSON.parse(reader.result);console.log("Imported data:", data); // 输出导入的完整数据,确保格式和内容正确this.clearScene();data.objects.forEach((objData, index) => {const geometry = new THREE.BoxGeometry();// 设置默认颜色为红色const color = new THREE.Color(0xff0000); // 红色// 如果数据中有颜色字段并且是合法的颜色值,则使用数据中的颜色if (objData.color && typeof objData.color === "string") {try {color.set(objData.color);} catch (error) {console.error(`Error parsing color for object ${index}:`, error);}} else {console.warn(`Invalid color value for object ${index}:`, objData.color);}const material = new THREE.MeshStandardMaterial({color: color,metalness: 0.5, // 示例中的金属度设置为0.5,可以根据需求调整roughness: 0.8, // 示例中的粗糙度设置为0.8,可以根据需求调整});const object = new THREE.Mesh(geometry, material);object.position.fromArray(objData.position);object.scale.fromArray(objData.scale);object.rotation.fromArray(objData.rotation);this.scene.add(object);});} catch (error) {console.error("Error importing model data:", error);}};reader.readAsText(file);}},clearScene() {while (this.scene.children.length > 0) {this.scene.remove(this.scene.children[0]);}},saveModelData() {const modelData = {objects: this.scene.children.map((obj) => ({position: obj.position.toArray(),scale: obj.scale.toArray(),rotation: obj.rotation.toArray(),color: `#${obj.material.color.getHexString()}`,})),};localStorage.setItem("modelData", JSON.stringify(modelData));},loadModelData() {const savedData = localStorage.getItem("modelData");if (savedData) {try {const data = JSON.parse(savedData);this.clearScene();data.objects.forEach((objData) => {const geometry = new THREE.BoxGeometry();const material = new THREE.MeshStandardMaterial({color: parseInt(objData.color.replace("#", "0x"), 16),});const object = new THREE.Mesh(geometry, material);object.position.fromArray(objData.position);object.scale.fromArray(objData.scale);object.rotation.fromArray(objData.rotation);this.scene.add(object);});} catch (error) {console.error("Error loading model data from localStorage:", error);}}},},
};
</script><style>
body {margin: 0;overflow: hidden;
}canvas {display: block;
}#info {position: absolute;top: 10px;left: 10px;background: rgba(255, 255, 255, 0.8);padding: 10px;
}#editor {position: absolute;top: 100px;left: 10px;background: rgba(255, 255, 255, 0.8);padding: 10px;
}
</style>

总结

未完待续

相关文章:

vue vite+three在线编辑模型导入导出

文章目录 序一、1.0.0版本1.新增2.编辑3.导出4.导入 总结 序 要实现一个类似于数字孪生的场景 可以在线、新增、删除模型 、以及编辑模型的颜色、长宽高 然后还要实现 编辑完后 保存为json数据 记录模型数据 既可以导入也可以导出 一、1.0.0版本 1.新增 先拿建议的立方体来…...

去水印小程序源码修复版-前端后端内置接口+第三方接口

去水印小程序源码&#xff0c;前端后端&#xff0c;内置接口第三方接口&#xff0c; 修复数据库账号密码错误问题&#xff0c;内置接口支持替换第三方接口&#xff0c; 文件挺全的&#xff0c;可以添加流量主代码&#xff0c;搭建需要准备一台服务器&#xff0c;备案域名和http…...

机器学习:预测评估8类指标

机器学习&#xff1a;8类预测评估指标 R方值、平均值绝对误差值MAE、均方误差MSE、均方误差根EMSE、中位数绝对误差MAD、平均绝对百分误差MAPE、可解释方差分EVS、均方根对数误差MLSE。 一、R方值 1、说明&#xff1a; R方值&#xff0c;也称为确定系数或拟合优度&#xff…...

【深度学习基础】MAC pycharm 专业版安装与激活

文章目录 一、pycharm专业版安装二、激活 一、pycharm专业版安装 PyCharm是一款专为Python开发者设计的集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在帮助用户在使用Python语言开发时提高效率。以下是对PyCharm软件的详细介绍&#xff0c;包括其作用和主要功能&…...

排序相关算法--1.插入排序+冒泡排序回顾

1.基本分类 2.插入排序 特点&#xff1a;有实践意义&#xff08;例如后期快排的优化&#xff09;&#xff0c;适应性强&#xff0c;一般不会到时间复杂度最坏的情况。 将第一个元素视为已经排好序的序列。取出下一个元素&#xff0c;在已经排好序的序列中从后往前比较&#xf…...

变阻器的故障排除方法有哪些?

变阻器&#xff0c;特别是滑动变阻器&#xff0c;作为电子电路中的常见元件&#xff0c;其故障排除方法主要依据具体的故障现象来确定。以下是一些常见的故障现象及其排除方法&#xff1a; 一、接触不良 现象&#xff1a;电阻器不起作用或电压不稳定。 排除方法&#xff1a; …...

软考《信息系统运行管理员》-3.1信息系统设施运维的管理体系

3.1信息系统设施运维的管理体系 1 信息系统设施运维的对象 基础环境 主要包括信息系统运行环境(机房、设备间、配线室、基站、云计算中心 等)中的空调系统、供配电系统、通信应急设备系统、防护设备系统(如消防系统、安全系统) 等&#xff0c;能维持系统安全正常运转&#xf…...

Nginx重定向

Nginx重定向 location 匹配 location匹配的就是后面的URL /WordPress 192.168.118.10/wordpress location匹配的分类和优先级 1.精确匹配 location/对字符串进行完全匹配,必须完全符合2.正则匹配 ^~ 前缀匹配,以什么为开头~ 区分大小写的匹配~* 不区分大小写!~: 区分大小…...

私有化地图离线部署方案之高程检索服务

私有化地图离线部署整体解决方案&#xff0c;除硬件之外&#xff0c;一般主要由基础地图服务、查询定位服务、路径规划服务和高程检索服务构成。 我们已经分享过基础地图服务、查询定位服务和路径规划服务&#xff0c;现在再为你分享高程检索服务的方法。 私有化高程检索服务…...

PostgreSQL 中如何实现数据的增量更新和全量更新的平衡?

文章目录 一、增量更新与全量更新的概念增量更新全量更新 二、考虑的因素1. 数据量2. 数据更改的频率和规模3. 数据一致性要求4. 系统性能和资源利用5. 业务逻辑和流程 三、解决方案&#xff08;一&#xff09;混合使用增量更新和全量更新&#xff08;二&#xff09;使用临时表…...

数据结构--二叉树相关习题5(判断二叉树是否是完全二叉树 )

1.判断二叉树是否是完全二叉树 辨别&#xff1a; 不能使用递归或者算节点个数和高度来判断。 满二叉树可以用高度和节点来判断&#xff0c;因为是完整的。 但是完全二叉树前面是满的&#xff0c;但是最后一层是从左到右连续这种 如果仍然用这种方法的话&#xff0c;如下图…...

Python 轻松生成多种条形码、二维码 (Code 128、EAN-13、QR code等)

条形码和二维码是现代信息交换和数据存储的重要工具&#xff0c;它们将信息以图形的形式编码&#xff0c;便于机器识别和数据处理&#xff0c;被广泛应用于物流、零售、医疗、教育等各领域。 本文将介绍如何使用Python快速生成各种常见的条形码如Code 128、EAN-13&#xff0c;…...

Python: 分块读取文本文件

在处理大文件时&#xff0c;逐行或分块读取文件是很常见的需求。下面是几种常见的方法&#xff0c;用于在 Python 中分块读取文本文件&#xff1a; 1、问题背景 如何分块读取一个较大的文本文件&#xff0c;并提取出特定的信息&#xff1f; 问题描述: fopen(blank.txt,r) quot…...

服务攻防——中间件Jboss

文章目录 一、Jboss简介二、Jboss渗透2.1 JBoss 5.x/6.x 反序列化漏洞&#xff08;CVE-2017-12149&#xff09;2.2 JBoss JMXInvokerServlet 反序列化漏洞&#xff08;CVE-2015-7501&#xff09;2.3 JBossMQ JMS 反序列化漏洞&#xff08;CVE-2017-7504&#xff09;2.4 Adminis…...

宏碁F5-572G-59K3笔记本笔记本电脑拆机清灰教程(详解)

1. 前言 我的笔记本开机比较慢&#xff0c;没有固态&#xff0c;听说最近固态比较便宜&#xff0c;就想入手一个&#xff0c;于是拆笔记本看一下有没有可以安的装位置。&#xff08;友情提示&#xff0c;在拆机之前记得洗手并擦干&#xff0c;以防静电损坏电源器件&#xff09…...

基于FPGA的LDPC编译码算法设计基础知识

基于FPGA的LDPC编译码算法设计基础知识 数字电路&#xff08;数电&#xff09;知识模拟电路&#xff08;模电&#xff09;知识1. 放大器1.1. 晶体管放大器1.2. 运算放大器1.3. 管子放大器&#xff08;真空管放大器&#xff09;微处理器/单片机知识其他相关知识 基于FPGA的算法设…...

国际网课平台Udemy上的亚马逊云科技AWS免费高分课程和创建、维护EC2动手实践

亚马逊云科技(AWS)是全球云行业最&#x1f525;火的云平台&#xff0c;在全球经济形势不好的大背景下&#xff0c;通过网课学习亚马逊云科技AWS基础备考亚马逊云科技AWS证书&#xff0c;对于找工作或者无背景转行做AWS帮助巨大。欢迎大家关注小李哥&#xff0c;及时了解世界最前…...

空中交通新动能!2024深圳eVTOL展动力电池展区核心内容抢先看!

空中交通新动能&#xff01;2024深圳eVTOL展动力电池展区核心内容抢先看&#xff01; 关键词&#xff1a;2024深圳eVTOL展 动力电池 高能量密度电池 高性能电池材料 作为2024深圳eVTOL展重要组成部分&#xff0c;2024深圳eVTOL动力电池展将于9月23-25日在深圳坪山燕子湖国际会…...

代码江湖:Python 中的进程与线程

大家好&#xff0c;我是阔升。今天&#xff0c;咱们来聊聊 Python 中的两个"老熟人"——进程和线程。这两个概念可以说是 Python 多任务编程中的"双子星"&#xff0c;既相似又不同&#xff0c;让不少小伙伴们头疼不已。不过别担心&#xff0c;今天我们就来…...

根据H在有限域GF(2^m)上求解生成矩阵G

原理 有时间再补充。 注1&#xff1a;使用高斯消去法。如果Py不为单位阵&#xff0c;则说明进行了列置换&#xff0c;此时G不是系统形式。 注2&#xff1a;校验矩阵H必须是行满秩才存在对应的生成矩阵G&#xff0c;且生成矩阵G通常不唯一。 matlab实现&#xff1a;只做列置…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...