当前位置: 首页 > 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;只做列置…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

【R语言编程——数据调用】

这里写自定义目录标题 可用库及数据集外部数据导入方法查看数据集信息 在R语言中&#xff0c;有多个库支持调用内置数据集或外部数据&#xff0c;包括studentdata等教学或示例数据集。以下是常见的库和方法&#xff1a; 可用库及数据集 openintro库 该库包含多个教学数据集&a…...

Springboot多数据源配置实践

Springboot多数据源配置实践 基本配置文件数据库配置Mapper包Model包Service包中业务代码Mapper XML文件在某些复杂的业务场景中,我们可能需要使用多个数据库来存储和管理不同类型的数据,而不是仅仅依赖于单一数据库。本技术文档将详细介绍如何在 Spring Boot 项目中进行多数…...