Three.js实战项目-智慧楼宇
Three.js实战项目-智慧楼宇
概述
如有不明白的可以加QQ:2354528292;wx: aichitudousien
更多教学视频请访问:https://space.bilibili.com/236087412
源码获取:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.3c3a645ezznpcA&ft=t&id=714973095604
先看看视频效果
Three.js 建筑可视化监控
分别具备楼层展示,楼层热力度分析,扩散波特效,无人机飞行,飞行路径点规划,扩散波特效,多场景下钻,多文本渲染,路径动画
初始化场景
这里和以前文章的加载场景一样,没有变化
app = new ZThree('screen');
app.initThree();
// app.initHelper();
app.initOrbitControls();
light = app.initLight();
stats = app.initStatus();
selectObj = app.initRaycaster();
window.app = app;
camera = app.camera;
bloomComposer = app.bloomComposer();
camera.position.set(...this.cameraPosition);
scene = app.scene;
renderer = app.renderer;
renderer.logarithmicDepthBuffer = true;
renderer.autoClear = false;
controls = app.controls;
controls.target.set(...this.target);
controls.maxDistance = 2000;
controls.maxPolarAngle = Math.PI / 2.2;
clock = new THREE.Clock();
加载建筑数据和道路数据
import axios from "axios";
import * as THREE from 'three'
import {
getDistance,
getRhumbLineBearing
} from "geolib";
import {
BufferGeometryUtils
} from "three/examples/jsm/utils/BufferGeometryUtils.js";
// 中心点数组
const center = [114.0504053235054, 22.532816043569957];
// 初始化创建组
let iR = new THREE.Group();
iR.scale.set(20, 20, 20);
iR.name = "建组";
let iR_Road = new THREE.Group();
iR_Road.name = "公路";
iR_Road.scale.set(20, 20, 20);
let iR_Line = new THREE.Group();
iR_Line.name = "公路动画线";
iR_Line.scale.set(20, 20, 20);
// 包围盒数组对象
let collider_building = [];
// 线条数组对象
let linelist = [];
// 建组数组对象
let geos_building = [];
let MAT_BUILDING;
// 线材质
let MAT_ROAD = new THREE.LineBasicMaterial({
color: 0x00ff00,
linewidth: 10
});
let MAT_ROAD_HIGEWAY;
/**
* 获得地图数据
*/
export function createBuildingRoad(app, buildingMaterials, lineMaterial) {
return new Promise(resolve => {
axios({
url: "/json/export.json"
}).then(res => {
MAT_BUILDING = buildingMaterials;
MAT_ROAD_HIGEWAY = lineMaterial;
LoadBuildings(res.data, app);
app.scene.add(iR);
app.scene.add(iR_Road);
app.scene.add(iR_Line);
resolve(true)
});
})
}
/**
* 加载地图模型
*/
function LoadBuildings(data, app) {
let features = data.features;
for (let i = 0; i < features.length; i++) {
let fel = features[i];
if (!fel["properties"]) return;
let info = fel.properties;
// building 建筑 highway 公路
if (info["building"]) {
addBuilding(fel.geometry.coordinates, info, info["height"]);
} else if (info["highway"]) {
if (fel.geometry.type == "LineString") {
// if (
// info["highway"] === "trunk_link" ||
// info["highway"] === "motorway"
// ) {
// addRoadTrunk(fel.geometry.coordinates, info);
// } else {
// // addRoad(fel.geometry.coordinates, info);
// }
addRoadTrunk(fel.geometry.coordinates, info);
}
}
}
linelist.forEach((e, i) => {
const line2 = app.createAnimateLine({
type: "pipe",
pointList: e,
material: MAT_ROAD_HIGEWAY,
number: 100,
radius: 0.01
});
line2.rotateZ(-Math.PI);
iR_Road.add(line2);
});
let mergeGeometry = BufferGeometryUtils.mergeBufferGeometries(
geos_building
);
let mesh = new THREE.Mesh(mergeGeometry, MAT_BUILDING);
iR.add(mesh);
}
/**
* 添加建筑
* @param {Array} data 坐标数组
* @param {Object} info 建筑信息
* @param {Number} height 建组高度
*/
function addBuilding(data, info, height = 1) {
height = height ? height : 1;
let shape, geometry;
// 建筑物的孔
let holes = [];
for (let i = 0; i < data.length; i++) {
let el = data[i];
if (i == 0) {
// 建筑路劲
shape = genShape(el, center);
} else {
// 当前孔的路径
holes.push(genShape(el, center));
}
}
// 将孔添加到当前建组中
for (let i = 0; i < holes.length; i++) {
shape.holes.push(holes[i]);
}
if (height > 10) {
height = 0.1 * height
} else {
height = 8 * height
}
geometry = genGeometry(shape, {
curveSegments: 1,
// depth: height,
depth: 0.1 * height,
bevelEnabled: false
});
geometry.rotateX(Math.PI / 2);
geometry.rotateZ(Math.PI);
geos_building.push(geometry);
let helper = genHelper(geometry);
if (helper) {
helper.name = info["name"] ? info["name"] : "Building";
helper.info = info;
collider_building.push(helper);
}
}
/**
* 添加公路
* @param {Array} d 坐标数组
* @param {Object} info 建筑信息
*/
function addRoadTrunk(d, info) {
// 点数组
let points = [];
for (let i = 0; i < d.length; i++) {
if (!d[0][1]) return;
let el = d[i];
if (!el[0] || !el[1]) return;
let elp = [el[0], el[1]];
// 位置转换
elp = GPSRelativePosition([elp[0], elp[1]], center);
points.push([elp[0], 0, elp[1]]);
}
linelist.push(points);
}
/**
* 添加公路
* @param {Array} d 坐标数组
* @param {Object} info 建筑信息
*/
function addRoad(d, info) {
// 点数组
let points = [];
for (let i = 0; i < d.length; i++) {
if (!d[0][1]) return;
let el = d[i];
if (!el[0] || !el[1]) return;
let elp = [el[0], el[1]];
// 位置转换
elp = GPSRelativePosition([elp[0], elp[1]], center);
points.push(new THREE.Vector3(elp[0], 0.5, elp[1]));
}
// 通过点队列设置该 BufferGeometry 的 attribute
let geometry = new THREE.BufferGeometry().setFromPoints(points);
geometry.rotateZ(Math.PI);
let line = new THREE.Line(geometry, MAT_ROAD);
line.info = info;
// 计算LineDashedMaterial所需的距离的值的数组
line.computeLineDistances();
iR_Road.add(line);
line.position.set(line.position.x, 0, line.position.z);
}
/**
* 添加建筑
* @param {Array} points 坐标数组
* @param {Array} center 地理位置中心点
* @return {Object}
*/
function genShape(points, center) {
let shape = new THREE.Shape();
for (let i = 0; i < points.length; i++) {
let elp = points[i];
elp = GPSRelativePosition(elp, center);
if (i == 0) {
shape.moveTo(elp[0], elp[1]);
} else {
shape.lineTo(elp[0], elp[1]);
}
}
return shape;
}
/**
* 生成Geometry
* @param {Object} shape shape对象
* @param {Object} settings 建筑配置项
* @return {Object} 建组对象
*/
function genGeometry(shape, settings) {
// 挤压缓冲几何体
let geometry = new THREE.ExtrudeBufferGeometry(shape, settings);
geometry.computeBoundingBox();
return geometry;
}
/**
* 建组包围盒子
* @param {Object} geometry
*/
function genHelper(geometry) {
if (!geometry.boundingBox) {
geometry.computeBoundingBox();
}
let box3 = geometry.boundingBox;
// 检测包围盒是不是无穷大
if (!isFinite(box3.max.x)) {
return false;
}
let helper = new THREE.Box3Helper(box3, 0xffff00);
helper.updateMatrixWorld();
return helper;
}
/**
* GPS经纬度转化
* @param {Array} objPosi 坐标数组
* @param {Array} centerPosi 中心点
* @return {Array} 转化后的数组
*/
function GPSRelativePosition(objPosi, centerPosi) {
let dis = getDistance(objPosi, centerPosi);
// Get bearing angle
let bearing = getRhumbLineBearing(objPosi, centerPosi);
// Calculate X by centerPosi.x + distance * cos(rad)
let x = centerPosi[0] + dis * Math.cos((bearing * Math.PI) / 180);
// Calculate Y by centerPosi.y + distance * sin(rad)
let y = centerPosi[1] + dis * Math.sin((bearing * Math.PI) / 180);
// Reverse X (it work)
return [-x / 100, y / 100];
}
道路和建筑数据在开源地图OpenStreetMap上获取即可,可获取道路,建筑,水系等多种数据,虽然数据比较简陋,不过拿来做demo数据还是可以的
扩散波
cylinder = loaderCylinder(app);
export function loaderCylinder(app) {
let texture = new THREE.TextureLoader().load("images/zhu.png")
texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //每个都重复
texture.repeat.set(1, 1)
texture.needsUpdate = true
let geometry = new THREE.CylinderGeometry(8, 8, 4, 64);
let materials = [
new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
transparent: true
}),
new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
side: THREE.DoubleSide
}),
new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
side: THREE.DoubleSide
})
]
let mesh = new THREE.Mesh(geometry, materials)
app.scene.add(mesh)
return mesh;
}
分类:
Three
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)