本文介绍: Cesium;模型;火箭;火箭动画模型动画模型控制;轨迹;平滑轨迹;Vue3;TS;Vue3中使用Cesium;火箭发射

起因:最近想做模型动画结果上网资料看到网上好多对于模型控制文章都有限制。决定还是自己研究下。欢迎大家一起探讨,评论留言

效果

火箭

全部代码最后

起步

let rocketPrimitive: Cesium.Model
let position = Cesium.Cartesian3.fromDegrees(104.200403, 30.396231, 600.0);
const hpRoll = new Cesium.HeadingPitchRoll();
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator("north", "west");
const rocketPrimitive = viewer.scene.primitives.add(
        Cesium.Model.fromGltf({
            url: "https://assets.agi.com/models/launchvehicle.glb",
            modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
                position,
                hpRoll,
                Cesium.Ellipsoid.WGS84,
                fixedFrameTransform
            ),
            minimumPixelSize: 128,
        })
    );

在这里插入图片描述

模型的组成

const articulations = model.sceneGraph._runtimeArticulations;官方的这段代码我用报错,文档里面没有。也不知道啥问题

在这里插入图片描述

发现nodes,模型里面的关节点,就是模型由哪些分组成,官网文档中也明确的说了。画红线的那段话翻译出来就是”返回glTF中具有给定名称节点。这用于修改用户定义动画节点变换
在这里插入图片描述

模型操作

  • 点火
 rocketPrimitive.setArticulationStage( //对应属性改变参数
   'SRBFlames Size',
     1
 );
 rocketPrimitive.applyArticulations(); //使得修改的属性生效

在这里插入图片描述

 Cesium.Transforms.headingPitchRollToFixedFrame(
        showPath[activeIndex], //当前坐标点Cesium.Cartesian3
        hpRoll,//姿态
        Cesium.Ellipsoid.WGS84,
        fixedFrameTransform,
        rocketPrimitive.modelMatrix //模型当前的世界矩阵
    );

viewer.scene.preUpdate.addEventListener(keepRun)

const keepRun = (scene: Cesium.Scene, time: number) => {
    if (activeIndex >= maxIndex) return
    if (autoDirection && activeIndex > 0 && !showPath[activeIndex - 1].equals(showPath[activeIndex])) { //判断前后两个是否一样,不一样就调整姿态
        const heading = Helper.getHeading(
            showPath[activeIndex - 1],
            showPath[activeIndex],
        );
        if (heading) hpRoll.heading = heading
        const pitch = Helper.getPitch(
            showPath[activeIndex - 1],
            showPath[activeIndex])
        if (pitch) hpRoll.pitch = pitch
    }
    Cesium.Transforms.headingPitchRollToFixedFrame(
        showPath[activeIndex],
        hpRoll,
        Cesium.Ellipsoid.WGS84,
        fixedFrameTransform,
        rocketPrimitive.modelMatrix
    );
    activeIndex += 1
}

在这里插入图片描述

function modelAnimationController(controller: typeModelAnimationController) {
    const { type, initVal, maxVal, fn, step, minVal } = controller
    let num = initVal
    let stopFrame: number
    const max = maxVal || 1
    const min = minVal || -99999
    const duration = step || 0.1
    const render = () => {
        num += duration
        rocketPrimitive.setArticulationStage(
            type,
            num
        );
        rocketPrimitive.applyArticulations();
        stopFrame = requestAnimationFrame(render)
        if (num > max || num <= min) {
            window.cancelAnimationFrame(stopFrame)
            fn && fn()
        }
    }
    render()
}
modelAnimationController({
  type: 'SRBFlames Size', initVal: 0, maxVal: 1, step: 0.05, fn: () => {
       viewer.scene.preUpdate.addEventListener(keepRun)
   }
})

在这里插入图片描述

剩下的都是重复操作,以及反复调试修改达到最佳

  • 比如火焰喷射要有真实感
  • 火箭转向时 喷射头偏转,等等
<template>
    <div class="btn-box">
    </div>
    <Map @onViewerLoaded="onViewerLoaded" :options="options">
    </Map>>
</template>
<script lang="ts" setup>
import Map from "@/components/Cesium/lib/Map.vue";
import * as Cesium from "cesium";
import { GetPosition } from "@/components/Cesium/utils";
import { initLayerPromise } from '@/components/Cesium/utils/initLayer'
import { Helper } from "@/components/Cesium/lib/helper";
let viewer: Cesium.Viewer
const options = {}
let handler: Cesium.ScreenSpaceEventHandler
const onViewerLoaded = (Viewer: Cesium.Viewer) => {
    viewer = Viewer
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

    initLayerPromise(Viewer, true).then(() => {
        viewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromDegrees(104.200403,
                30.396231, 2000),
            // destination: Cesium.Cartesian3.fromDegrees(120.38105869, 31.10115627, 3000),
            complete: () => { init() }
        });
        // init()
    })
    const getP = new GetPosition(Viewer);
    getP.getPositionByClick((position: any) => {
        console.log(position);
    });
};
let planePrimitive: Cesium.Model
let position = Cesium.Cartesian3.fromDegrees(104.200403, 30.396231, 600.0);
const hpRoll = new Cesium.HeadingPitchRoll();
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator("north", "west");

let activeIndex = 0 //插值经纬度索引
let maxIndex = 0// 最大插值经纬度数组索引
let autoDirection = true; //自动调整方向
let path: [number, number, number][] = [] //存在路线数组
let showPath: Cesium.Cartesian3[] = [] //插值数组
let camera: Cesium.Camera
let controller: Cesium.ScreenSpaceCameraController
let r: number
const hpRange = new Cesium.HeadingPitchRange();
let nodes: any[] = []
const init = () => {
    hpRoll.pitch = 90 * Math.PI / 180;
    planePrimitive = viewer.scene.primitives.add(
        Cesium.Model.fromGltf({
            url: "models/launchvehicle.glb",
            modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
                position,
                hpRoll,
                Cesium.Ellipsoid.WGS84,
                fixedFrameTransform
            ),
            minimumPixelSize: 128,
        })
    );
    const scene = viewer.scene;
    planePrimitive.readyPromise.then((model) => {
        camera = viewer.camera;
        controller = scene.screenSpaceCameraController;
        r =
            2.0 * Math.max(model.boundingSphere.radius, camera.frustum.near);
        controller.minimumZoomDistance = r * 0.2;
        /**
         modelAnimationController({
            type: 'SRBFlames Size', initVal: 0, maxVal: 1, step: 0.05, fn: () => {
                modelAnimationController({ type: 'SRBFlames Size', initVal: 1, minVal: 0, step: -0.5 })
                modelAnimationController({
                    type: 'SRBs Separate', initVal: 0, maxVal: 10, step: 0.5, fn: () => {//一级脱落
                        modelAnimationController({ type: 'SRBs Drop', initVal: 0, minVal: -50, step: -0.5 })
                    }
                })
                // modelAnimationController({ type: 'BoosterEngines Yaw', initVal: 0, maxVal: 1, step: 0.1 }) //左右
                // modelAnimationController({ type: 'BoosterEngines Pitch', initVal: 0, maxVal: 1, step: 0.1 }) //上下
                modelAnimationController({
                    type: 'BoosterFlames Size', initVal: 0, maxVal: 1, step: 0.1, fn: () => {
                        modelAnimationController({ type: 'Fairing Open', initVal: 0, maxVal: 45, step: 0.5 })
                        modelAnimationController({ type: 'Fairing Separate', initVal: 0, minVal: -10, step: -0.1 })
                        modelAnimationController({ type: 'Fairing Drop', initVal: 0, minVal: -50, step: -0.5, fn: ()=> {
                            //主推进器脱落
                            modelAnimationController({ type: 'Booster MoveZ', initVal: 0, minVal: -50, step: -0.5})
                            modelAnimationController({ type: 'UpperStageFlames Size', initVal: 0, maxVal: 1, step: 0.05, fn:()=> {
                                modelAnimationController({ type: 'InterstageAdapter MoveZ', initVal: 0, minVal: -50, step: -0.5})
                            }})
                            // modelAnimationController({ type: 'UpperStageEngines Yaw', initVal: 0, maxVal: 1, step: 0.05,})//左右
                            // modelAnimationController({ type: 'UpperStageEngines Pitch', initVal: 0, maxVal: 1, step: 0.05,}) //上下
                        } })
                    }
                })
            }
        })
         */
        lookAt()
        pickup()
        nodes = planePrimitive.gltf.nodes
        // nodes.forEach(i => {
        //     if (new RegExp(/InterstageAdapter/).test(i.name)) {
        //         planePrimitive.getNode(i.name).show = false
        //     }
        // })

        crateLine().then(() => {
            modelAnimationController({
                type: 'SRBFlames Size', initVal: 0, maxVal: 1, step: 0.05, fn: () => {
                    viewer.scene.preUpdate.addEventListener(keepRun)
                }
            })
        })
    })

}
function pickup() {
    handler.setInputAction(function (movement) {
        const pickedObject = viewer.scene.pick(movement.position);
        if (Cesium.defined(pickedObject)) {
            console.log(pickedObject)
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
const lookAt = () => {
    const center = Cesium.Matrix4.multiplyByPoint(
        planePrimitive.modelMatrix,
        Cesium.Cartesian3.ZERO,
        new Cesium.Cartesian3()
    );
    const heading = Cesium.Math.toRadians(10.0);
    const pitch = Cesium.Math.toRadians(-5.0);
    camera.lookAt(
        center,
        new Cesium.HeadingPitchRange(heading, pitch, r * 2)
    );
}
type typeModelAnimationController = {
    type: string;
    initVal: number;
    maxVal?: number;
    minVal?: number;
    fn?: Function;
    step?: number,
}
function modelAnimationController(controller: typeModelAnimationController) {
    const { type, initVal, maxVal, fn, step, minVal } = controller
    let num = initVal
    let stopFrame: number
    const max = maxVal || 1
    const min = minVal || -99999
    const duration = step || 0.1
    const render = () => {
        num += duration
        planePrimitive.setArticulationStage(
            type,
            num
        );
        planePrimitive.applyArticulations();
        stopFrame = requestAnimationFrame(render)
        if (num > max || num <= min) {
            window.cancelAnimationFrame(stopFrame)
            fn && fn()
        }
    }
    render()
}
const crateLine = () => {
    const lon = 104.200403, lat = 30.396231, alt = 20600
    for (let index = 1; index < 20; index++) {
        path.push([lon, lat, 600 + 1000 * index])
    }
    const len = 1000
    let lastLat = 0, lastLon = 0, lastAlt = 0, activeLon, activeLat, activeAlt
    for (let index = 0; index < len; index++) {
        activeLon = Number((lon + index * 0.01).toFixed(6))
        activeLat = Number((lat + index * 0.02).toFixed(6))
        activeAlt = alt + index * 1000
        path.push([activeLon, activeLat, activeAlt])
        if (index === len - 1) {
            lastLon = activeLon
            lastLat = activeLat
            lastAlt = activeAlt
        }
    }
    for (let i = 0; i <= 360; i += 1) {
        path.push([lastLon + i, lastLat, lastAlt])
    }
    return new Promise(resolve => {
        getPosition().then(res => {
            showPath = res
            maxIndex = res.length
            const line = viewer.scene.primitives.add(
                new Cesium.Primitive({
                    geometryInstances: new Cesium.GeometryInstance({
                        geometry: new Cesium.PolylineGeometry({
                            positions: res,
                            width: 3.0,
                            vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT,
                        }),
                        attributes: {
                            color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE.withAlpha(.7)),
                        },
                    }),
                    appearance: new Cesium.PolylineColorAppearance(),
                })
            );
            line.readyPromise.then(() => {
                resolve('')
            })
        })
    })
}

const keepRun = (scene: Cesium.Scene, time: number) => {
    if (activeIndex >= maxIndex) return
    console.log(activeIndex)
    if (activeIndex === 1000) {
        modelAnimationController({ type: 'SRBFlames Size', initVal: 1, minVal: 0, step: -0.5 })
        modelAnimationController({
            type: 'SRBs Separate', initVal: 0, maxVal: 10, step: 0.5, fn: () => {//一级脱落
                modelAnimationController({
                    type: 'SRBs Drop', initVal: 0, minVal: -100, step: -1, fn: () => nodes.forEach(i => {
                        if (new RegExp(/SRBd/).test(i.name)) {
                            planePrimitive.getNode(i.name).show = false
                        }
                    })
                })
            }
        })
        modelAnimationController({ type: 'BoosterFlames Size', initVal: 0, maxVal: 1, step: 0.1 }) //主推期开始点火
    }
    if (activeIndex === 3000) {
        modelAnimationController({ type: 'Fairing Open', initVal: 0, maxVal: 45, step: 0.5 })
        modelAnimationController({ type: 'Fairing Separate', initVal: 0, minVal: -10, step: -0.1 })
        modelAnimationController({
            type: 'Fairing Drop', initVal: 0, minVal: -150, step: -1, fn: () => {
                //主推进器脱落
                modelAnimationController({ type: 'BoosterFlames Size', initVal: 1, minVal: 0, step: -0.05 })
                modelAnimationController({
                    type: 'Booster MoveZ', initVal: 0, minVal: -150, step: -1, fn: () => {
                        nodes.forEach(i => {
                            if (new RegExp(/Fairingd/).test(i.name) || new RegExp(/Booster/).test(i.name)) {
                                planePrimitive.getNode(i.name).show = false
                            }
                        })
                    }
                })
                modelAnimationController({ type: 'UpperStageFlames Size', initVal: 0, maxVal: 1, step: 0.05 })
            }
        })
    }
    if (activeIndex === 3600) {
        modelAnimationController({
            type: 'InterstageAdapter MoveZ', initVal: 0, minVal: -150, step: -1, fn: () => {
                nodes.forEach(i => {
                    if (new RegExp(/InterstageAdapter/).test(i.name)) {
                        planePrimitive.getNode(i.name).show = false
                    }
                })
            }
        })
    }
    lookAt()
    if (autoDirection && activeIndex > 0 && !showPath[activeIndex - 1].equals(showPath[activeIndex])) {
        const heading = Helper.getHeading(
            showPath[activeIndex - 1],
            showPath[activeIndex],
        );
        if (heading) hpRoll.heading = heading
        const pitch = Helper.getPitch(
            showPath[activeIndex - 1],
            showPath[activeIndex])
        if (pitch) hpRoll.pitch = pitch
    }
    Cesium.Transforms.headingPitchRollToFixedFrame(
        showPath[activeIndex],
        hpRoll,
        Cesium.Ellipsoid.WGS84,
        fixedFrameTransform,
        planePrimitive.modelMatrix
    );
    activeIndex += 1
}
const getPosition = () => {
    //插值 new Cesium.LinearSpline  new Cesium.CatmullRomSpline esium.HermiteSpline.createNaturalCubic
    //let pos = Cesium.Cartesian3.lerp(startP, endP, i / duration, new Cesium.Cartesian3());
    return new Promise((resolve: (value: Cesium.Cartesian3[]) => void) => {
        const points = path.map(i => Cesium.Cartesian3.fromDegrees(...i))
        let times: number[] = []
        for (let index = 0; index < points.length; index++) {
            times.push(index)
        }
        const spline = new Cesium.CatmullRomSpline({
            points,
            times,
        });
        const positions: Cesium.Cartesian3[] = [];
        for (let i = 1; i < times.length; i++) {
            for (let j = 0; j < 100; j++) {
                const cartesian3 = spline.evaluate(i - 1 + j * 0.01);
                positions.push(cartesian3);
            }
        }
        resolve(positions)
    })
}
</script>
<style lang="less" scoped>
.btn-box {
    position: absolute;
    top: 10px;
    z-index: 10;
    width: 500px;
    margin-left: 20px;
}
</style>

原文地址:https://blog.csdn.net/qq_41400354/article/details/128017925

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_14041.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注