Finish explode tool
This commit is contained in:
parent
f998ecd519
commit
3e78657aef
5 changed files with 149 additions and 51 deletions
|
@ -22,24 +22,29 @@ function Toggle({
|
||||||
title,
|
title,
|
||||||
onChange,
|
onChange,
|
||||||
defaultChecked,
|
defaultChecked,
|
||||||
|
disabled = false,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
defaultChecked?: boolean;
|
defaultChecked?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<label className="inline-flex items-center cursor-pointer">
|
<label className="group">
|
||||||
|
<div className="inline-flex items-center cursor-pointer group-has-[input:disabled]:cursor-default">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value=""
|
value=""
|
||||||
className="sr-only peer"
|
className="sr-only peer"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
defaultChecked={defaultChecked ? true : false}
|
defaultChecked={defaultChecked ? true : false}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-gray-300 rounded-full peer dark:bg-gray-400 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-400 peer-checked:bg-blue-600 dark:peer-checked:bg-blue-400"></div>
|
<div className="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-gray-300 rounded-full peer dark:bg-gray-400 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-400 peer-checked:bg-blue-600 dark:peer-checked:bg-blue-400"></div>
|
||||||
<span className="ms-3 text-xs xl:text-sm font-medium text-gray-500 dark:text-gray-400">
|
<span className="ms-3 text-xs xl:text-sm font-medium text-gray-500 dark:text-gray-400 group-has-[input:disabled]:text-gray-200">
|
||||||
{title}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +138,7 @@ export function Form() {
|
||||||
const accordionRef0 = useRef<AccordionRef>(null);
|
const accordionRef0 = useRef<AccordionRef>(null);
|
||||||
|
|
||||||
const [emptyProfile, setEmptyProfile] = useState<boolean>(false);
|
const [emptyProfile, setEmptyProfile] = useState<boolean>(false);
|
||||||
|
const [exploded, setExploded] = useState<boolean>(false);
|
||||||
const { sceneView } = useContext(SceneViewContext) as SceneViewContextType;
|
const { sceneView } = useContext(SceneViewContext) as SceneViewContextType;
|
||||||
|
|
||||||
function handleChange() {
|
function handleChange() {
|
||||||
|
@ -207,6 +213,18 @@ export function Form() {
|
||||||
sceneView.exportOBJ();
|
sceneView.exportOBJ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleExplode(e: ChangeEvent<HTMLInputElement>) {
|
||||||
|
if (!sceneView) return;
|
||||||
|
|
||||||
|
if (e.target.checked) {
|
||||||
|
sceneView.explode(true);
|
||||||
|
// setExploded(true);
|
||||||
|
} else {
|
||||||
|
sceneView.explode(false);
|
||||||
|
// setExploded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-h-full min-h-0 flex flex-col gap-2 dark:bg-gray-700">
|
<div className="w-full max-h-full min-h-0 flex flex-col gap-2 dark:bg-gray-700">
|
||||||
<div className="w-full flex justify-end">
|
<div className="w-full flex justify-end">
|
||||||
|
@ -218,8 +236,16 @@ export function Form() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="border border-gray-200 dark:border-gray-400 rounded grid grid-cols-2 gap-y-2 p-2">
|
<div className="border border-gray-200 dark:border-gray-400 rounded grid grid-cols-2 gap-y-2 p-2">
|
||||||
<Toggle title="Slicing Box" onChange={handleChange} />
|
<Toggle
|
||||||
<Toggle title="Virtual Profile" onChange={handleDrilling} />
|
title="Slicing Box"
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={exploded ? true : false}
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
title="Virtual Profile"
|
||||||
|
onChange={handleDrilling}
|
||||||
|
disabled={exploded ? true : false}
|
||||||
|
/>
|
||||||
<Toggle title="Coordinate Grid" onChange={handleChangeCG} />
|
<Toggle title="Coordinate Grid" onChange={handleChangeCG} />
|
||||||
<Toggle title="Wireframe" onChange={handleChangeWireframe} />
|
<Toggle title="Wireframe" onChange={handleChangeWireframe} />
|
||||||
<Toggle
|
<Toggle
|
||||||
|
@ -227,6 +253,7 @@ export function Form() {
|
||||||
onChange={handleChangeTopography}
|
onChange={handleChangeTopography}
|
||||||
defaultChecked
|
defaultChecked
|
||||||
/>
|
/>
|
||||||
|
<Toggle title="Explode" onChange={handleExplode} />
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2 pt-2 border border-gray-200 dark:border-gray-400 rounded">
|
<div className="px-2 pt-2 border border-gray-200 dark:border-gray-400 rounded">
|
||||||
<RangeSlider></RangeSlider>
|
<RangeSlider></RangeSlider>
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import {
|
import {
|
||||||
Camera,
|
|
||||||
Group,
|
Group,
|
||||||
|
Material,
|
||||||
Mesh,
|
Mesh,
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
MeshStandardMaterial,
|
MeshStandardMaterial,
|
||||||
|
PerspectiveCamera,
|
||||||
Plane,
|
Plane,
|
||||||
Raycaster,
|
Raycaster,
|
||||||
Scene,
|
Scene,
|
||||||
SphereGeometry,
|
SphereGeometry,
|
||||||
Vector2,
|
Vector2,
|
||||||
Vector3,
|
Vector3,
|
||||||
|
WebGLRenderer,
|
||||||
} from "three";
|
} from "three";
|
||||||
import { buildMeshes } from "./utils/build-meshes";
|
import { buildMeshes } from "./utils/build-meshes";
|
||||||
import { Extent, buildScene } from "./utils/build-scene";
|
import { Extent, buildScene } from "./utils/build-scene";
|
||||||
|
@ -40,7 +42,7 @@ export class SceneView extends EventTarget {
|
||||||
private _scene: Scene;
|
private _scene: Scene;
|
||||||
private _dragControls: DragControls;
|
private _dragControls: DragControls;
|
||||||
private _model: Group;
|
private _model: Group;
|
||||||
private _camera: Camera;
|
private _camera: PerspectiveCamera;
|
||||||
private _container: HTMLElement;
|
private _container: HTMLElement;
|
||||||
private _raycaster: Raycaster;
|
private _raycaster: Raycaster;
|
||||||
private _extent: Extent;
|
private _extent: Extent;
|
||||||
|
@ -50,15 +52,17 @@ export class SceneView extends EventTarget {
|
||||||
private static _DRAG_THRESHOLD = 5;
|
private static _DRAG_THRESHOLD = 5;
|
||||||
private _callback: EventListenerOrEventListenerObject | null = null;
|
private _callback: EventListenerOrEventListenerObject | null = null;
|
||||||
private _orbitControls: OrbitControls;
|
private _orbitControls: OrbitControls;
|
||||||
|
private _renderer: WebGLRenderer;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
model: Group,
|
model: Group,
|
||||||
dragControls: DragControls,
|
dragControls: DragControls,
|
||||||
camera: Camera,
|
camera: PerspectiveCamera,
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
extent: Extent,
|
extent: Extent,
|
||||||
orbitControls: OrbitControls
|
orbitControls: OrbitControls,
|
||||||
|
renderer: WebGLRenderer
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._scene = scene;
|
this._scene = scene;
|
||||||
|
@ -69,13 +73,12 @@ export class SceneView extends EventTarget {
|
||||||
this._raycaster = new Raycaster();
|
this._raycaster = new Raycaster();
|
||||||
this._extent = extent;
|
this._extent = extent;
|
||||||
this._orbitControls = orbitControls;
|
this._orbitControls = orbitControls;
|
||||||
|
this._renderer = renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async create(container: HTMLElement, modelId: string) {
|
static async create(container: HTMLElement, modelId: string) {
|
||||||
const { scene, model, dragControls, camera, extent, controls } = await init(
|
const { scene, model, dragControls, camera, extent, controls, renderer } =
|
||||||
container,
|
await init(container, modelId);
|
||||||
modelId
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SceneView(
|
return new SceneView(
|
||||||
scene,
|
scene,
|
||||||
|
@ -84,7 +87,8 @@ export class SceneView extends EventTarget {
|
||||||
camera,
|
camera,
|
||||||
container,
|
container,
|
||||||
extent,
|
extent,
|
||||||
controls
|
controls,
|
||||||
|
renderer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +315,7 @@ export class SceneView extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to export the group as an OBJ file
|
// Function to export the group as an OBJ file
|
||||||
public exportOBJ() {
|
exportOBJ() {
|
||||||
const exporter = new OBJExporter();
|
const exporter = new OBJExporter();
|
||||||
const objString = exporter.parse(this._model);
|
const objString = exporter.parse(this._model);
|
||||||
|
|
||||||
|
@ -326,9 +330,70 @@ export class SceneView extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset view to initial extent
|
// Reset view to initial extent
|
||||||
public resetView() {
|
resetView() {
|
||||||
this._orbitControls.reset();
|
this._orbitControls.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explode(explode: boolean) {
|
||||||
|
const DISPLACEMENT = 2000;
|
||||||
|
for (let i = 1; i < this._model.children.length; i++) {
|
||||||
|
const mesh = this._model.children[i];
|
||||||
|
|
||||||
|
if (explode) {
|
||||||
|
const displacement =
|
||||||
|
(this._model.children.length - i - 1) * DISPLACEMENT;
|
||||||
|
mesh.userData.originalPosition = mesh.position.clone();
|
||||||
|
mesh.translateZ(displacement);
|
||||||
|
|
||||||
|
if (i === 1) {
|
||||||
|
this._model.userData.zmax = this._extent.zmax;
|
||||||
|
this._extent.zmax += displacement;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mesh.userData.originalPosition) {
|
||||||
|
mesh.position.copy(mesh.userData.originalPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset extent
|
||||||
|
if (!explode && this._model.userData.zmax) {
|
||||||
|
this._extent.zmax = this._model.userData.zmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
const box = this._scene.getObjectByName("clipping-box");
|
||||||
|
let visible = false;
|
||||||
|
if (box) {
|
||||||
|
visible = box.visible;
|
||||||
|
this._scene.remove(box);
|
||||||
|
}
|
||||||
|
const { planes, dragControls } = buildClippingplanes(
|
||||||
|
this._renderer,
|
||||||
|
this._camera,
|
||||||
|
this._orbitControls,
|
||||||
|
this._extent,
|
||||||
|
this._model.children as Mesh[],
|
||||||
|
this._scene,
|
||||||
|
visible
|
||||||
|
);
|
||||||
|
|
||||||
|
this._dragControls = dragControls;
|
||||||
|
|
||||||
|
// Add clipping planes to the meshes
|
||||||
|
for (const mesh of this._model.children) {
|
||||||
|
((mesh as Mesh).material as Material).clippingPlanes = planes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove existing cap meshes
|
||||||
|
for (const o in Orientation) {
|
||||||
|
const capMeshGroupName = `cap-mesh-group-${o}`;
|
||||||
|
let capMeshGroup = this._scene.getObjectByName(capMeshGroupName);
|
||||||
|
while (capMeshGroup) {
|
||||||
|
this._scene.remove(capMeshGroup);
|
||||||
|
capMeshGroup = this._scene.getObjectByName(capMeshGroupName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
|
@ -358,13 +423,15 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
scene.add(model);
|
scene.add(model);
|
||||||
|
|
||||||
// Build the clipping planes and add them to the scene
|
// Build the clipping planes and add them to the scene
|
||||||
|
const visible = false;
|
||||||
const { planes, dragControls } = buildClippingplanes(
|
const { planes, dragControls } = buildClippingplanes(
|
||||||
renderer,
|
renderer,
|
||||||
camera,
|
camera,
|
||||||
controls,
|
controls,
|
||||||
extent,
|
extent,
|
||||||
meshes,
|
meshes,
|
||||||
scene
|
scene,
|
||||||
|
visible
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add clipping planes to the meshes
|
// Add clipping planes to the meshes
|
||||||
|
@ -404,5 +471,5 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
map.name = "topography";
|
map.name = "topography";
|
||||||
scene.add(map);
|
scene.add(map);
|
||||||
|
|
||||||
return { scene, model, dragControls, camera, extent, controls };
|
return { scene, model, dragControls, camera, extent, controls, renderer };
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ import { Extent } from "./build-scene";
|
||||||
import earcut from "earcut";
|
import earcut from "earcut";
|
||||||
|
|
||||||
export enum Orientation {
|
export enum Orientation {
|
||||||
X = "x",
|
X = "X",
|
||||||
Y = "y",
|
Y = "Y",
|
||||||
Z = "z",
|
Z = "Z",
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlaneMesh = Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>;
|
type PlaneMesh = Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>;
|
||||||
|
@ -47,7 +47,8 @@ export function buildClippingplanes(
|
||||||
orbitControls: OrbitControls,
|
orbitControls: OrbitControls,
|
||||||
extent: Extent,
|
extent: Extent,
|
||||||
meshes: Mesh[],
|
meshes: Mesh[],
|
||||||
scene: Scene
|
scene: Scene,
|
||||||
|
visible: boolean
|
||||||
) {
|
) {
|
||||||
const planesData = [
|
const planesData = [
|
||||||
{
|
{
|
||||||
|
@ -166,7 +167,7 @@ export function buildClippingplanes(
|
||||||
const clippingBox = new Group();
|
const clippingBox = new Group();
|
||||||
clippingBox.add(planeMeshGroup, edgeMeshGroup);
|
clippingBox.add(planeMeshGroup, edgeMeshGroup);
|
||||||
clippingBox.name = "clipping-box";
|
clippingBox.name = "clipping-box";
|
||||||
clippingBox.visible = false;
|
clippingBox.visible = visible;
|
||||||
scene.add(clippingBox);
|
scene.add(clippingBox);
|
||||||
|
|
||||||
// Enable DragControls for the clipping planes
|
// Enable DragControls for the clipping planes
|
||||||
|
@ -176,7 +177,7 @@ export function buildClippingplanes(
|
||||||
renderer.domElement
|
renderer.domElement
|
||||||
);
|
);
|
||||||
|
|
||||||
dragControls.enabled = false;
|
dragControls.enabled = visible;
|
||||||
|
|
||||||
dragControls.addEventListener("dragstart", () => {
|
dragControls.addEventListener("dragstart", () => {
|
||||||
// Disable OrbitControls when dragging starts
|
// Disable OrbitControls when dragging starts
|
||||||
|
@ -469,8 +470,6 @@ function generateCapMeshes(
|
||||||
|
|
||||||
// Iterate over the list of geologic meshes
|
// Iterate over the list of geologic meshes
|
||||||
for (const mesh of meshes) {
|
for (const mesh of meshes) {
|
||||||
// Slice visible meshes only
|
|
||||||
|
|
||||||
const position = mesh.geometry.attributes.position.array;
|
const position = mesh.geometry.attributes.position.array;
|
||||||
const indices = mesh.geometry.index ? mesh.geometry.index.array : null;
|
const indices = mesh.geometry.index ? mesh.geometry.index.array : null;
|
||||||
const edges: Array<[Vector3, Vector3]> = [];
|
const edges: Array<[Vector3, Vector3]> = [];
|
||||||
|
@ -484,9 +483,22 @@ function generateCapMeshes(
|
||||||
const i2 = indices ? indices[i + 1] * 3 : (i + 1) * 3;
|
const i2 = indices ? indices[i + 1] * 3 : (i + 1) * 3;
|
||||||
const i3 = indices ? indices[i + 2] * 3 : (i + 2) * 3;
|
const i3 = indices ? indices[i + 2] * 3 : (i + 2) * 3;
|
||||||
|
|
||||||
const v1 = new Vector3(position[i1], position[i1 + 1], position[i1 + 2]);
|
// Account for local translation of the mesh to its original geometry
|
||||||
const v2 = new Vector3(position[i2], position[i2 + 1], position[i2 + 2]);
|
const v1 = new Vector3(
|
||||||
const v3 = new Vector3(position[i3], position[i3 + 1], position[i3 + 2]);
|
position[i1],
|
||||||
|
position[i1 + 1],
|
||||||
|
position[i1 + 2] + mesh.position.z
|
||||||
|
);
|
||||||
|
const v2 = new Vector3(
|
||||||
|
position[i2],
|
||||||
|
position[i2 + 1],
|
||||||
|
position[i2 + 2] + mesh.position.z
|
||||||
|
);
|
||||||
|
const v3 = new Vector3(
|
||||||
|
position[i3],
|
||||||
|
position[i3 + 1],
|
||||||
|
position[i3 + 2] + mesh.position.z
|
||||||
|
);
|
||||||
|
|
||||||
// Check if the triangle is cut by the plane
|
// Check if the triangle is cut by the plane
|
||||||
const d1 = plane.distanceToPoint(v1);
|
const d1 = plane.distanceToPoint(v1);
|
||||||
|
@ -516,7 +528,7 @@ function generateCapMeshes(
|
||||||
color: (mesh.material as MeshStandardMaterial).color,
|
color: (mesh.material as MeshStandardMaterial).color,
|
||||||
side: DoubleSide,
|
side: DoubleSide,
|
||||||
metalness: 0.0,
|
metalness: 0.0,
|
||||||
roughness: 0.75,
|
roughness: 1.0,
|
||||||
flatShading: true,
|
flatShading: true,
|
||||||
polygonOffset: true,
|
polygonOffset: true,
|
||||||
polygonOffsetFactor: offset,
|
polygonOffsetFactor: offset,
|
||||||
|
|
|
@ -52,15 +52,11 @@ async function buildMesh(layerData: MappedFeature) {
|
||||||
const indices = new BufferAttribute(indexArray, 1);
|
const indices = new BufferAttribute(indexArray, 1);
|
||||||
|
|
||||||
geometry.setIndex(indices);
|
geometry.setIndex(indices);
|
||||||
geometry.scale(1, 1, 1);
|
|
||||||
geometry.computeBoundingSphere();
|
|
||||||
geometry.computeVertexNormals();
|
|
||||||
geometry.computeBoundingBox();
|
|
||||||
|
|
||||||
const material = new MeshStandardMaterial({
|
const material = new MeshStandardMaterial({
|
||||||
color: color,
|
color: color,
|
||||||
metalness: 0.0,
|
metalness: 0.0,
|
||||||
roughness: 0.75,
|
roughness: 1.0,
|
||||||
flatShading: true,
|
flatShading: true,
|
||||||
side: DoubleSide,
|
side: DoubleSide,
|
||||||
wireframe: false,
|
wireframe: false,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
SpriteMaterial,
|
SpriteMaterial,
|
||||||
Sprite,
|
Sprite,
|
||||||
Euler,
|
Euler,
|
||||||
|
Color,
|
||||||
} from "three";
|
} from "three";
|
||||||
|
|
||||||
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
||||||
|
@ -41,12 +42,7 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
const width = container.clientWidth;
|
const width = container.clientWidth;
|
||||||
const height = container.clientHeight;
|
const height = container.clientHeight;
|
||||||
|
|
||||||
camera = new PerspectiveCamera(
|
camera = new PerspectiveCamera(50, width / height, 10, maxSize * 20);
|
||||||
50,
|
|
||||||
width / height,
|
|
||||||
maxSize * 0.1,
|
|
||||||
maxSize * 25
|
|
||||||
);
|
|
||||||
|
|
||||||
camera.position.set(center.x, center.y - 200000, extent.zmax + 100000);
|
camera.position.set(center.x, center.y - 200000, extent.zmax + 100000);
|
||||||
camera.up.set(0, 0, 1);
|
camera.up.set(0, 0, 1);
|
||||||
|
@ -54,7 +50,6 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
|
|
||||||
// Initialize the renderer
|
// Initialize the renderer
|
||||||
renderer = new WebGLRenderer({
|
renderer = new WebGLRenderer({
|
||||||
alpha: true,
|
|
||||||
logarithmicDepthBuffer: true,
|
logarithmicDepthBuffer: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,6 +75,7 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
// Set wireframe to false on initial load
|
// Set wireframe to false on initial load
|
||||||
scene = new Scene();
|
scene = new Scene();
|
||||||
scene.userData.wireframe = false;
|
scene.userData.wireframe = false;
|
||||||
|
scene.background = new Color(0xbfd1e5);
|
||||||
|
|
||||||
// Add lights to the scene
|
// Add lights to the scene
|
||||||
buildDefaultLights(scene, extent);
|
buildDefaultLights(scene, extent);
|
||||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue