Skip to content

视频投放


ts
import { Viewer, VideoShadow } from "joDVF";
import {
  Cartesian3,
  Math as CesiumMath,
  JulianDate,
  ClassificationType,
  ClockRange,
  ShadowMode,
  Transforms,
  HeadingPitchRoll,
  VelocityOrientationProperty,
  SampledPositionProperty,
  Color,
  Cartographic,
  PolylineGlowMaterialProperty,
  TimeIntervalCollection,
  TimeInterval
} from "joCesium";
import { Pane } from "tweakpane";

const { mapContainer, uiContainer } = createContainer();

const viewer = new Viewer(mapContainer);
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;
let gltfModel;
let videoShaderInstance;

viewer.camera.flyTo({
  destination: Cartesian3.fromDegrees(108.979413027, 34.3397688922, 7890.5),
  orientation: {
    heading: CesiumMath.toRadians(175.0),
    pitch: CesiumMath.toRadians(-20.0),
    roll: 0.0
  },
  complete: () => {
    videoShaderInstance = new VideoShadow(viewer, {
      id: "videoShaderLayer",
      position: [108.959413027, 34.2197688922, 2487.8386],
      fov: 90,
      near: 130,
      far: 2736.8386,
      pitch: -92.5,
      url: "./assets/video/op.mp4",
      rotation: {
        pitch: -92.5,
        yaw: 0
      },
      alpha: 1,
      debugFrustum: true,
      classificationType: ClassificationType.BOTH,
      isOutputTexture: false
    });
  }
});

const start = JulianDate.fromDate(new Date(2023, 2, 25, 16));
const stop = JulianDate.addSeconds(start, 360, new JulianDate());
CesiumMath.setRandomNumberSeed(3);
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = ClockRange.LOOP_STOP; // Loop at the end
viewer.clock.multiplier = 10;

addUI(uiContainer);
addModel();
addPath();

function addModel() {
  //模型加载 by entity
  gltfModel = viewer.entities.add({
    orientation: Transforms.headingPitchRollQuaternion(
      Cartesian3.fromDegrees(108.959413027, 34.2197688922, 2587.8386),
      new HeadingPitchRoll(
        CesiumMath.toRadians(-90), // 顺时针旋转的角度值
        CesiumMath.toRadians(180),
        CesiumMath.toRadians(0)
      )
    ),

    position: Cartesian3.fromDegrees(108.959413027, 34.2197688922, 2587.8386), //模型的加载位置,
    model: {
      uri: "./assets/model/glb/15-0.glb",
      scale: 0.5,
      incrementallyLoadTextures: true,
      runAnimations: true,
      clampAnimations: true,
      eyeOffset: new Cartesian3(0, 0, -10000),
      disableDepthTestDistance: Number.POSITIVE_INFINITY,
      shadows: ShadowMode.DISABLED,
      show: true
    }
  });
}

function addPath() {
  const position = computeCirclularFlight(108.959413027, 34.2197688922, 0.03);
  viewer.entities.add({
    availability: new TimeIntervalCollection([
      new TimeInterval({
        start: start,
        stop: stop
      })
    ]),
    position,
    orientation: new VelocityOrientationProperty(position),
    path: {
      resolution: 1,
      material: new PolylineGlowMaterialProperty({
        glowPower: 0.1,
        color: Color.YELLOW
      }),
      width: 10
    }
  });
}

function createContainer() {
  const container = document.createElement("div");
  container.style.width = "100%";
  container.style.height = "100%";

  const uiContainer = document.createElement("div");
  uiContainer.style.position = "fixed";
  uiContainer.style.top = "5px";
  uiContainer.style.left = "5px";
  document.body.appendChild(container);
  document.body.appendChild(uiContainer);

  const img = document.createElement("img");
  img.id = "put_img";
  img.style.position = "fixed";
  img.style.bottom = "5px";
  img.style.left = "5px";
  img.style.width = "200px";
  img.style.height = "200px";
  document.body.appendChild(img);
  return {
    mapContainer: container,
    uiContainer
  };
}

function addUI(uiContainer) {
  const PARAMS = {
    videoPlay: false,
    isShowDebugFrustum: true,
    opacity: 1,
    fov: 90,
    yaw: 0,
    pitch: -92.5,
    near: 130,
    far: 2736.8386,
    position: [108.959413027, 34.2197688922, 2487.8386],
    show: true,
    isOutputTexture: false
  };

  const pane = new Pane({
    container: uiContainer,
    title: "参数"
  });

  // opacity
  pane.addBinding(PARAMS, "opacity", {
    step: 0.1,
    min: 0.1,
    max: 1
  });

  // fov
  pane.addBinding(PARAMS, "fov", {
    step: 1,
    min: 0.1,
    max: 180
  });

  // yaw
  pane.addBinding(PARAMS, "yaw", {
    step: 1,
    min: -180,
    max: 180
  });

  // pitch
  pane.addBinding(PARAMS, "pitch", {
    step: 0.5,
    min: -180,
    max: 180
  });

  // far
  pane.addBinding(PARAMS, "far", {
    step: 0.1,
    min: 0.1,
    max: 10000
  });

  pane.addBinding(PARAMS, "videoPlay");
  pane.addBinding(PARAMS, "isShowDebugFrustum");
  pane.addBinding(PARAMS, "isOutputTexture");
  pane.addBinding(PARAMS, "show", {
    label: "是否显示"
  });

  // 改变飞机位置
  const changePositionBtn = pane.addButton({
    title: "change",
    label: "改变飞机位置" // optional
  });

  changePositionBtn.on("click", () => {
    // Compute the entity position property.
    const position = computeCirclularFlight(108.959413027, 34.2197688922, 0.03);
    gltfModel.position = position;
    gltfModel.orientation = new VelocityOrientationProperty(position);

    // pathEntity.position = position
    // pathEntity.orientation = new VelocityOrientationProperty(position)
    viewer.clock.onTick.addEventListener((clock) => {
      const cartesian = position.getValue(clock.currentTime);
      if (!cartesian) {
        return;
      }

      if (!videoShaderInstance) return;
      const cartographic = Cartographic.fromCartesian(cartesian);

      videoShaderInstance.position = [
        CesiumMath.toDegrees(cartographic.longitude),
        CesiumMath.toDegrees(cartographic.latitude),
        cartographic.height
      ];
      const q = gltfModel.orientation.getValue(clock.currentTime);

      const pitch = Math.atan2(
        2 * q.x * q.w - 2 * q.y * q.z,
        1 - 2 * q.x * q.x - 2 * q.z * q.z
      );
      const yaw = Math.atan2(
        2 * q.y * q.w - 2 * q.x * q.z,
        1 - 2 * q.y * q.y - 2 * q.z * q.z
      );
      const roll = Math.asin(2 * q.x * q.y + 2 * q.z * q.w);
      videoShaderInstance.yaw = CesiumMath.toDegrees(-roll + 90);

      if (PARAMS.isOutputTexture) {
        if (videoShaderInstance.reflectionImageData) {
          // const canvas = document.getElementById("canvas-cap");
          const canvas = document.createElement("canvas");
          const ctx = canvas.getContext("2d");
          canvas.width = videoShaderInstance.reflectionImageData.width;
          canvas.height = videoShaderInstance.reflectionImageData.height;
          ctx &&
            ctx.putImageData(videoShaderInstance.reflectionImageData, 0, 0);

          const image = document.getElementById("put_img");
          if (image) {
            (image as HTMLImageElement).src = canvas.toDataURL();
            image.style.display = "block";
          }
        }
      }
    });
  });

  // 获取参数
  const btn = pane.addButton({
    title: "获取参数",
    label: "参数" // optional
  });

  btn.on("click", () => {
    console.info("控制台已打印参数");
    console.log(PARAMS);
  });

  // 销毁对象
  const destoryBtn = pane.addButton({
    title: "destroy",
    label: "销毁对象" // optional
  });

  destoryBtn.on("click", () => {
    if (videoShaderInstance) {
      videoShaderInstance.destroy();
      videoShaderInstance = null;
    }
  });

  pane.on("change", (ev) => {
    let timer: NodeJS.Timeout | null = null;
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    timer = setTimeout(() => {
      console.log("changed: " + JSON.stringify(ev.value), ev);
      if (videoShaderInstance) {
        videoShaderInstance[ev.target.key] = ev.value;
      }
    }, 300);
  });
}

function computeCirclularFlight(lon, lat, radius) {
  const property = new SampledPositionProperty();
  for (let i = 0; i <= 360; i += 45) {
    const radians = CesiumMath.toRadians(i);
    const time = JulianDate.addSeconds(start, i, new JulianDate());
    const position = Cartesian3.fromDegrees(
      lon + radius * 1.5 * Math.cos(radians),
      lat + radius * Math.sin(radians),
      2480.8386
    );
    property.addSample(time, position);

    // Also create a point for each sample we generate.
    viewer.entities.add({
      position: position,
      point: {
        pixelSize: 8,
        color: Color.TRANSPARENT,
        outlineColor: Color.YELLOW,
        outlineWidth: 3
      }
    });
  }
  return property;
}