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

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

文章目录

  • 一、1.0.0版本
    • 1.新增
    • 2.编辑
    • 3.导出
    • 4.导入
  • 二、2.0.0版本
    • 1. 修复模型垂直方向放置时 模型会重合
    • 4. 修复了导出导入功能 现在是1:1导出导入
    • 5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形
    • 6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自己的模型文件
    • 7. 优化面板样式
  • 总结

要实现一个类似于数字孪生的场景 可以在线、新增、删除模型 、以及编辑模型的颜色、长宽高
然后还要实现 编辑完后 保存为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>

二、2.0.0版本

在这里插入图片描述

1. 修复模型垂直方向放置时 模型会重合

4. 修复了导出导入功能 现在是1:1导出导入

5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形

6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自己的模型文件

7. 优化面板样式

<template><div id="app" @click="onAppClick"><div id="info"><button @click.stop="toggleBuildingMode">{{ buildingMode ? "关闭建造模式" : "开启建造模式" }}</button><button @click.stop="showEditor">编辑所选模型</button><button @click.stop="exportModelData">导出模型数据</button><input type="file" @change="importModelData" ref="fileInput" /><input type="file" @change="importCustomModel" ref="customModelInput" /><label for="modelType">模型类型:</label><select v-model="selectedModelType"><option value="box">立方体</option><option value="sphere">球体</option><option value="cylinder">圆柱体</option><option value="custom">自定义模型</option></select></div><div id="editor" v-if="editorVisible" @click.stop><h3>编辑模型</h3><div class="form-group"><label for="color">颜色:</label><input type="color" id="color" v-model="selectedObjectProps.color" /><br /></div><div class="form-group"><label for="posX">位置 X:</label><input type="number" id="posX" v-model="selectedObjectProps.posX" step="0.1" /><br /></div><div class="form-group"><label for="posY">位置 Y:</label><input type="number" id="posY" v-model="selectedObjectProps.posY" step="0.1" /><br /></div><div class="form-group"><label for="posZ">位置 Z:</label><input type="number" id="posZ" v-model="selectedObjectProps.posZ" step="0.1" /><br /></div><div class="form-group"><label for="scaleX">缩放 X:</label><input type="number" id="scaleX" v-model="selectedObjectProps.scaleX" step="0.1" /><br /></div><div class="form-group"><label for="scaleY">缩放 Y:</label><input type="number" id="scaleY" v-model="selectedObjectProps.scaleY" step="0.1" /><br /></div><div class="form-group"><label for="scaleZ">缩放 Z:</label><input type="number" id="scaleZ" v-model="selectedObjectProps.scaleZ" step="0.1" /><br /></div><div class="form-group"><label for="rotX">旋转 X:</label><input type="number" id="rotX" v-model="selectedObjectProps.rotX" step="0.1" /><br /></div><div class="form-group"><label for="rotY">旋转 Y:</label><input type="number" id="rotY" v-model="selectedObjectProps.rotY" step="0.1" /><br /></div><div class="form-group"><label for="rotZ">旋转 Z:</label><input type="number" id="rotZ" v-model="selectedObjectProps.rotZ" step="0.1" /><br /></div><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";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";export default {data() {return {editorVisible: false,selectedObject: null,selectedObjectProps: {color: "#000",posX: 0,posY: 0,posZ: 0,scaleX: 1,scaleY: 1,scaleZ: 1,rotX: 0,rotY: 0,rotZ: 0,},raycaster: null,buildingMode: false,selectedModelType: "box",customModel: null,};},mounted() {this.init();this.animate();window.addEventListener("resize", this.onWindowResize, false);},methods: {animate() {requestAnimationFrame(this.animate);this.renderer.render(this.scene, this.camera);this.controls.update();},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);this.controls.minDistance = 10;this.controls.maxDistance = 50;this.controls.maxPolarAngle = Math.PI / 2;const planeGeometry = new THREE.CircleGeometry(100, 32);const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });const plane = new THREE.Mesh(planeGeometry, planeMaterial);plane.rotation.x = -Math.PI / 2;plane.userData.isGround = true;this.scene.add(plane);const light = new THREE.DirectionalLight(0xffffff, 1);light.position.set(5, 10, 7.5);this.scene.add(light);this.raycaster = new THREE.Raycaster();},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 (this.buildingMode && intersects.length > 0) {const intersect = intersects[0];const point = intersect.point;if (intersect.object.userData.isGround) {if (this.isOverlapping(point.x, point.z)) {this.stackBuilding(point.x, point.z);} else {this.addBuilding(point.x, 0, point.z);}} else {const stackHeight = intersect.object.position.y + intersect.object.scale.y;this.addBuilding(intersect.object.position.x, stackHeight, intersect.object.position.z);}} else if (intersects.length > 0) {this.selectedObject = intersects[0].object;console.log("Object selected:", this.selectedObject);this.showEditor();}},isOverlapping(x, z) {const threshold = 1;for (let obj of this.scene.children) {if (Math.abs(obj.position.x - x) < threshold &&Math.abs(obj.position.z - z) < threshold &&!obj.userData.isGround) {return true;}}return false;},stackBuilding(x, z) {let maxY = 0;this.scene.children.forEach((obj) => {if (Math.abs(obj.position.x - x) < 1 &&Math.abs(obj.position.z - z) < 1 &&!obj.userData.isGround &&obj.position.y + obj.scale.y > maxY) {maxY = obj.position.y + obj.scale.y;}});this.addBuilding(x, maxY, z);},addBuilding(x, y, z) {let geometry;switch (this.selectedModelType) {case "sphere":geometry = new THREE.SphereGeometry(0.5, 32, 32);break;case "cylinder":geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);break;case "custom":if (this.customModel) {this.loadCustomModel(x, y, z);return;}break;case "box":default:geometry = new THREE.BoxGeometry(1, 1, 1);break;}if (geometry) {const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });const building = new THREE.Mesh(geometry, material);building.position.set(x, y, z);this.scene.add(building);}},loadCustomModel(x, y, z) {const loader = new GLTFLoader();loader.load(this.customModel,(gltf) => {const object = gltf.scene;object.position.set(x, y, z);this.scene.add(object);},undefined,(error) => {console.error("An error happened while loading the custom model", error);});},importCustomModel(event) {const file = event.target.files[0];this.customModel = URL.createObjectURL(file);},showEditor() {if (this.selectedObject) {this.selectedObjectProps.color = "#" + this.selectedObject.material.color.getHexString();this.selectedObjectProps.posX = this.selectedObject.position.x;this.selectedObjectProps.posY = this.selectedObject.position.y;this.selectedObjectProps.posZ = this.selectedObject.position.z;this.selectedObjectProps.scaleX = this.selectedObject.scale.x;this.selectedObjectProps.scaleY = this.selectedObject.scale.y;this.selectedObjectProps.scaleZ = this.selectedObject.scale.z;this.selectedObjectProps.rotX = this.selectedObject.rotation.x;this.selectedObjectProps.rotY = this.selectedObject.rotation.y;this.selectedObjectProps.rotZ = this.selectedObject.rotation.z;}this.editorVisible = true;},applyEdit() {if (this.selectedObject) {this.selectedObject.material.color.set(this.selectedObjectProps.color);this.selectedObject.position.set(this.selectedObjectProps.posX,this.selectedObjectProps.posY,this.selectedObjectProps.posZ);this.selectedObject.scale.set(this.selectedObjectProps.scaleX,this.selectedObjectProps.scaleY,this.selectedObjectProps.scaleZ);this.selectedObject.rotation.set(this.selectedObjectProps.rotX,this.selectedObjectProps.rotY,this.selectedObjectProps.rotZ);}this.editorVisible = false;},deleteBuilding() {if (this.selectedObject) {this.scene.remove(this.selectedObject);this.selectedObject.geometry.dispose();this.selectedObject.material.dispose();this.selectedObject = null;this.editorVisible = false;}},toggleBuildingMode() {this.buildingMode = !this.buildingMode;},exportModelData() {const modelData = this.scene.children.filter((obj) => obj.type === "Mesh" && !obj.userData.isGround).map((obj) => ({type: obj.geometry.type,position: obj.position,rotation: obj.rotation,scale: obj.scale,color: obj.material.color.getHex(),}));const blob = new Blob([JSON.stringify(modelData)], { type: "application/json" });const link = document.createElement("a");link.href = URL.createObjectURL(blob);link.download = "modelData.json";link.click();},importModelData(event) {const file = event.target.files[0];const reader = new FileReader();reader.onload = (e) => {const modelData = JSON.parse(e.target.result);this.loadModelData(modelData);};reader.readAsText(file);},loadModelData(modelData = null) {if (!modelData) {return;}modelData.forEach((data) => {let geometry;switch (data.type) {case "SphereGeometry":geometry = new THREE.SphereGeometry(0.5, 32, 32);break;case "CylinderGeometry":geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);break;case "BoxGeometry":default:geometry = new THREE.BoxGeometry(1, 1, 1);break;}const material = new THREE.MeshStandardMaterial({ color: data.color });const object = new THREE.Mesh(geometry, material);object.position.copy(data.position);object.rotation.copy(data.rotation);object.scale.copy(data.scale);this.scene.add(object);});},},
};
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialias;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;
}#info {position: absolute;top: 10px;left: 10px;background: rgba(255, 255, 255, 0.8);padding: 10px;border-radius: 5px;
}#editor {position: absolute;top: 50px;right: 10px;background: rgba(255, 255, 255, 0.9);padding: 10px;border-radius: 5px;z-index: 1000;width: 200px;
}#editor .form-group {margin-bottom: 10px;
}#editor label {display: block;margin-bottom: 5px;
}#editor input {width: 100%;
}
</style>

总结

未完待续

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Emacs有什么优点,用Emacs写程序真的比IDE更方便吗?
  • S5730 OSPF: 配置 OSPF 进程和区域
  • 硬盘模式vmd怎么改ahci_电脑vmd改ahci模式详细步骤
  • Visual Studio编译优化选项
  • PPTP、L2TP、IPSec、IPS 有什么区别?
  • 星网安全产品线成立 引领卫星互联网解决方案创新
  • 美团到家平台业务探索
  • [终端安全]-8 隐私保护和隐私计算技术
  • Apache Seata Mac下的Seata Demo环境搭建
  • 华为如何做成数字化转型?
  • 设计模式使用场景实现示例及优缺点(结构型模式——享元模式)
  • 如何成为互联网数据淘金者?爬虫管理平台助您一臂之力
  • 【088】基于SpringBoot+Vue实现高校就业管理系统
  • Linux 忘记root密码,通过单用户模式修改
  • 无人机之电池保养
  • @angular/forms 源码解析之双向绑定
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Android单元测试 - 几个重要问题
  • Create React App 使用
  • css属性的继承、初识值、计算值、当前值、应用值
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JavaScript设计模式系列一:工厂模式
  • Shell编程
  • vue脚手架vue-cli
  • 从伪并行的 Python 多线程说起
  • 给初学者:JavaScript 中数组操作注意点
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 使用putty远程连接linux
  • 数组的操作
  • 用quicker-worker.js轻松跑一个大数据遍历
  • kubernetes资源对象--ingress
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • 浅谈sql中的in与not in,exists与not exists的区别
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • (done) 两个矩阵 “相似” 是什么意思?
  • (Oracle)SQL优化技巧(一):分页查询
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (二)c52学习之旅-简单了解单片机
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (九)信息融合方式简介
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (全注解开发)学习Spring-MVC的第三天
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)mysql使用Navicat 导出和导入数据库
  • 、写入Shellcode到注册表上线
  • .Net CF下精确的计时器