Oasis Engine 是一套 Web 为先,移动优先的互动引擎,使用 Typescript 编写。核心功能由 oasis-engine 提供,非核心和偏业务逻辑定制的高级功能由 oasis-engine-toolkit 提供,推荐通过 NPM 的方式进行安装:
官网:Oasis Engine
体验demo地址:3D全景展示
1660708514209838
<!--
* @Descripttion: 天道通勤
* @version: 1.0.1
* @Author: 汤玉鹏
* @Date: 2022-07-06 15:00:10
* @LastEditors: typ
* @LastEditTime: 2022-08-17 11:45:12
-->
<script setup lang="ts">
import { ElLoading } from "element-plus";
import { onMounted, ref, reactive, getCurrentInstance } from "vue";
// import { createOasis } from "./oasis";
import { OrbitControl, OrthoControl } from "@oasis-engine-toolkit/controls";
// import { OrbitControl,OrthoControl } from "@oasis-engine-toolkit";
import {
AmbientLight,
AssetType,
BackgroundMode,
Camera,
DirectLight,
GLTFResource,
Logger,
PrimitiveMesh,
SkyBoxMaterial,
WebGLEngine,
Script,
Vector3,
Animator,
SpotLight,
PointLight
} from "oasis-engine";
// 右上角面板
// const gui = new dat.GUI();
const msg = ref("Hello Vue3");
const instance = getCurrentInstance();
const setShow = ref(false);
const innerHeight = ref(969);
const tablePage = reactive({
formData: {
pageNum: 1,
pageSize: 10,
galleryType: 1
},
a: 1
});
const show = () => {
setShow.value = true;
};
const hide = () => {
setShow.value = false;
};
const createOasis = () => {
show();
const engine = new WebGLEngine("canvas");
Logger.enable();
//-- create engine object
engine.canvas.resizeByClientSize();
const scene = engine.sceneManager.activeScene;
const { ambientLight, background } = scene;
const rootEntity = scene.createRootEntity();
//Create camera 创建相机
const cameraNode = rootEntity.createChild("camera_node");
// setPosition 通过X, Y, Z值设置本地位置。
cameraNode.transform.setPosition(0, 0, 5);
cameraNode.addComponent(Camera);
// cameraNode.addComponent(OrbitControl);
// 设置相机能够上下左右旋转的角度
const control = cameraNode.addComponent(OrbitControl);
// 判定的距离操作合理范围的最小值
control.minDistance = 2;
// 判定的距离操作合理范围的最大值
control.maxDistance = 7;
//竖直方向操作合理范围的最大弧度
// control.maxPolarAngle = (Math.PI * 3) / 4;
//竖直方向操作合理范围的最小弧度
// control.minPolarAngle = (Math.PI * 1) / 4;
// 水平方向操作合理范围的最大弧度,默认为正无穷大
// control.maxAzimuthAngle = Math.PI / 2;
// 水平方向操作合理范围的最小弧度,默认为负无穷大
// control.minAzimuthAngle = -Math.PI / 2;
// 相机旋转速度,默认为 1.0
control.rotateSpeed = 0.7;
// 是否支持相机旋转,默认为 true
// control.enableRotate = true;
// Create sky 创建的天空
const sky = background.sky;
const skyMaterial = new SkyBoxMaterial(engine);
background.mode = BackgroundMode.SolidColor;
//rgb(116, 111, 108)=(1-116/255,111/255,108/255,1(透明度))
background.solidColor.set(0.545, 0.565, 0.576, 1);
sky.material = skyMaterial;
sky.mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
// 光照
//平行光1正面
// const lightEntityDirect = rootEntity.createChild("light");
// const directLight = lightEntityDirect.addComponent(DirectLight);
// //光照颜色
// directLight.color.set(1, 1, 1, 1);
// //光照强度
// directLight.intensity = 1;
// // 调整方向
// lightEntityDirect.transform.setRotation(0, 0, 10);
// //平行光2背面
// const lightEntityDirec2 = rootEntity.createChild("light");
// const directLight2 = lightEntityDirec2.addComponent(DirectLight);
// //光照颜色
// directLight2.color.set(1, 1, 1, 1);
// //光照强度
// directLight2.intensity = 1;
// // 调整方向
// lightEntityDirec2.transform.setRotation(0, 0, 100);
// 聚光灯1正面
const lightEntitySpot = rootEntity.createChild("light");
const spotLight = lightEntitySpot.addComponent(SpotLight);
spotLight.angle = Math.PI / 10; // 散射角度
spotLight.penumbra = Math.PI / 6; // 半影衰减角度
spotLight.color.set(1, 1, 1, 1);
// 光照强度
spotLight.intensity = 5;
//光照距离
spotLight.distance = 1.5;
//光照位置
lightEntitySpot.transform.setPosition(0, 1.5, 0.1);
//光照角度
lightEntitySpot.transform.setRotation(-90, 0, 0);
// 聚光灯2背面
const lightEntitySpot2 = rootEntity.createChild("light");
const spotLight2 = lightEntitySpot2.addComponent(SpotLight);
spotLight2.angle = Math.PI / 10; // 散射角度
spotLight2.penumbra = Math.PI / 6; // 半影衰减角度
spotLight2.color.set(1, 1, 1, 1);
// 光照强度
spotLight2.intensity = 5;
//光照距离
spotLight2.distance = 2;
//光照位置
lightEntitySpot2.transform.setPosition(0, 1.5, -0.1);
//光照角度
lightEntitySpot2.transform.setRotation(-90, 0, 0);
// 点光1正面
const lightEntityPoint = rootEntity.createChild("light");
const pointLight = lightEntityPoint.addComponent(PointLight);
// 光照强度
pointLight.intensity = 2;
//光照距离
pointLight.distance = 0.4;
// 光照颜色
pointLight.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint.transform.setPosition(-0.5, -0.8, 0.3);
// 点光2正面
const lightEntityPoint2 = rootEntity.createChild("light");
const pointLight2 = lightEntityPoint2.addComponent(PointLight);
// 光照强度
pointLight2.intensity = 5;
//光照距离
pointLight2.distance = 1;
// 光照颜色
pointLight2.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint2.transform.setPosition(1, 0.5, 0);
// 点光3背面
const lightEntityPoint3 = rootEntity.createChild("light");
const pointLight3 = lightEntityPoint3.addComponent(PointLight);
// 光照强度
pointLight3.intensity = 2;
//光照距离
pointLight3.distance = 0.4;
// 光照颜色
pointLight3.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint3.transform.setPosition(-0.5, -0.8, -0.3);
// 点光4背面
const lightEntityPoint4 = rootEntity.createChild("light");
const pointLight4 = lightEntityPoint4.addComponent(PointLight);
// 光照强度
pointLight4.intensity = 2;
//光照距离
pointLight4.distance = 1;
// 光照颜色
pointLight4.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint4.transform.setPosition(1, -0.6, 0);
// 点光5背面
const lightEntityPoint5 = rootEntity.createChild("light");
const pointLight5 = lightEntityPoint5.addComponent(PointLight);
// 光照强度
pointLight5.intensity = 2;
//光照距离
pointLight5.distance = 1.5;
// 光照颜色
pointLight5.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint5.transform.setPosition(-1, 0, 0);
// 点光6背面
const lightEntityPoint6 = rootEntity.createChild("light");
const pointLight6 = lightEntityPoint6.addComponent(PointLight);
// 光照强度
pointLight6.intensity = 1;
//光照距离
pointLight6.distance = 2;
// 光照颜色
pointLight6.color.set(1, 1, 1, 1);
//光照位置
lightEntityPoint6.transform.setPosition(0.3, -1, -1);
window.onresize = () => {
return (() => {
// 监听浏览器发生变化,并重新初始化模型,防止变形
engine.canvas.resizeByClientSize();
})();
};
//光照完结--------
Promise.all([
engine.resourceManager
// GLTF资源路径
// .load<GLTFResource>("src/assets/sxgs.gltf")
// .load<GLTFResource>("src/assets/mao.glb")
.load<GLTFResource>(
"https://file.zhenqingkongjian.com/gallery/embroidery/361d6f_fengye.glb"
)
// .load<GLTFResource>("https://gw.alipayobjects.com/os/bmw-prod/150e44f6-7810-4c45-8029-3575d36aff30.gltf")
.then(gltf => {
const model = gltf.defaultSceneRoot;
// 设置模型默认旋转的角度
// model.transform.rotate(0, 0, 0);
// 设置模型偏移位置
model.transform.setPosition(0, 0, 0);
const entity = rootEntity.createChild("");
entity.addChild(gltf.defaultSceneRoot);
// entity.transform.rotate(new Vector3(0, 0, 0))
// 添加旋转方法
model.addComponent(Rotate);
}),
// 环境光
engine.resourceManager
.load<AmbientLight>({
type: AssetType.Env,
// url: "https://gw.alipayobjects.com/os/bmw-prod/f369110c-0e33-47eb-8296-756e9c80f254.bin"
url:
"https://file.zhenqingkongjian.com/gallery/embroidery/88c5b9_modelBg081.env"
// url: "src/assets/modelBg.hdr.env"
})
.then(ambientLight => {
scene.ambientLight = ambientLight;
skyMaterial.textureCubeMap = ambientLight.specularTexture;
skyMaterial.textureDecodeRGBM = true;
// 设置环境光颜色
// ambientLight.diffuseSolidColor.set(0.5, 0.5, 1, 1);
// 设置环境光强度
ambientLight.diffuseIntensity = 0.5;
// openDebug(ambientLight.specularTexture);
})
]).then(results => {
console.log("results:", results);
engine.run();
let getNum = 0;
let getNumber = setInterval(() => {
getNum++;
if (getNum == 10) {
clearInterval(getNumber);
engine.canvas.resizeByClientSize();
hide();
}
}, 100);
console.log("加载完成.....");
});
// 旋转逻辑
class Rotate extends Script {
private _tempVector = new Vector3(0, 0.75, 0); //旋转方向和速度控制,现为绕Y轴旋转,每帧旋转0.625°
private _totalTime: number = 0;
private _countNum: number = 0;
onStart() {
console.log("_countNum:", this._countNum);
}
onUpdate(deltaTime: number) {
if (this._countNum < 200) {
this._countNum++; //时间累进
this.entity.transform.rotate(this._tempVector); //控制旋转,旋转时长为240ms,旋转角度为200*0.75 = 150°
const scaleFactor = 250 / this._countNum; //缩放的倍数控制,由最大倍数缩小至1.25(250/200)倍
this.entity.transform.scale = new Vector3(
scaleFactor,
scaleFactor,
scaleFactor
);
}
}
}
};
onMounted(() => {
innerHeight.value = window.innerHeight;
// engine.canvas.resizeByClientSize();
// alert(window.screen.availHeight + "---" + window.innerHeight);
createOasis();
});
</script>
<template>
<div class="content-box">
<!-- {
{innerHeight}} -->
<!-- {
{ msg }}--{
{ tablePage.formData.pageNum }}--{
{ tablePage.a }} -->
<transition name="test">
<div class="page" @click.stop v-show="setShow">
<div class="loadings">
<img
src="./assets/bgtu.png"
alt=""
width="80"
style="width:80px; margin-bottom:10px"
/>
<el-progress
:percentage="20"
:indeterminate="true"
:show-text="false"
color="#ffca85"
/>
</div>
</div>
</transition>
<canvas
:style="{ width: '100vw', height: innerHeight + 'px' }"
id="canvas"
/>
</div>
</template>
<style>
.test-enter,
.test-leave-to {
opacity: 0;
}
.test-enter-to,
.test-leave {
opacity: 1;
}
.test-enter-active,
.test-leave-active {
transition: all 2s;
}
::v-deep .el-progress-bar__outer {
background-color: #746f6c;
}
.loadings {
width: 100px;
text-align: center;
background: #000;
padding: 20px;
border-radius: 10px;
}
.page {
position: fixed;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
z-index: 999;
background: rgba(0, 0, 0, 1);
}
.loading {
display: flex;
align-items: center;
justify-content: center;
width: 30vw;
height: 30vw;
background: rgba(0, 0, 0, 0.7);
border-radius: 6px;
}
.el-loading-mask {
position: absolute;
z-index: 2000;
background-color: rgba(0, 0, 0, 0.1);
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
transition: opacity 0.3s;
}
.lodding-box {
position: absolute;
top: 50%;
left: 50%;
margin-left: -60px;
margin-top: -60px;
z-index: 2001;
}
.loading {
width: 60px;
height: 60px;
margin: 0 auto;
margin-top: 100px;
position: relative;
-webkit-animation: load 3s linear infinite;
}
.loading div {
width: 100%;
height: 100%;
position: absolute;
}
.loading span {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: #99cc66;
position: absolute;
left: 50%;
margin-top: -10px;
margin-left: -10px;
-webkit-animation: changeBgColor 3s ease infinite;
}
@-webkit-keyframes load {
0% {
-webkit-transform: rotate(0deg);
}
33.3% {
-webkit-transform: rotate(120deg);
}
66.6% {
-webkit-transform: rotate(240deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes changeBgColor {
0%,
100% {
background: #99cc66;
}
33.3% {
background: #ffff66;
}
66.6% {
background: #ff6666;
}
}
.loading div:nth-child(2) {
-webkit-transform: rotate(120deg);
}
.loading div:nth-child(3) {
-webkit-transform: rotate(240deg);
}
.loading div:nth-child(2) span {
-webkit-animation-delay: 1s;
}
.loading div:nth-child(3) span {
-webkit-animation-delay: 2s;
}
.content-box {
position: relative;
}
.leftbox_img {
position: absolute;
top: 10px;
left: 10px;
right: 0;
}
.leftText {
color: #fff;
}
</style>