Scene.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. import * as THREE from "three";
  2. import Stats from "three/examples/jsm/libs/stats.module.js";
  3. import Player from "./player/Player.js";
  4. import BoxManager from "./box/BoxManager.js";
  5. import { Mitt } from "./mitt.js";
  6. import testData from "./save.json";
  7. import { dataURItoBlob, saveFile } from "./utils/utils.js";
  8. import { screenshotObject } from "./utils/cap.js";
  9. const stats = new Stats();
  10. function sleep(ms) {
  11. return new Promise((resolve) => setTimeout(resolve, ms));
  12. }
  13. export default class Scene extends Mitt {
  14. constructor(domElement) {
  15. super();
  16. this.domElement = domElement;
  17. this.scene = null;
  18. this.renderer = null;
  19. this.orthCamera = null;
  20. this.player = null;
  21. this.sceneType = 1;
  22. this.width = 0;
  23. this.height = 0;
  24. this.defaultZoom = 250;
  25. this.defaultUseZoom = 250 / window.devicePixelRatio;
  26. this.initCamPView = new THREE.Vector3();
  27. this.initCamRView = new THREE.Vector3();
  28. this.blobScreens = [];
  29. this.inited = false;
  30. this.init = () => {
  31. this.scene = new THREE.Scene();
  32. this.scene.background = new THREE.Color(0xf0f2f5);
  33. this.renderer = new THREE.WebGLRenderer({
  34. canvas: this.domElement,
  35. antialias: true,
  36. autoClear: true,
  37. preserveDrawingBuffer: true,
  38. });
  39. this.width = this.domElement.clientWidth;
  40. this.height = this.domElement.clientHeight;
  41. this.renderRes = window.devicePixelRatio;
  42. this.defaultUseZoom = this.defaultZoom / window.devicePixelRatio;
  43. this.renderer.setSize(this.width, this.height);
  44. this.renderer.setPixelRatio(this.renderRes);
  45. console.log(
  46. "init",
  47. this.width,
  48. this.height,
  49. this.renderRes,
  50. this.defaultZoom
  51. );
  52. this.orthCamera = new THREE.OrthographicCamera(
  53. -this.width / 2,
  54. this.width / 2,
  55. this.height / 2,
  56. -this.height / 2,
  57. 0.1,
  58. 1000
  59. );
  60. this.orthCamera.zoom = this.defaultUseZoom;
  61. // 影响画线
  62. this.orthCamera.position.set(0, 10, 0);
  63. this.orthCamera.lookAt(0, 0, 0);
  64. // this.orthCamera.setViewOffset(this.width, this.height, 0, 0);
  65. this.orthCamera.updateProjectionMatrix();
  66. //player
  67. this.player = new Player(this);
  68. domElement.parentNode.appendChild(stats.dom);
  69. stats.dom.style.pointerEvents = "none";
  70. stats.dom.style.left = "15%";
  71. stats.dom.style.display = "none";
  72. this.onBindEvent();
  73. this.inited = true;
  74. this.load();
  75. this.animate();
  76. };
  77. }
  78. load = (list, type, data) => {
  79. if (!list) return;
  80. // console.log("scene: ", list, type, data);
  81. //axesHeloer
  82. this.clearScene();
  83. this.sceneType = type;
  84. // const axesHelper = new THREE.AxesHelper(1);
  85. // this.scene.add(axesHelper);
  86. this.boxManager = new BoxManager(this);
  87. this.boxManager.load(list, type);
  88. //light
  89. this.loadLight();
  90. this.player.load(type, data || []);
  91. this.initCamPView.copy(this.orthCamera.position);
  92. this.initCamRView.copy(this.orthCamera.rotation);
  93. };
  94. clearScene() {
  95. for (var i = this.scene.children.length - 1; i >= 0; i--) {
  96. let obj = this.scene.children[i];
  97. this.scene.remove(obj);
  98. }
  99. }
  100. clearDrawScene() {
  101. for (var i = this.scene.children.length - 1; i >= 0; i--) {
  102. let obj = this.scene.children[i];
  103. if (
  104. String(obj.name).includes("marker_") ||
  105. String(obj.name).includes("line_") ||
  106. String(obj.name).includes("line_point_") ||
  107. String(obj.name).includes("circle_") ||
  108. String(obj.name).includes("pureText_")
  109. ) {
  110. this.scene.remove(obj);
  111. }
  112. }
  113. }
  114. deleteItemById(uuid, type) {
  115. for (var i = this.scene.children.length - 1; i >= 0; i--) {
  116. let obj = this.scene.children[i];
  117. if (obj.uuid === uuid) {
  118. console.log("deleteItemById-userData", obj.userData);
  119. this.player.deleteItemByType(type, obj.userData);
  120. this.scene.remove(obj);
  121. }
  122. }
  123. }
  124. deleteImageDataByIds(ids) {
  125. this.player.deleteImageDataByIds(ids);
  126. }
  127. loadLight = () => {
  128. const light = new THREE.AmbientLight(0xffffff, 1.5); // 柔和的白光
  129. this.scene.add(light);
  130. };
  131. setCamera = () => {};
  132. toHorizontal = () => {};
  133. toVertical = () => {};
  134. lockView(open) {
  135. if (!open) {
  136. this.player.floorplanControls.enablePan = true;
  137. this.player.floorplanControls.enableZoom = true;
  138. } else {
  139. this.player.floorplanControls.enablePan = false;
  140. this.player.floorplanControls.enableZoom = false;
  141. }
  142. }
  143. setMode(mode) {
  144. this.player.setMode(mode);
  145. }
  146. onResize = (rWidth, rHeight) => {
  147. this.width =
  148. typeof rWidth === "number" ? rWidth : this.domElement.clientWidth;
  149. this.height =
  150. typeof rHeight === "height" ? rHeight : this.domElement.clientHeight;
  151. const container = document.querySelector(".right");
  152. const { width, height } = container.getBoundingClientRect();
  153. if (width !== this.domElement.clientWidth) {
  154. this.width = width;
  155. }
  156. if (height !== this.domElement.clientHeight) {
  157. this.height = height;
  158. }
  159. console.warn("onResize", this.width, this.height);
  160. (this.orthCamera.left = -this.width / 2),
  161. (this.orthCamera.right = this.width / 2),
  162. (this.orthCamera.bottom = -this.height / 2),
  163. (this.orthCamera.top = this.height / 2),
  164. this.orthCamera.updateProjectionMatrix();
  165. this.renderer.setSize(this.width, this.height);
  166. this.renderRes = window.devicePixelRatio;
  167. this.defaultUseZoom = this.defaultZoom / this.renderRes;
  168. this.renderer.setPixelRatio(this.renderRes);
  169. };
  170. render = () => {
  171. if (this.player) {
  172. this.player.update();
  173. this.renderer.render(this.scene, this.orthCamera);
  174. }
  175. };
  176. animate = () => {
  177. stats.begin();
  178. this.render();
  179. stats.end();
  180. requestAnimationFrame(this.animate);
  181. };
  182. resetCameraView() {
  183. this.orthCamera.zoom = this.defaultUseZoom;
  184. // this.orthCamera.position.set(0, 0, 0);
  185. // this.orthCamera.rotation.set(0, 10, 0);
  186. // this.orthCamera.updateMatrixWorld();
  187. }
  188. editing(item) {
  189. this.player.editing(item);
  190. }
  191. endScreenshot() {
  192. this.lockView(false);
  193. this.blobScreens = [];
  194. this.scene.position.x = 0;
  195. this.player.floorplanControls.reset();
  196. this.onResize(this.width, this.height);
  197. }
  198. screenshot(x, zoom, index) {
  199. var imgData, imgNode;
  200. const times = 4;
  201. this.orthCamera.zoom = zoom || this.defaultUseZoom;
  202. this.scene.position.x = x || 0;
  203. this.renderer.setSize(this.width * times, this.height * times);
  204. this.renderer.setPixelRatio(this.renderRes);
  205. this.orthCamera.aspect = this.width / this.height;
  206. this.orthCamera.updateProjectionMatrix();
  207. this.renderer.render(this.scene, this.orthCamera, null, false);
  208. const dataURL = this.renderer.domElement.toDataURL("image/jpeg");
  209. this.blobScreens.push(dataURItoBlob(dataURL));
  210. console.log(this.width, this.height);
  211. if (typeof index === "number") {
  212. saveFile(dataURL, `${index}.jpg`);
  213. }
  214. this.renderer.setSize(this.width, this.height);
  215. }
  216. exportScreenshot() {
  217. if (window.devicePixelRatio !== 1) {
  218. this.emit("devicePixelRatio");
  219. return;
  220. }
  221. this.onResize();
  222. this.player.floorplanControls.reset();
  223. this.lockView(true);
  224. this.setMode(0);
  225. // await sleep(500);
  226. const object = this.boxManager.model;
  227. const total = object.children.length;
  228. if (total === 0) {
  229. return;
  230. console.error("没数据");
  231. }
  232. object.updateMatrixWorld();
  233. this.orthCamera.updateProjectionMatrix();
  234. const boundingBox = new THREE.Box3().setFromObject(object);
  235. // 计算宽度、高度和深度
  236. const width = boundingBox.max.x - boundingBox.min.x;
  237. const one = width / total;
  238. let slides = Math.floor(total / 3);
  239. console.log("slides", slides);
  240. if (slides >= 1) {
  241. for (var i = 0; i <= slides; i++) {
  242. (function (index, that) {
  243. setTimeout(function () {
  244. const offset = -(one * 3 * index);
  245. console.log("Iteration:", one, index, offset, that.defaultUseZoom);
  246. that.screenshot(offset, that.defaultUseZoom);
  247. console.log(`Width: ${offset}`);
  248. if (index === slides) {
  249. console.log("last");
  250. that.emit("submitScreenshot");
  251. }
  252. }, index * 500);
  253. })(i, this); // 传递当前迭代的索引i给setTimeout的回调函数
  254. }
  255. } else {
  256. // 只有一个或0个
  257. if (total >= 1 && total <= 3) {
  258. console.log("total", total);
  259. this.player.floorplanControls.reset();
  260. this.screenshot(-0.3, 227);
  261. this.emit("submitScreenshot");
  262. }
  263. }
  264. }
  265. test() {
  266. const object = this.boxManager.model;
  267. const boundingBox = new THREE.Box3().setFromObject(object);
  268. this.lockView(true);
  269. // let size = new THREE.Vector3();
  270. // boundingBox.getSize(size);
  271. const width = boundingBox.max.x - boundingBox.min.x;
  272. const height = boundingBox.max.y - boundingBox.min.y;
  273. const z = boundingBox.max.z - boundingBox.min.z;
  274. const radio = width / height;
  275. // const dynamicWidth = this.height * radio;
  276. // (this.orthCamera.left = -dynamicWidth / 2),
  277. // (this.orthCamera.right = dynamicWidth / 2),
  278. // (this.orthCamera.bottom = -this.height / 2),
  279. // (this.orthCamera.top = this.height / 2),
  280. // this.orthCamera.updateProjectionMatrix();
  281. // this.renderer.setSize(dynamicWidth, this.height);
  282. // this.renderRes = window.devicePixelRatio;
  283. // this.defaultUseZoom = this.defaultZoom / this.renderRes;
  284. // this.renderer.setPixelRatio(this.renderRes);
  285. // console.log("dynamicWidth", dynamicWidth);
  286. // console.log("height", height);
  287. }
  288. center() {
  289. const center = new THREE.Vector3();
  290. const boundingBox = new THREE.Box3().setFromObject(this.boxManager.model);
  291. boundingBox.getCenter(center);
  292. const helper = new THREE.BoxHelper();
  293. helper.setFromObject(this.boxManager.model);
  294. helper.material.color.set(0xff0000);
  295. console.log("obj", helper, center);
  296. // const box = new THREE.BoxHelper(object, 0xffff00);
  297. this.scene.add(helper);
  298. this.boxManager.model.position.x -= center.x;
  299. }
  300. onBindEvent = () => {
  301. window.addEventListener("resize", this.onResize, false);
  302. };
  303. }