diff --git a/sandbox/modelLoad/amort_left_back.glb b/sandbox/modelLoad/amort_left_back.glb new file mode 100644 index 00000000..ef812927 Binary files /dev/null and b/sandbox/modelLoad/amort_left_back.glb differ diff --git a/sandbox/modelLoad/amort_left_front.glb b/sandbox/modelLoad/amort_left_front.glb new file mode 100644 index 00000000..31e4ea65 Binary files /dev/null and b/sandbox/modelLoad/amort_left_front.glb differ diff --git a/sandbox/modelLoad/amort_right_front.glb b/sandbox/modelLoad/amort_right_front.glb new file mode 100644 index 00000000..addbc7f6 Binary files /dev/null and b/sandbox/modelLoad/amort_right_front.glb differ diff --git a/sandbox/modelLoad/dracoLoader.js b/sandbox/modelLoad/dracoLoader.js index f2fe01d1..c05c814d 100644 --- a/sandbox/modelLoad/dracoLoader.js +++ b/sandbox/modelLoad/dracoLoader.js @@ -32,6 +32,178 @@ import { Easing } from "../../lib/og.es.js"; +// Создаем HTML интерфейс для слайдеров +function createSliderControls() { + const controlsContainer = document.createElement('div'); + controlsContainer.style.cssText = ` + position: absolute; + top: 10px; + left: 10px; + background: rgba(0, 0, 0, 0.8); + padding: 20px; + border-radius: 8px; + color: white; + font-family: Arial, sans-serif; + z-index: 1000; + `; + + // Roll слайдер для suspLeftFront + const rollContainer = document.createElement('div'); + rollContainer.style.marginBottom = '15px'; + + const rollLabel = document.createElement('label'); + rollLabel.textContent = 'SuspLeftFront Roll (-90° - 90°): '; + rollLabel.style.display = 'block'; + rollLabel.style.marginBottom = '5px'; + + const rollSlider = document.createElement('input'); + rollSlider.type = 'range'; + rollSlider.min = '-90'; + rollSlider.max = '90'; + rollSlider.value = '0'; + rollSlider.step = '0.1'; + rollSlider.id = 'rollSlider'; + rollSlider.style.width = '200px'; + + const rollValue = document.createElement('span'); + rollValue.id = 'rollValue'; + rollValue.textContent = '0°'; + rollValue.style.marginLeft = '10px'; + + rollContainer.appendChild(rollLabel); + rollContainer.appendChild(rollSlider); + rollContainer.appendChild(rollValue); + + // Pitch слайдер для suspLeftFront + const pitchContainer = document.createElement('div'); + pitchContainer.style.marginBottom = '15px'; + + const pitchLabel = document.createElement('label'); + pitchLabel.textContent = 'SuspLeftFront Pitch (-45° - 45°): '; + pitchLabel.style.display = 'block'; + pitchLabel.style.marginBottom = '5px'; + + const pitchSlider = document.createElement('input'); + pitchSlider.type = 'range'; + pitchSlider.min = '-45'; + pitchSlider.max = '45'; + pitchSlider.value = '13'; + pitchSlider.step = '0.1'; + pitchSlider.id = 'pitchSlider'; + pitchSlider.style.width = '200px'; + + const pitchValue = document.createElement('span'); + pitchValue.id = 'pitchValue'; + pitchValue.textContent = '13°'; + pitchValue.style.marginLeft = '10px'; + + pitchContainer.appendChild(pitchLabel); + pitchContainer.appendChild(pitchSlider); + pitchContainer.appendChild(pitchValue); + + // Yaw слайдер для amortLeftFront + const yawContainer = document.createElement('div'); + yawContainer.style.marginBottom = '15px'; + + const yawLabel = document.createElement('label'); + yawLabel.textContent = 'AmortLeftFront Roll (-180° - 180°): '; + yawLabel.style.display = 'block'; + yawLabel.style.marginBottom = '5px'; + + const yawSlider = document.createElement('input'); + yawSlider.type = 'range'; + yawSlider.min = '-180'; + yawSlider.max = '180'; + yawSlider.value = '0'; + yawSlider.step = '0.1'; + yawSlider.id = 'yawSlider'; + yawSlider.style.width = '200px'; + + const yawValue = document.createElement('span'); + yawValue.id = 'yawValue'; + yawValue.textContent = '0°'; + yawValue.style.marginLeft = '10px'; + + yawContainer.appendChild(yawLabel); + yawContainer.appendChild(yawSlider); + yawContainer.appendChild(yawValue); + + // Roll слайдер для suspLeftBack + const rollBackContainer = document.createElement('div'); + rollBackContainer.style.marginBottom = '15px'; + + const rollBackLabel = document.createElement('label'); + rollBackLabel.textContent = 'SuspLeftBack Roll (-45° - 45°): '; + rollBackLabel.style.display = 'block'; + rollBackLabel.style.marginBottom = '5px'; + + const rollBackSlider = document.createElement('input'); + rollBackSlider.type = 'range'; + rollBackSlider.min = '-45'; + rollBackSlider.max = '45'; + rollBackSlider.value = '0'; + rollBackSlider.step = '0.1'; + rollBackSlider.id = 'rollBackSlider'; + rollBackSlider.style.width = '200px'; + + const rollBackValue = document.createElement('span'); + rollBackValue.id = 'rollBackValue'; + rollBackValue.textContent = '0°'; + rollBackValue.style.marginLeft = '10px'; + + rollBackContainer.appendChild(rollBackLabel); + rollBackContainer.appendChild(rollBackSlider); + rollBackContainer.appendChild(rollBackValue); + + // Yaw слайдер для amortLeftBack + const yawBackContainer = document.createElement('div'); + yawBackContainer.style.marginBottom = '15px'; + + const yawBackLabel = document.createElement('label'); + yawBackLabel.textContent = 'AmortLeftBack Yaw (-90° - 90°): '; + yawBackLabel.style.display = 'block'; + yawBackLabel.style.marginBottom = '5px'; + + const yawBackSlider = document.createElement('input'); + yawBackSlider.type = 'range'; + yawBackSlider.min = '-180'; + yawBackSlider.max = '180'; + yawBackSlider.value = '0'; + yawBackSlider.step = '0.1'; + yawBackSlider.id = 'yawBackSlider'; + yawBackSlider.style.width = '200px'; + + const yawBackValue = document.createElement('span'); + yawBackValue.id = 'yawBackValue'; + yawBackValue.textContent = '0°'; + yawBackValue.style.marginLeft = '10px'; + + yawBackContainer.appendChild(yawBackLabel); + yawBackContainer.appendChild(yawBackSlider); + yawBackContainer.appendChild(yawBackValue); + + controlsContainer.appendChild(rollContainer); + controlsContainer.appendChild(pitchContainer); + controlsContainer.appendChild(yawContainer); + controlsContainer.appendChild(rollBackContainer); + controlsContainer.appendChild(yawBackContainer); + + document.body.appendChild(controlsContainer); + + return { + rollSlider, + pitchSlider, + yawSlider, + rollBackSlider, + yawBackSlider, + rollValue, + pitchValue, + yawValue, + rollBackValue, + yawBackValue + }; +} + let renderer = new Renderer("frame", { msaa: 8, controls: [new control.SimpleNavigation({ speed: 0.01 }), new control.GeoObjectEditor()], @@ -43,7 +215,7 @@ class MyScene extends RenderNode { super("MyScene"); } - init() { + async init() { let collection = new EntityCollection({ entities: [] @@ -51,40 +223,174 @@ class MyScene extends RenderNode { collection.addTo(this); - const cameraPositions = [ - [new Vec3(20, 21, 23), new Vec3(0, 2, 2), Vec3.UP, Easing.ElasticOut], - [new Vec3(40, 10, 15), new Vec3(10, 0, 0), Vec3.LEFT, Easing.ElasticOut], - [new Vec3(10, 30, 45), new Vec3(10, 0, 0), Vec3.DOWN, Easing.CubicInOut], - [new Vec3(40, 10, 15), new Vec3(10, 10, 0), Vec3.RIGHT, Easing.BackInOut], - ]; - let i = 0; - setInterval(() => { - this.renderer.activeCamera.flyCartesian(cameraPositions[i][0], { - look: cameraPositions[i][1], - up: cameraPositions[i][2], - ease: cameraPositions[i][3], - }); - i = (i + 1) % cameraPositions.length; - }, 1500); - console.log(this.renderer.activeCamera); + this.renderer.activeCamera.set(new Vec3(10, 11, 13), new Vec3(0, 2, 2)); - DracoDecoderModule().then((decoderModule) => { - Gltf.connectDracoDecoderModule(decoderModule); - Gltf.loadGlb("./maxwell_the_cat.glb").then((gltf) => { - const entities = gltf.toEntities(); - const cat = entities[0]; - cat.setScale(0.5); - cat.setPitch(-90 * (Math.PI / 180)); - collection.add(cat); - }); + let base = new Entity(); + window.base = base; + + Gltf.loadGlb("./rover_base.glb").then((gltf) => { + const entities = gltf.toEntities(); + for (let i = 0; i < entities.length; i++) { + base.appendChild(entities[i]); + } }); - Gltf.loadGlb("./f22.glb").then((gltf) => { - const entity = gltf.toEntities()[0]; - entity.setScale(1); - entity.setCartesian(20, 5, 0); - entity.setPitch(-90 * (Math.PI / 180)); - collection.add(entity); + let suspLeftFront = new Entity({ + cartesian: new Vec3(0.26, -0.0, -0.78), + pitch: 13 * Math.PI / 180, + relativePosition: true, + }); + + window.suspLeftFront = suspLeftFront; + + Gltf.loadGlb("./susp_left_front.glb").then((gltf) => { + const entities = gltf.toEntities(); + for (let i = 0; i < entities.length; i++) { + entities[i].relativePosition = true; + suspLeftFront.appendChild(entities[i]); + } + }); + + let amortLeftFront = new Entity({ + cartesian: new Vec3(0.876, -0.3, -0.26), + relativePosition: true, + pitch: -103 * Math.PI / 180, + }); + + window.amortLeftFront = amortLeftFront; + + Gltf.loadGlb("./amort_left_front.glb").then((gltf) => { + const entities = gltf.toEntities(); + amortLeftFront.appendChildren(entities, true); + }); + + suspLeftFront.appendChild(amortLeftFront); + + + let suspLeftBack = new Entity({ + cartesian: new Vec3(-0.757, -0.222, -0.008), + relativePosition: true, + pitch: -13 * Math.PI / 180 + }); + + window.suspLeftBack = suspLeftBack; + + Gltf.loadGlb("./susp_left_back.glb").then((gltf) => { + const entities = gltf.toEntities(); + for (let i = 0; i < entities.length; i++) { + entities[i].relativePosition = true; + suspLeftBack.appendChild(entities[i]); + } + }); + + suspLeftFront.appendChild(suspLeftBack); + + let amortLeftBack = new Entity({ + cartesian: new Vec3(-0.625, -0.01, -0.263), + relativePosition: true, + }); + + window.amortLeftBack = amortLeftBack; + + Gltf.loadGlb("./amort_left_back.glb").then((gltf) => { + const entities = gltf.toEntities()[0]; + entities.relativePosition = true; + amortLeftBack.appendChild(entities) + }); + + suspLeftBack.appendChild(amortLeftBack); + + suspLeftFront.appendChild(amortLeftFront); + + base.appendChild(suspLeftFront); + + + let wheelFrontLeft = new Entity({ + cartesian: new Vec3(0, -0.05, -0.395), + relativePosition: true, + pitch: 90 * Math.PI / 180 + }); + + let wheelBackLeft = new Entity({ + cartesian: new Vec3(0, -0.392, 0.065), + relativePosition: true, + }); + + let wheelMiddleLeft = new Entity({ + cartesian: new Vec3(0.45, -0.4, -0.3), + relativePosition: true, + }); + + Gltf.loadGlb("./wheel_left.glb").then((gltf) => { + const entities = gltf.toEntities()[0]; + entities.relativePosition = true; + wheelFrontLeft.appendChild(entities); + }); + + Gltf.loadGlb("./wheel_left.glb").then((gltf) => { + const entities = gltf.toEntities()[0]; + entities.relativePosition = true; + wheelBackLeft.appendChild(entities); + }); + + Gltf.loadGlb("./wheel_left.glb").then((gltf) => { + const entities = gltf.toEntities()[0]; + entities.relativePosition = true; + wheelMiddleLeft.appendChild(entities); + }); + + amortLeftFront.appendChild(wheelFrontLeft); + amortLeftBack.appendChild(wheelBackLeft); + suspLeftBack.appendChild(wheelMiddleLeft); + + let wheelRoll = 0; + this.renderer.events.on("draw", () => { + wheelFrontLeft.setRoll(wheelRoll * Math.PI / 180); + wheelBackLeft.setRoll(wheelRoll * Math.PI / 180); + wheelMiddleLeft.setRoll(wheelRoll * Math.PI / 180); + wheelRoll -= 0.3; + }) + + collection.add(base); + + // Создаем слайдеры после загрузки модели + const sliders = createSliderControls(); + + // Настройка обработчиков событий для слайдеров + sliders.rollSlider.addEventListener('input', (e) => { + const rollDegrees = parseFloat(e.target.value); + const rollRadians = rollDegrees * (Math.PI / 180); + suspLeftFront.setRoll(rollRadians); + sliders.rollValue.textContent = rollDegrees.toFixed(1) + '°'; + }); + + sliders.pitchSlider.addEventListener('input', (e) => { + const pitchDegrees = parseFloat(e.target.value); + const pitchRadians = pitchDegrees * (Math.PI / 180); + suspLeftFront.setPitch(pitchRadians); + sliders.pitchValue.textContent = pitchDegrees.toFixed(1) + '°'; + }); + + sliders.yawSlider.addEventListener('input', (e) => { + const yawDegrees = parseFloat(e.target.value); + const yawRadians = yawDegrees * (Math.PI / 180); + //amortLeftFront.childEntities[0].childEntities[0].setYaw(yawRadians); + amortLeftFront.setRoll(yawRadians); + sliders.yawValue.textContent = yawDegrees.toFixed(1) + '°'; + }); + + sliders.rollBackSlider.addEventListener('input', (e) => { + const rollBackDegrees = parseFloat(e.target.value); + const rollBackRadians = rollBackDegrees * (Math.PI / 180); + suspLeftBack.setRoll(rollBackRadians); + sliders.rollBackValue.textContent = rollBackDegrees.toFixed(1) + '°'; + }); + + sliders.yawBackSlider.addEventListener('input', (e) => { + const yawBackDegrees = parseFloat(e.target.value); + const yawBackRadians = yawBackDegrees * (Math.PI / 180); + amortLeftBack.setYaw(yawBackRadians); + sliders.yawBackValue.textContent = yawBackDegrees.toFixed(1) + '°'; }); } } diff --git a/sandbox/modelLoad/rover_base.glb b/sandbox/modelLoad/rover_base.glb new file mode 100644 index 00000000..0b006dc0 Binary files /dev/null and b/sandbox/modelLoad/rover_base.glb differ diff --git a/sandbox/modelLoad/susp_left_back.glb b/sandbox/modelLoad/susp_left_back.glb new file mode 100644 index 00000000..1ffc8e11 Binary files /dev/null and b/sandbox/modelLoad/susp_left_back.glb differ diff --git a/sandbox/modelLoad/susp_left_front.glb b/sandbox/modelLoad/susp_left_front.glb new file mode 100644 index 00000000..19124ebd Binary files /dev/null and b/sandbox/modelLoad/susp_left_front.glb differ diff --git a/sandbox/modelLoad/wheel_left.glb b/sandbox/modelLoad/wheel_left.glb new file mode 100644 index 00000000..8be9b13b Binary files /dev/null and b/sandbox/modelLoad/wheel_left.glb differ diff --git a/sandbox/modelLoad/wheel_left_front.glb b/sandbox/modelLoad/wheel_left_front.glb new file mode 100644 index 00000000..15456a77 Binary files /dev/null and b/sandbox/modelLoad/wheel_left_front.glb differ diff --git a/sandbox/modelLoad/wheel_right.glb b/sandbox/modelLoad/wheel_right.glb new file mode 100644 index 00000000..9aa6878b Binary files /dev/null and b/sandbox/modelLoad/wheel_right.glb differ diff --git a/src/entity/Entity.ts b/src/entity/Entity.ts index 0c9f5b38..8c91a1b6 100644 --- a/src/entity/Entity.ts +++ b/src/entity/Entity.ts @@ -117,6 +117,8 @@ class Entity { static __counter__: number = 0; + protected _name: string; + /** * Uniq identifier. * @public @@ -311,9 +313,11 @@ class Entity { this.__id = Entity.__counter__++; + this._name = options.name || `entity:${this.__id}`; + this.properties = options.properties || {}; - this.properties.name = this.properties.name != undefined ? this.properties.name : ""; + //this.properties.name = this.properties.name != undefined ? this.properties.name : ""; this.childEntities = []; @@ -395,6 +399,17 @@ class Entity { } + public get name(): string { + return this._name; + } + + public set name(name: string) { + if (name !== this._name) { + this._name = name; + //ec && ec.events.dispatch(ec.events.entityname, this); + } + } + public get isEmpty(): boolean { return !(this.strip || this.polyline @@ -721,6 +736,9 @@ class Entity { this._rollRad = this._qRot.getRoll(); this._updateAbsolutePosition(); + + // ? + //this._useDirectQuaternion = false; } /** @@ -971,7 +989,13 @@ class Entity { this._rootCartesian.copy(parent._rootCartesian); if (!this._useDirectQuaternion) { - this._qRot.setPitchYawRoll(this._pitchRad, this._yawRad, this._rollRad); + //this._qRot.setPitchYawRoll(this._pitchRad, this._yawRad, this._rollRad); + + if (parent && this.forceGlobalRotation) { + this._qRot.setPitchYawRoll(parent._pitchRad, parent._yawRad, parent._rollRad); + } else { + this._qRot.setPitchYawRoll(this._pitchRad, this._yawRad, this._rollRad); + } } parent._absoluteQRot.mulRes(this._qRot, this._absoluteQRot); @@ -1305,6 +1329,21 @@ class Entity { return null; } + /** + * Append child entity. + * @public + * @param {Entity[]} entities - Child entities. + * @param {boolean} [forceRelativePosition] - Force relative position property. + */ + public appendChildren(entities: Entity[], forceRelativePosition?: boolean) { + for (let i = 0; i < entities.length; i++) { + if (forceRelativePosition !== undefined) { + entities[i].relativePosition = forceRelativePosition; + } + this.appendChild(entities[i]); + } + } + /** * Append child entity. * @public diff --git a/src/utils/gltf/gltfParser.ts b/src/utils/gltf/gltfParser.ts index 9d0cb895..a8be7b15 100644 --- a/src/utils/gltf/gltfParser.ts +++ b/src/utils/gltf/gltfParser.ts @@ -1,10 +1,10 @@ -import { DecoderModule } from "draco3d"; -import { Entity } from "../../entity"; -import { Quat } from "../../math/Quat"; -import { Vec3 } from "../../math/Vec3"; -import { Mat4, NumberArray16 } from "../../math/Mat4"; -import { Object3d } from "../../Object3d"; -import { Glb } from "./glbParser"; +import {DecoderModule} from "draco3d"; +import {Entity} from "../../entity"; +import {Quat} from "../../math/Quat"; +import {Vec3} from "../../math/Vec3"; +import {Mat4, NumberArray16} from "../../math/Mat4"; +import {Object3d} from "../../Object3d"; +import {Glb} from "./glbParser"; import { Accessor, AccessorComponentType, @@ -23,9 +23,11 @@ import { export class Gltf { private static _dracoDecoderModule: DecoderModule | null = null; + public static connectDracoDecoderModule(decoder: any): void { Gltf._dracoDecoderModule = decoder; } + public static async loadGlb(url: string) { const data = await Glb.load(url); return new Gltf(data); @@ -79,7 +81,7 @@ export class Gltf { private _nodeToEntity(node: GltfNode, parent?: Entity): Entity { const entity = new Entity({ - name: `node_${node.name}`, + name: node.name, cartesian: new Vec3(0, 0, 0), relativePosition: parent !== undefined, }); @@ -197,7 +199,7 @@ export class Gltf { const source = this.gltf.gltf.textures[ material.pbrMetallicRoughness.baseColorTexture.index - ].source; + ].source; if (source !== undefined) { mat.baseColorTexture = { image: this._images[source], @@ -209,12 +211,12 @@ export class Gltf { const source = this.gltf.gltf.textures[ material.pbrMetallicRoughness.metallicRoughnessTexture.index - ].source; + ].source; if (source !== undefined) { mat.metallicRoughnessTexture = { image: this._images[source], texCoord: - material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord + material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord }; } } @@ -344,7 +346,7 @@ export class Gltf { draco.destroy(decoder); primitive = { - name: `${meshData.name}_${material.name}_${index}`, + name: `${meshData.name}/${material.name}/${index}`, vertices: attributes.POSITION, indices: indices, mode: primitiveData.mode ? primitiveData.mode : PrimitiveMode.triangles, @@ -358,7 +360,7 @@ export class Gltf { ? this.gltf.gltf.accessors[texcoordAccessorKey] : undefined; primitive = { - name: `${meshData.name}_${material.name}_${index}`, + name: `${meshData.name}/${material.name}/${index}`, indices: primitiveData.indices ? Gltf._access(this.gltf.gltf.accessors[primitiveData.indices], this.gltf) : undefined,