|
@@ -0,0 +1,716 @@
|
|
|
|
|
+Component({
|
|
|
|
|
+ behaviors: [require('../common/share-behavior').default],
|
|
|
|
|
+ properties: {
|
|
|
|
|
+ markerImg: {
|
|
|
|
|
+ type: String
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ data: {
|
|
|
|
|
+ loaded: false,
|
|
|
|
|
+ arReady: false,
|
|
|
|
|
+ isStartPlay1: false,
|
|
|
|
|
+ adlScale: '0 0 0',
|
|
|
|
|
+ adlPos: '-0.1 -0.5 -0.05',
|
|
|
|
|
+ sunReady: false,
|
|
|
|
|
+ tigerReady: false
|
|
|
|
|
+ },
|
|
|
|
|
+ lifetimes: {
|
|
|
|
|
+ attached() {
|
|
|
|
|
+ },
|
|
|
|
|
+ detached() {
|
|
|
|
|
+ this.stopSunRotation();
|
|
|
|
|
+ this.stopTiger();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ handleReady({
|
|
|
|
|
+ detail
|
|
|
|
|
+ }) {
|
|
|
|
|
+ const xrScene = this.scene = detail.value;
|
|
|
|
|
+ // console.log('xr-scene', xrScene);
|
|
|
|
|
+ },
|
|
|
|
|
+ handleAssetsProgress: function ({
|
|
|
|
|
+ detail
|
|
|
|
|
+ }) {
|
|
|
|
|
+ // console.log('assets progress', detail.value);
|
|
|
|
|
+ },
|
|
|
|
|
+ handleAssetsLoaded: function ({
|
|
|
|
|
+ detail
|
|
|
|
|
+ }) {
|
|
|
|
|
+ console.log('assets loaded', detail.value);
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ loaded: true
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ handleARReady: function ({
|
|
|
|
|
+ detail
|
|
|
|
|
+ }) {
|
|
|
|
|
+ console.log('arReady');
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ arReady: true
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ handleItem1Loaded({ detail }) {
|
|
|
|
|
+ const el = detail.value.target;
|
|
|
|
|
+ const animator = el.getComponent("animator");
|
|
|
|
|
+ this.animator1 = animator
|
|
|
|
|
+ console.log('animator1', animator)
|
|
|
|
|
+
|
|
|
|
|
+ const gltf = el.getComponent("gltf");
|
|
|
|
|
+ this.bgGltf = gltf;
|
|
|
|
|
+ this.sunMeshes = this.resolveSunMeshesFromBG(gltf);
|
|
|
|
|
+ this.setData({ sunReady: !!(this.sunMeshes && this.sunMeshes.length) });
|
|
|
|
|
+ if (this.sunShouldRun) this.startSunRotation();
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerMeshes = this.resolveTigerMeshesFromBG(gltf);
|
|
|
|
|
+ this.setData({ tigerReady: !!(this.tigerMeshes && this.tigerMeshes.length) });
|
|
|
|
|
+ if (this.tigerShouldRun && this.tigerStartRequested) this.scheduleTigerStart();
|
|
|
|
|
+ },
|
|
|
|
|
+ handleItem2Loaded({ detail }) {
|
|
|
|
|
+ const el = detail.value.target;
|
|
|
|
|
+ const animator = el.getComponent("animator");
|
|
|
|
|
+ this.animator2 = animator;
|
|
|
|
|
+ },
|
|
|
|
|
+ handleARTrackerState1({
|
|
|
|
|
+ detail
|
|
|
|
|
+ }) {
|
|
|
|
|
+ // 事件的值即为`ARTracker`实例
|
|
|
|
|
+ const tracker = detail.value;
|
|
|
|
|
+ // 获取当前状态和错误信息
|
|
|
|
|
+ console.log('tracker', tracker)
|
|
|
|
|
+
|
|
|
|
|
+ const {
|
|
|
|
|
+ state,
|
|
|
|
|
+ } = tracker;
|
|
|
|
|
+ if (state == 2) {
|
|
|
|
|
+ this.play()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.pause()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ play() {
|
|
|
|
|
+ if (!this.data.loaded) return
|
|
|
|
|
+ this.sunShouldRun = true;
|
|
|
|
|
+ this.startSunRotation();
|
|
|
|
|
+ this.tigerShouldRun = true;
|
|
|
|
|
+ this.tigerStartRequested = false;
|
|
|
|
|
+ this.prepareTigerAudio();
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.data.isStartPlay1) {
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ isStartPlay1: true
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ this.playitem1Action()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ pause() {
|
|
|
|
|
+ this.sunShouldRun = false;
|
|
|
|
|
+ this.stopSunRotation();
|
|
|
|
|
+ this.tigerShouldRun = false;
|
|
|
|
|
+ this.tigerStartRequested = false;
|
|
|
|
|
+ this.stopTiger();
|
|
|
|
|
+ },
|
|
|
|
|
+ resolveSunMeshesFromBG(gltf) {
|
|
|
|
|
+ if (!gltf || !this.scene) return [];
|
|
|
|
|
+
|
|
|
|
|
+ const candidates = this.getSunNameCandidates();
|
|
|
|
|
+ const uniqueCandidates = [];
|
|
|
|
|
+ for (const name of candidates) {
|
|
|
|
|
+ if (name && !uniqueCandidates.includes(name)) uniqueCandidates.push(name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const name of uniqueCandidates) {
|
|
|
|
|
+ if (typeof gltf.getPrimitivesByNodeName === 'function') {
|
|
|
|
|
+ const meshes = gltf.getPrimitivesByNodeName(name);
|
|
|
|
|
+ if (meshes && meshes.length) return meshes;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof gltf.getPrimitivesByMeshName === 'function') {
|
|
|
|
|
+ const meshes = gltf.getPrimitivesByMeshName(name);
|
|
|
|
|
+ if (meshes && meshes.length) return meshes;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof gltf.getMeshes === 'function') {
|
|
|
|
|
+ const meshes = gltf.getMeshes() || [];
|
|
|
|
|
+ const meshNames = meshes.map(m => m && (m.name || m.el && m.el.name)).filter(Boolean);
|
|
|
|
|
+ console.warn('sun mesh not found, available mesh names:', meshNames);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('sun mesh not found');
|
|
|
|
|
+ }
|
|
|
|
|
+ return [];
|
|
|
|
|
+ },
|
|
|
|
|
+ getSunNameCandidates() {
|
|
|
|
|
+ const defaultCandidates = ['Sun', 'sun', 'SUN', '太阳', 'Taiyang', 'taiyang'];
|
|
|
|
|
+ if (!this.scene) return defaultCandidates;
|
|
|
|
|
+
|
|
|
|
|
+ const gltfAsset = this.scene.assets && this.scene.assets.getAsset && this.scene.assets.getAsset('gltf', 'bg');
|
|
|
|
|
+ const jsonRaw = gltfAsset && gltfAsset.jsonRaw;
|
|
|
|
|
+ if (!jsonRaw) return defaultCandidates;
|
|
|
|
|
+
|
|
|
|
|
+ const names = [];
|
|
|
|
|
+ const addIfMatch = (n) => {
|
|
|
|
|
+ if (!n || typeof n !== 'string') return;
|
|
|
|
|
+ if (/Sun/i.test(n) || n.includes('太阳')) names.push(n);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (Array.isArray(jsonRaw.nodes)) {
|
|
|
|
|
+ for (const node of jsonRaw.nodes) addIfMatch(node && node.name);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Array.isArray(jsonRaw.meshes)) {
|
|
|
|
|
+ for (const mesh of jsonRaw.meshes) addIfMatch(mesh && mesh.name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return names.length ? names.concat(defaultCandidates) : defaultCandidates;
|
|
|
|
|
+ },
|
|
|
|
|
+ startSunRotation() {
|
|
|
|
|
+ if (!this.scene) return;
|
|
|
|
|
+ if (!this.sunShouldRun) return;
|
|
|
|
|
+ if (!this.sunMeshes || !this.sunMeshes.length) return;
|
|
|
|
|
+ if (this.sunRotationTimer) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.sunFrameCount = 159;
|
|
|
|
|
+ this.sunFrameIndex = this.sunFrameIndex || 1;
|
|
|
|
|
+ this.sunTextureCacheLimit = 12;
|
|
|
|
|
+ if (!this.sunTextureCache) this.sunTextureCache = new Map();
|
|
|
|
|
+ if (!this.sunTextureQueue) this.sunTextureQueue = [];
|
|
|
|
|
+
|
|
|
|
|
+ this.updateSunFrame();
|
|
|
|
|
+ this.sunRotationTimer = setInterval(() => this.updateSunFrame(), 80);
|
|
|
|
|
+ },
|
|
|
|
|
+ stopSunRotation() {
|
|
|
|
|
+ if (this.sunRotationTimer) {
|
|
|
|
|
+ clearInterval(this.sunRotationTimer);
|
|
|
|
|
+ this.sunRotationTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.sunFrameLoading = false;
|
|
|
|
|
+ if (this.scene && this.scene.assets && this.scene.assets.releaseAsset && this.sunTextureQueue) {
|
|
|
|
|
+ for (const assetId of this.sunTextureQueue) {
|
|
|
|
|
+ this.scene.assets.releaseAsset('texture', assetId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.sunTextureCache = null;
|
|
|
|
|
+ this.sunTextureQueue = null;
|
|
|
|
|
+ },
|
|
|
|
|
+ async updateSunFrame() {
|
|
|
|
|
+ if (this.sunFrameLoading) return;
|
|
|
|
|
+ if (!this.scene || !this.sunMeshes || !this.sunMeshes.length) return;
|
|
|
|
|
+ if (!this.sunShouldRun) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.sunFrameLoading = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const frame = this.sunFrameIndex;
|
|
|
|
|
+ this.sunFrameIndex = frame >= this.sunFrameCount ? 1 : frame + 1;
|
|
|
|
|
+
|
|
|
|
|
+ const frameStr = String(frame).padStart(3, '0');
|
|
|
|
|
+ const assetId = `sun-${frameStr}`;
|
|
|
|
|
+ const src = `https://ossxiaoan.4dage.com/hq-eduction-vr/sun/Sun_${frameStr}.png`;
|
|
|
|
|
+
|
|
|
|
|
+ let texture = this.sunTextureCache && this.sunTextureCache.get(assetId);
|
|
|
|
|
+ if (!texture) {
|
|
|
|
|
+ const result = await this.scene.assets.loadAsset({ type: 'texture', assetId, src });
|
|
|
|
|
+ texture = result && result.value;
|
|
|
|
|
+ if (texture) {
|
|
|
|
|
+ if (this.sunTextureCache) this.sunTextureCache.set(assetId, texture);
|
|
|
|
|
+ if (this.sunTextureQueue) this.sunTextureQueue.push(assetId);
|
|
|
|
|
+ while (this.sunTextureQueue && this.sunTextureQueue.length > this.sunTextureCacheLimit) {
|
|
|
|
|
+ const oldAssetId = this.sunTextureQueue.shift();
|
|
|
|
|
+ if (oldAssetId) {
|
|
|
|
|
+ this.scene.assets.releaseAsset('texture', oldAssetId);
|
|
|
|
|
+ if (this.sunTextureCache) this.sunTextureCache.delete(oldAssetId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (texture) {
|
|
|
|
|
+ for (const mesh of this.sunMeshes) {
|
|
|
|
|
+ if (mesh && mesh.material && typeof mesh.material.setTexture === 'function') {
|
|
|
|
|
+ mesh.material.setTexture('u_baseColorMap', texture);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('updateSunFrame failed', e);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.sunFrameLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ resolveTigerMeshesFromBG(gltf) {
|
|
|
|
|
+ if (!gltf || !this.scene) return [];
|
|
|
|
|
+
|
|
|
|
|
+ const candidates = this.getTigerNameCandidates();
|
|
|
|
|
+ const uniqueCandidates = [];
|
|
|
|
|
+ for (const name of candidates) {
|
|
|
|
|
+ if (name && !uniqueCandidates.includes(name)) uniqueCandidates.push(name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const name of uniqueCandidates) {
|
|
|
|
|
+ if (typeof gltf.getPrimitivesByNodeName === 'function') {
|
|
|
|
|
+ const meshes = gltf.getPrimitivesByNodeName(name);
|
|
|
|
|
+ if (meshes && meshes.length) return meshes;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof gltf.getPrimitivesByMeshName === 'function') {
|
|
|
|
|
+ const meshes = gltf.getPrimitivesByMeshName(name);
|
|
|
|
|
+ if (meshes && meshes.length) return meshes;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof gltf.getMeshes === 'function') {
|
|
|
|
|
+ const meshes = gltf.getMeshes() || [];
|
|
|
|
|
+ const meshNames = meshes.map(m => m && (m.name || m.el && m.el.name)).filter(Boolean);
|
|
|
|
|
+ console.warn('tiger mesh not found, available mesh names:', meshNames);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('tiger mesh not found');
|
|
|
|
|
+ }
|
|
|
|
|
+ return [];
|
|
|
|
|
+ },
|
|
|
|
|
+ getTigerNameCandidates() {
|
|
|
|
|
+ const defaultCandidates = ['Tiger', 'tiger', 'TIGER', '老虎', '虎', 'Laohu', 'laohu', 'hu'];
|
|
|
|
|
+ if (!this.scene) return defaultCandidates;
|
|
|
|
|
+
|
|
|
|
|
+ const gltfAsset = this.scene.assets && this.scene.assets.getAsset && this.scene.assets.getAsset('gltf', 'bg');
|
|
|
|
|
+ const jsonRaw = gltfAsset && gltfAsset.jsonRaw;
|
|
|
|
|
+ if (!jsonRaw) return defaultCandidates;
|
|
|
|
|
+
|
|
|
|
|
+ const names = [];
|
|
|
|
|
+ const addIfMatch = (n) => {
|
|
|
|
|
+ if (!n || typeof n !== 'string') return;
|
|
|
|
|
+ if (/tiger/i.test(n) || /laohu/i.test(n) || n.includes('虎')) names.push(n);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (Array.isArray(jsonRaw.nodes)) {
|
|
|
|
|
+ for (const node of jsonRaw.nodes) addIfMatch(node && node.name);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Array.isArray(jsonRaw.meshes)) {
|
|
|
|
|
+ for (const mesh of jsonRaw.meshes) addIfMatch(mesh && mesh.name);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return names.length ? names.concat(defaultCandidates) : defaultCandidates;
|
|
|
|
|
+ },
|
|
|
|
|
+ scheduleTigerStart() {
|
|
|
|
|
+ if (!this.scene) return;
|
|
|
|
|
+ if (!this.tigerShouldRun) return;
|
|
|
|
|
+ if (!this.tigerStartRequested) return;
|
|
|
|
|
+ if (this.tigerCompleted) return;
|
|
|
|
|
+ if (this.tigerRotationTimer) return;
|
|
|
|
|
+ if (!this.tigerMeshes || !this.tigerMeshes.length) return;
|
|
|
|
|
+ if (this.tigerAppearTimer) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerAppearTimer = setInterval(() => {
|
|
|
|
|
+ if (!this.tigerShouldRun || this.tigerCompleted) {
|
|
|
|
|
+ this.stopTigerAppearWatch();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const visibleMeshes = this.getVisibleTigerMeshes();
|
|
|
|
|
+ if (!visibleMeshes.length) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.stopTigerAppearWatch();
|
|
|
|
|
+ this.tigerTargetMeshes = visibleMeshes;
|
|
|
|
|
+ this.startTigerAudio();
|
|
|
|
|
+ this.startTigerAnimation();
|
|
|
|
|
+ }, 100);
|
|
|
|
|
+ },
|
|
|
|
|
+ stopTigerAppearWatch() {
|
|
|
|
|
+ if (!this.tigerAppearTimer) return;
|
|
|
|
|
+ clearInterval(this.tigerAppearTimer);
|
|
|
|
|
+ this.tigerAppearTimer = null;
|
|
|
|
|
+ },
|
|
|
|
|
+ isTigerVisible() {
|
|
|
|
|
+ return this.getVisibleTigerMeshes().length > 0;
|
|
|
|
|
+ },
|
|
|
|
|
+ getVisibleTigerMeshes() {
|
|
|
|
|
+ if (!this.tigerMeshes || !this.tigerMeshes.length) return [];
|
|
|
|
|
+ const visible = [];
|
|
|
|
|
+
|
|
|
|
|
+ for (const mesh of this.tigerMeshes) {
|
|
|
|
|
+ const el = mesh && mesh.el;
|
|
|
|
|
+ if (!el || typeof el.getComponent !== 'function') continue;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const transform = el.getComponent('transform');
|
|
|
|
|
+ const scale = transform && transform.scale;
|
|
|
|
|
+ if (!scale) continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof scale === 'string') {
|
|
|
|
|
+ const parts = scale.split(/\s+/).map(Number);
|
|
|
|
|
+ const maxV = Math.max(...parts.filter(n => Number.isFinite(n)));
|
|
|
|
|
+ if (Number.isFinite(maxV) && maxV > 0.001) visible.push(mesh);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof scale === 'object') {
|
|
|
|
|
+ const sx = typeof scale.x === 'number' ? scale.x : undefined;
|
|
|
|
|
+ const sy = typeof scale.y === 'number' ? scale.y : undefined;
|
|
|
|
|
+ const sz = typeof scale.z === 'number' ? scale.z : undefined;
|
|
|
|
|
+ const maxV = Math.max(sx || 0, sy || 0, sz || 0);
|
|
|
|
|
+ if (maxV > 0.001) visible.push(mesh);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return visible;
|
|
|
|
|
+ },
|
|
|
|
|
+ startTigerAnimation() {
|
|
|
|
|
+ if (!this.scene) return;
|
|
|
|
|
+ if (!this.tigerShouldRun) return;
|
|
|
|
|
+ if (!this.tigerMeshes || !this.tigerMeshes.length) return;
|
|
|
|
|
+ if (this.tigerCompleted) return;
|
|
|
|
|
+ if (this.tigerRotationTimer) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerFrameStart = 1;
|
|
|
|
|
+ this.tigerFrameEnd = 460;
|
|
|
|
|
+ this.tigerFrameCount = 460;
|
|
|
|
|
+ const skipFrames = 38;
|
|
|
|
|
+ this.tigerPlayedFrames = Math.min(skipFrames, this.tigerFrameCount);
|
|
|
|
|
+ this.tigerTextureCacheLimit = 12;
|
|
|
|
|
+ if (!this.tigerTextureCache) this.tigerTextureCache = new Map();
|
|
|
|
|
+ if (!this.tigerTextureQueue) this.tigerTextureQueue = [];
|
|
|
|
|
+
|
|
|
|
|
+ this.updateTigerFrame();
|
|
|
|
|
+ const intervalMs = this.getTigerFrameIntervalMs();
|
|
|
|
|
+ this.tigerRotationTimer = setInterval(() => this.updateTigerFrame(), intervalMs);
|
|
|
|
|
+ },
|
|
|
|
|
+ getTigerFrameIntervalMs() {
|
|
|
|
|
+ const durationMs = Number.isFinite(this.tigerAudioDurationMs) && this.tigerAudioDurationMs > 0
|
|
|
|
|
+ ? this.tigerAudioDurationMs
|
|
|
|
|
+ : 46000;
|
|
|
|
|
+ const frameCount = typeof this.tigerFrameCount === 'number' && this.tigerFrameCount > 0 ? this.tigerFrameCount : 460;
|
|
|
|
|
+ const raw = Math.round(durationMs / frameCount);
|
|
|
|
|
+ return Math.min(500, Math.max(16, raw));
|
|
|
|
|
+ },
|
|
|
|
|
+ stopTiger() {
|
|
|
|
|
+ this.stopTigerAppearWatch();
|
|
|
|
|
+ if (this.tigerRotationTimer) {
|
|
|
|
|
+ clearInterval(this.tigerRotationTimer);
|
|
|
|
|
+ this.tigerRotationTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.tigerFrameLoading = false;
|
|
|
|
|
+ this.tigerTargetMeshes = null;
|
|
|
|
|
+ if (this.scene && this.scene.assets && this.scene.assets.releaseAsset && this.tigerTextureQueue) {
|
|
|
|
|
+ for (const assetId of this.tigerTextureQueue) {
|
|
|
|
|
+ if (assetId === this.tigerLastAssetId) continue;
|
|
|
|
|
+ this.scene.assets.releaseAsset('texture', assetId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.tigerTextureCache = null;
|
|
|
|
|
+ this.tigerTextureQueue = null;
|
|
|
|
|
+ this.stopTigerAudio();
|
|
|
|
|
+ },
|
|
|
|
|
+ async updateTigerFrame() {
|
|
|
|
|
+ if (this.tigerFrameLoading) return;
|
|
|
|
|
+ if (!this.scene || !this.tigerMeshes || !this.tigerMeshes.length) return;
|
|
|
|
|
+ if (!this.tigerShouldRun) return;
|
|
|
|
|
+ if (this.tigerCompleted) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerFrameLoading = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const frameCount = typeof this.tigerFrameCount === 'number' && this.tigerFrameCount > 0 ? this.tigerFrameCount : 460;
|
|
|
|
|
+ const durationMs = Number.isFinite(this.tigerAudioDurationMs) && this.tigerAudioDurationMs > 0
|
|
|
|
|
+ ? this.tigerAudioDurationMs
|
|
|
|
|
+ : 46000;
|
|
|
|
|
+
|
|
|
|
|
+ const played = this.tigerPlayedFrames || 0;
|
|
|
|
|
+ let desiredPlayed = played + 1;
|
|
|
|
|
+
|
|
|
|
|
+ const audioCtx = this.tigerAudioCtx;
|
|
|
|
|
+ if (audioCtx && this.tigerAudioStarted) {
|
|
|
|
|
+ const currentTime = Number(audioCtx.currentTime);
|
|
|
|
|
+ if (Number.isFinite(currentTime) && currentTime >= 0) {
|
|
|
|
|
+ const p = Math.floor((currentTime * 1000 / durationMs) * frameCount) + 1;
|
|
|
|
|
+ desiredPlayed = Math.max(desiredPlayed, p);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (desiredPlayed <= played) return;
|
|
|
|
|
+ if (desiredPlayed >= frameCount) desiredPlayed = frameCount;
|
|
|
|
|
+
|
|
|
|
|
+ if (played >= frameCount) {
|
|
|
|
|
+ this.completeTigerAnimation();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const start = typeof this.tigerFrameStart === 'number' ? this.tigerFrameStart : 1;
|
|
|
|
|
+ const end = typeof this.tigerFrameEnd === 'number' ? this.tigerFrameEnd : frameCount;
|
|
|
|
|
+ const rangeLen = Math.max(1, end - start + 1);
|
|
|
|
|
+ const frameNo = start + ((desiredPlayed - 1) % rangeLen);
|
|
|
|
|
+ this.tigerPlayedFrames = desiredPlayed;
|
|
|
|
|
+
|
|
|
|
|
+ const frameStr = String(frameNo).padStart(3, '0');
|
|
|
|
|
+ const assetId = `tigerx-${frameStr}`;
|
|
|
|
|
+ const src = `https://ossxiaoan.4dage.com/hq-eduction-vr/tiger/Tigerx_${frameStr}.png`;
|
|
|
|
|
+
|
|
|
|
|
+ let texture = this.tigerTextureCache && this.tigerTextureCache.get(assetId);
|
|
|
|
|
+ if (!texture) {
|
|
|
|
|
+ const result = await this.scene.assets.loadAsset({ type: 'texture', assetId, src });
|
|
|
|
|
+ texture = result && result.value;
|
|
|
|
|
+ if (texture) {
|
|
|
|
|
+ if (this.tigerTextureCache) this.tigerTextureCache.set(assetId, texture);
|
|
|
|
|
+ if (this.tigerTextureQueue) this.tigerTextureQueue.push(assetId);
|
|
|
|
|
+ while (this.tigerTextureQueue && this.tigerTextureQueue.length > this.tigerTextureCacheLimit) {
|
|
|
|
|
+ const oldAssetId = this.tigerTextureQueue.shift();
|
|
|
|
|
+ if (oldAssetId) {
|
|
|
|
|
+ this.scene.assets.releaseAsset('texture', oldAssetId);
|
|
|
|
|
+ if (this.tigerTextureCache) this.tigerTextureCache.delete(oldAssetId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (texture) {
|
|
|
|
|
+ this.tigerLastAssetId = assetId;
|
|
|
|
|
+ const targets = (this.tigerTargetMeshes && this.tigerTargetMeshes.length) ? this.tigerTargetMeshes : this.tigerMeshes;
|
|
|
|
|
+ for (const mesh of targets) {
|
|
|
|
|
+ if (mesh && mesh.material && typeof mesh.material.setTexture === 'function') {
|
|
|
|
|
+ mesh.material.setTexture('u_baseColorMap', texture);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (this.tigerPlayedFrames >= this.tigerFrameCount) {
|
|
|
|
|
+ this.completeTigerAnimation();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('updateTigerFrame failed', e);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.tigerFrameLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ completeTigerAnimation() {
|
|
|
|
|
+ if (this.tigerRotationTimer) {
|
|
|
|
|
+ clearInterval(this.tigerRotationTimer);
|
|
|
|
|
+ this.tigerRotationTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.tigerCompleted = true;
|
|
|
|
|
+ },
|
|
|
|
|
+ prepareTigerAudio() {
|
|
|
|
|
+ if (this.tigerAudioCtx || this.tigerAudioPreparing) return;
|
|
|
|
|
+ const url = 'https://ossxiaoan.4dage.com/hq-eduction-vr/tiger/tiger.mp3';
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerAudioPrepared = false;
|
|
|
|
|
+ this.tigerAudioStartPending = !!this.tigerAudioStartPending;
|
|
|
|
|
+ this.tigerAudioStarted = false;
|
|
|
|
|
+ this.tigerAudioPreparing = true;
|
|
|
|
|
+
|
|
|
|
|
+ const createAudioCtx = (src) => {
|
|
|
|
|
+ const audioCtx = wx.createInnerAudioContext();
|
|
|
|
|
+ audioCtx.src = src;
|
|
|
|
|
+ audioCtx.loop = false;
|
|
|
|
|
+ audioCtx.volume = 0;
|
|
|
|
|
+
|
|
|
|
|
+ audioCtx.onCanplay(() => {
|
|
|
|
|
+ if (this.tigerAudioPrepared) return;
|
|
|
|
|
+ this.tigerAudioPrepared = true;
|
|
|
|
|
+ if (this.tigerAudioStarted) return;
|
|
|
|
|
+
|
|
|
|
|
+ const updateDuration = () => {
|
|
|
|
|
+ if (!this.tigerAudioCtx) return;
|
|
|
|
|
+ const d = Number(audioCtx.duration);
|
|
|
|
|
+ if (Number.isFinite(d) && d > 0) this.tigerAudioDurationMs = Math.round(d * 1000);
|
|
|
|
|
+ };
|
|
|
|
|
+ updateDuration();
|
|
|
|
|
+ setTimeout(updateDuration, 300);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.volume = 0;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.play();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (!this.tigerAudioCtx) return;
|
|
|
|
|
+ if (this.tigerAudioStarted) return;
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.pause();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (Number(audioCtx.currentTime) > 0.1) audioCtx.seek(0);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.tigerAudioStartPending) this.startTigerAudio();
|
|
|
|
|
+ }, 200);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ audioCtx.onEnded(() => {
|
|
|
|
|
+ this.stopTigerAudio();
|
|
|
|
|
+ this.completeTigerAnimation();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerAudioCtx = audioCtx;
|
|
|
|
|
+ this.tigerAudioPreparing = false;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.play();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (this.tigerAudioTempPath) {
|
|
|
|
|
+ createAudioCtx(this.tigerAudioTempPath);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ wx.downloadFile({
|
|
|
|
|
+ url,
|
|
|
|
|
+ success: (res) => {
|
|
|
|
|
+ const tempPath = res && res.statusCode === 200 ? res.tempFilePath : '';
|
|
|
|
|
+ if (tempPath) this.tigerAudioTempPath = tempPath;
|
|
|
|
|
+ createAudioCtx(tempPath || url);
|
|
|
|
|
+ },
|
|
|
|
|
+ fail: () => {
|
|
|
|
|
+ createAudioCtx(url);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ startTigerAudio() {
|
|
|
|
|
+ if (this.tigerAudioStarted) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.tigerAudioCtx) {
|
|
|
|
|
+ this.tigerAudioStartPending = true;
|
|
|
|
|
+ this.prepareTigerAudio();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!this.tigerAudioCtx) return;
|
|
|
|
|
+ if (this.tigerAudioStarted) return;
|
|
|
|
|
+
|
|
|
|
|
+ const audioCtx = this.tigerAudioCtx;
|
|
|
|
|
+ const startOffsetSec = 2;
|
|
|
|
|
+ const startNow = () => {
|
|
|
|
|
+ if (!this.tigerAudioCtx) return;
|
|
|
|
|
+ if (this.tigerAudioStarted) return;
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.volume = 1;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ const t = Number(audioCtx.currentTime);
|
|
|
|
|
+ if (!Number.isFinite(t) || Math.abs(t - startOffsetSec) > 0.2) audioCtx.seek(startOffsetSec);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ audioCtx.play();
|
|
|
|
|
+ this.tigerAudioStarted = true;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.tigerAudioStarted = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (this.tigerAudioPrepared) {
|
|
|
|
|
+ this.tigerAudioStartPending = false;
|
|
|
|
|
+ startNow();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.tigerAudioStartPending = true;
|
|
|
|
|
+ startNow();
|
|
|
|
|
+ },
|
|
|
|
|
+ stopTigerAudio() {
|
|
|
|
|
+ if (!this.tigerAudioCtx) return;
|
|
|
|
|
+ this.tigerAudioCtx.stop();
|
|
|
|
|
+ this.tigerAudioCtx.destroy();
|
|
|
|
|
+ this.tigerAudioCtx = null;
|
|
|
|
|
+ this.tigerAudioPrepared = false;
|
|
|
|
|
+ this.tigerAudioStartPending = false;
|
|
|
|
|
+ this.tigerAudioStarted = false;
|
|
|
|
|
+ this.tigerAudioPreparing = false;
|
|
|
|
|
+ this.tigerAudioDurationMs = null;
|
|
|
|
|
+ },
|
|
|
|
|
+ playitem1Action() {
|
|
|
|
|
+ if (!this.animator1 || !this.animator2) return;
|
|
|
|
|
+
|
|
|
|
|
+ console.log('start animator1');
|
|
|
|
|
+ this.animator1.play('All Animations', {
|
|
|
|
|
+ loop: 0
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ console.log('start animator2');
|
|
|
|
|
+ this.animator2.pause();
|
|
|
|
|
+
|
|
|
|
|
+ const duration = 1500;
|
|
|
|
|
+ const startTime = Date.now();
|
|
|
|
|
+ const startScale = 0;
|
|
|
|
|
+ const endScale = 0.38;
|
|
|
|
|
+ const startPX = -0.1
|
|
|
|
|
+ const endPX = 0
|
|
|
|
|
+ const startPY = -0.5
|
|
|
|
|
+ const endPY = 0
|
|
|
|
|
+ const startPZ = -0.05
|
|
|
|
|
+ const endPZ = 0
|
|
|
|
|
+
|
|
|
|
|
+ const step = () => {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ let t = (now - startTime) / duration;
|
|
|
|
|
+ if (t > 1) t = 1;
|
|
|
|
|
+
|
|
|
|
|
+ const s = startScale + (endScale - startScale) * t;
|
|
|
|
|
+ const scaleStr = `${s} ${s} ${s}`;
|
|
|
|
|
+ const p = `${startPX + (endPX - startPX) * t} ${startPY + (endPY - startPY) * t} ${startPZ + (endPZ - startPZ) * t}`;
|
|
|
|
|
+
|
|
|
|
|
+ if (t >= 0.5 && !this.isStartPlay2) {
|
|
|
|
|
+ this.isStartPlay2 = true;
|
|
|
|
|
+ this.animator2.play('All Animations', {
|
|
|
|
|
+ loop: 0
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ adlScale: scaleStr,
|
|
|
|
|
+ adlPos: p
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (t < 1) {
|
|
|
|
|
+ setTimeout(step, 16); // 60fps
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const audioUrl = 'https://houseoss.4dkankan.com/project/hq-eduction-vr/public/%E5%AE%89%E5%BE%B7%E7%83%88.mp3';
|
|
|
|
|
+ if (!this.audioCtx) {
|
|
|
|
|
+ const audioCtx = wx.createInnerAudioContext();
|
|
|
|
|
+ audioCtx.src = audioUrl;
|
|
|
|
|
+ audioCtx.play();
|
|
|
|
|
+ audioCtx.onEnded(() => {
|
|
|
|
|
+ const hideDuration = 1000;
|
|
|
|
|
+ const hideStartTime = Date.now();
|
|
|
|
|
+ const startScaleHide = 0.38;
|
|
|
|
|
+ const endScaleHide = 0;
|
|
|
|
|
+
|
|
|
|
|
+ const hideStep = () => {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ let t = (now - hideStartTime) / hideDuration;
|
|
|
|
|
+ if (t > 1) t = 1;
|
|
|
|
|
+
|
|
|
|
|
+ const s = startScaleHide + (endScaleHide - startScaleHide) * t;
|
|
|
|
|
+ const scaleStr = `${s} ${s} ${s}`;
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ adlScale: scaleStr
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (t < 1) {
|
|
|
|
|
+ setTimeout(hideStep, 16);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.animator2 = null;
|
|
|
|
|
+ if (this.audioCtx) {
|
|
|
|
|
+ this.audioCtx.destroy();
|
|
|
|
|
+ this.audioCtx = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.tigerStartRequested = true;
|
|
|
|
|
+ this.scheduleTigerStart();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ hideStep();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ this.setData({
|
|
|
|
|
+ adlScale: '0 0 0'
|
|
|
|
|
+ });
|
|
|
|
|
+ step();
|
|
|
|
|
+ }, 1500);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+})
|