@v2x-three/core
Version:
V2X Three.js 核心库 - 开放式的Three.js工具集,支持React和Vue项目
2,289 lines (2,087 loc) • 2.57 MB
JavaScript
'use strict';
var proj4 = require('proj4');
/**
* 🎨 智能气泡管理器
*
* 功能特性:
* - 科技感毛玻璃蓝色半透明气泡
* - 智能避障定位算法
* - 内容自适应布局
* - 设备关联和状态同步
*/
/**
* 智能气泡管理器
*/
class BubbleManager {
constructor(camera, container) {
this.bubbles = new Map();
// 样式配置
this.BUBBLE_STYLES = {
background: 'rgba(0, 123, 255, 0.15)',
backdropFilter: 'blur(20px)',
border: '1px solid rgba(0, 123, 255, 0.3)',
borderRadius: '12px',
boxShadow: '0 8px 32px rgba(0, 123, 255, 0.1)',
color: '#ffffff',
fontSize: '14px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
padding: '16px',
maxWidth: '300px',
minWidth: '200px',
zIndex: '10000',
};
this.camera = camera;
this.container = container;
this.initializeEventListeners();
this.createBubbleContainer();
}
/**
* 初始化事件监听器
*/
initializeEventListeners() {
// 窗口大小变化时重新计算位置
window.addEventListener('resize', () => {
this.updateAllBubblePositions();
});
// 相机移动时更新位置
this.container.addEventListener('mousemove', () => {
requestAnimationFrame(() => {
this.updateAllBubblePositions();
});
});
}
/**
* 创建气泡容器
*/
createBubbleContainer() {
let bubbleContainer = document.getElementById('v2x-bubble-container');
if (!bubbleContainer) {
bubbleContainer = document.createElement('div');
bubbleContainer.id = 'v2x-bubble-container';
bubbleContainer.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
`;
this.container.appendChild(bubbleContainer);
}
}
/**
* 添加气泡
*/
addBubble(config) {
if (this.bubbles.has(config.id)) {
console.warn(`气泡 ${config.id} 已存在,将被替换`);
this.removeBubble(config.id);
}
const element = this.createBubbleElement(config);
const position = this.calculateOptimalPosition(config);
const instance = {
config,
element,
position,
visible: false,
};
this.bubbles.set(config.id, instance);
this.updateBubblePosition(instance);
return config.id;
}
/**
* 移除气泡
*/
removeBubble(id) {
const instance = this.bubbles.get(id);
if (!instance)
return false;
instance.element.remove();
this.bubbles.delete(id);
return true;
}
/**
* 显示气泡
*/
showBubble(id) {
const instance = this.bubbles.get(id);
if (!instance)
return false;
instance.visible = true;
instance.element.style.display = 'block';
instance.element.style.opacity = '0';
// 动画显示
requestAnimationFrame(() => {
instance.element.style.transition =
'opacity 0.3s ease, transform 0.3s ease';
instance.element.style.opacity = '1';
instance.element.style.transform = 'translateY(0) scale(1)';
});
this.updateBubblePosition(instance);
return true;
}
/**
* 隐藏气泡
*/
hideBubble(id) {
const instance = this.bubbles.get(id);
if (!instance)
return false;
instance.visible = false;
instance.element.style.transition =
'opacity 0.3s ease, transform 0.3s ease';
instance.element.style.opacity = '0';
instance.element.style.transform = 'translateY(-10px) scale(0.95)';
setTimeout(() => {
if (!instance.visible) {
instance.element.style.display = 'none';
}
}, 300);
return true;
}
/**
* 更新气泡内容
*/
updateBubbleContent(id, content) {
const instance = this.bubbles.get(id);
if (!instance)
return false;
instance.config.content = { ...instance.config.content, ...content };
const contentElement = instance.element.querySelector('.bubble-content');
if (contentElement) {
contentElement.innerHTML = this.generateContentHTML(instance.config.content);
}
return true;
}
/**
* 创建气泡元素
*/
createBubbleElement(config) {
const bubble = document.createElement('div');
bubble.className = `v2x-bubble bubble-${config.type || 'info'}`;
bubble.style.cssText = `
position: absolute;
background: ${this.BUBBLE_STYLES.background};
backdrop-filter: ${this.BUBBLE_STYLES.backdropFilter};
-webkit-backdrop-filter: ${this.BUBBLE_STYLES.backdropFilter};
border: ${this.BUBBLE_STYLES.border};
border-radius: ${this.BUBBLE_STYLES.borderRadius};
box-shadow: ${this.BUBBLE_STYLES.boxShadow};
color: ${this.BUBBLE_STYLES.color};
font-size: ${this.BUBBLE_STYLES.fontSize};
font-family: ${this.BUBBLE_STYLES.fontFamily};
padding: ${this.BUBBLE_STYLES.padding};
max-width: ${config.maxWidth ? config.maxWidth + 'px' : this.BUBBLE_STYLES.maxWidth};
min-width: ${this.BUBBLE_STYLES.minWidth};
z-index: ${this.BUBBLE_STYLES.zIndex};
pointer-events: auto;
display: none;
opacity: 0;
transform: translateY(-10px) scale(0.95);
transition: opacity 0.3s ease, transform 0.3s ease;
`;
// 添加类型特定样式
if (config.type === 'warning') {
bubble.style.borderColor = 'rgba(255, 193, 7, 0.5)';
bubble.style.background = 'rgba(255, 193, 7, 0.15)';
bubble.style.boxShadow = '0 8px 32px rgba(255, 193, 7, 0.1)';
}
else if (config.type === 'error') {
bubble.style.borderColor = 'rgba(220, 53, 69, 0.5)';
bubble.style.background = 'rgba(220, 53, 69, 0.15)';
bubble.style.boxShadow = '0 8px 32px rgba(220, 53, 69, 0.1)';
}
else if (config.type === 'success') {
bubble.style.borderColor = 'rgba(40, 167, 69, 0.5)';
bubble.style.background = 'rgba(40, 167, 69, 0.15)';
bubble.style.boxShadow = '0 8px 32px rgba(40, 167, 69, 0.1)';
}
bubble.innerHTML = `
<div class="bubble-header" style="
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
font-weight: 600;
">
<span class="bubble-title">${config.title}</span>
<span class="bubble-close" style="
cursor: pointer;
opacity: 0.7;
font-size: 16px;
line-height: 1;
padding: 4px;
border-radius: 4px;
transition: opacity 0.2s;
" data-bubble-id="${config.id}" data-action="close">✕</span>
</div>
<div class="bubble-content" style="line-height: 1.5;">
${this.generateContentHTML(config.content)}
</div>
`;
// 添加悬停效果
bubble.addEventListener('mouseenter', () => {
bubble.style.transform = 'translateY(0) scale(1.02)';
bubble.style.boxShadow = bubble.style.boxShadow.replace('0.1)', '0.2)');
});
bubble.addEventListener('mouseleave', () => {
bubble.style.transform = 'translateY(0) scale(1)';
bubble.style.boxShadow = bubble.style.boxShadow.replace('0.2)', '0.1)');
});
// 添加关闭按钮事件监听器
const closeButton = bubble.querySelector('[data-action="close"]');
if (closeButton) {
closeButton.addEventListener('click', e => {
e.stopPropagation();
this.hideBubble(config.id);
// 触发关闭回调
if (config.onClose) {
config.onClose(config.id);
}
});
}
const container = document.getElementById('v2x-bubble-container');
if (container) {
container.appendChild(bubble);
}
// 初始化视频播放器
if (config.content.videoUrl) {
this.initializeHLS();
// 延迟执行,确保DOM元素已渲染
setTimeout(() => {
this.setupVideoPlayer(bubble);
}, 100);
}
return bubble;
}
/**
* 生成内容HTML
*/
generateContentHTML(content) {
let html = '';
// 状态显示
if (content.status) {
const statusColors = {
online: '#28a745',
offline: '#dc3545',
warning: '#ffc107',
maintenance: '#6c757d',
error: '#dc3545',
moving: '#17a2b8',
};
const statusLabels = {
online: '在线',
offline: '离线',
warning: '警告',
maintenance: '维护中',
error: '错误',
moving: '移动中',
};
html += `
<div style="
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
padding: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 6px;
">
<div style="
width: 8px;
height: 8px;
border-radius: 50%;
background: ${statusColors[content.status]};
"></div>
<span style="font-weight: 500;">${statusLabels[content.status]}</span>
</div>
`;
}
// 视频播放器
if (content.videoUrl) {
html += this.createVideoPlayer(content.videoUrl, content.videoType);
}
// 数据显示
if (content.data) {
html += '<div class="bubble-data">';
Object.entries(content.data).forEach(([key, value]) => {
html += `
<div style="
display: flex;
justify-content: space-between;
margin-bottom: 6px;
padding: 4px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
">
<span style="opacity: 0.8;">${key}:</span>
<span style="font-weight: 500;">${value}</span>
</div>
`;
});
html += '</div>';
}
// 自定义HTML内容
if (content.html) {
html += content.html;
}
// 操作按钮
if (content.actions && content.actions.length > 0) {
html +=
'<div class="bubble-actions" style="margin-top: 12px; display: flex; gap: 8px;">';
content.actions.forEach(action => {
const buttonStyles = {
primary: 'background: rgba(0, 123, 255, 0.8); color: white;',
secondary: 'background: rgba(255, 255, 255, 0.2); color: white;',
danger: 'background: rgba(220, 53, 69, 0.8); color: white;',
};
html += `
<button style="
${buttonStyles[action.type || 'secondary']}
border: none;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
" onclick="${action.onClick}">
${action.icon ? action.icon + ' ' : ''}${action.label}
</button>
`;
});
html += '</div>';
}
return html;
}
/**
* 计算最佳位置(优先显示在下方)
*/
calculateOptimalPosition(config) {
// 如果有手动偏移,直接使用
if (config.offset) {
return {
x: config.offset.x,
y: config.offset.y,
anchor: 'top',
offset: { x: 0, y: 0 },
};
}
// 获取目标在屏幕上的投影位置
const targetPosition = config.targetPosition || config.target.position;
const screenPosition = this.worldToScreen(targetPosition);
// 获取容器尺寸
const containerRect = this.container.getBoundingClientRect();
// 计算最佳锚点和偏移
const bubbleWidth = config.maxWidth || 320; // 估算气泡宽度
const bubbleHeight = config.content.videoUrl ? 300 : 200; // 有视频时增加高度
const margin = 20; // 边距
let anchor = 'top';
let x = screenPosition.x;
let y = screenPosition.y;
// 位置偏好逻辑(优先下方)
const preference = config.positionPreference || 'bottom';
if (preference === 'bottom') {
// 优先显示在下方
if (screenPosition.y + bubbleHeight + margin < containerRect.height) {
anchor = 'top';
y = screenPosition.y + margin;
}
else {
// 下方空间不足,显示在上方
anchor = 'bottom';
y = screenPosition.y - bubbleHeight - margin;
}
}
else if (preference === 'top') {
// 优先显示在上方
if (screenPosition.y - bubbleHeight - margin > 0) {
anchor = 'bottom';
y = screenPosition.y - bubbleHeight - margin;
}
else {
// 上方空间不足,显示在下方
anchor = 'top';
y = screenPosition.y + margin;
}
}
else {
// 自动选择(旧逻辑)
if (screenPosition.y > containerRect.height / 2) {
anchor = 'bottom';
y = screenPosition.y - bubbleHeight - margin;
}
else {
anchor = 'top';
y = screenPosition.y + margin;
}
}
// 水平位置调整(居中对齐设备)
x = screenPosition.x - bubbleWidth / 2;
// 确保不超出容器边界
if (x + bubbleWidth > containerRect.width - margin) {
x = containerRect.width - bubbleWidth - margin;
}
if (x < margin) {
x = margin;
}
// 确保垂直位置不超出边界
if (y < margin) {
y = margin;
}
if (y + bubbleHeight > containerRect.height - margin) {
y = containerRect.height - bubbleHeight - margin;
}
return {
x,
y,
anchor,
offset: { x: 0, y: 0 },
};
}
/**
* 世界坐标转屏幕坐标
*/
worldToScreen(worldPosition) {
const vector = worldPosition.clone();
vector.project(this.camera);
const containerRect = this.container.getBoundingClientRect();
const x = (vector.x * 0.5 + 0.5) * containerRect.width;
const y = (vector.y * -0.5 + 0.5) * containerRect.height;
return { x, y };
}
/**
* 更新气泡位置
*/
updateBubblePosition(instance) {
if (!instance.visible)
return;
const position = this.calculateOptimalPosition(instance.config);
instance.position = position;
instance.element.style.left = position.x + 'px';
instance.element.style.top = position.y + 'px';
}
/**
* 更新所有气泡位置
*/
updateAllBubblePositions() {
this.bubbles.forEach(instance => {
if (instance.visible) {
this.updateBubblePosition(instance);
}
});
}
/**
* 初始化HLS.js用于视频播放
*/
initializeHLS() {
// 动态加载HLS.js
if (typeof window !== 'undefined' && !window.Hls) {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest';
script.onload = () => {
console.log('HLS.js 加载完成');
};
document.head.appendChild(script);
}
}
/**
* 创建视频播放器元素
*/
createVideoPlayer(videoUrl, videoType = 'hls') {
const videoId = `video-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
return `
<div class="bubble-video-container" style="
margin: 8px 0;
border-radius: 8px;
overflow: hidden;
background: rgba(0, 0, 0, 0.8);
">
<video
id="${videoId}"
controls
muted
preload="metadata"
style="
width: 100%;
height: 180px;
object-fit: cover;
display: block;
"
data-video-url="${videoUrl}"
data-video-type="${videoType}"
>
<source src="${videoUrl}" type="application/x-mpegURL">
您的浏览器不支持视频播放
</video>
<div class="video-controls" style="
padding: 8px;
background: rgba(0, 0, 0, 0.9);
font-size: 12px;
color: #fff;
">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>🎥 ${videoType.toUpperCase()} 视频流</span>
<button onclick="this.closest('.bubble-video-container').querySelector('video').load()" style="
background: rgba(255, 255, 255, 0.2);
border: none;
color: #fff;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
">重连</button>
</div>
</div>
</div>
`;
}
/**
* 设置视频播放(支持HLS)
*/
setupVideoPlayer(container) {
const videos = container.querySelectorAll('video[data-video-url]');
videos.forEach(element => {
const video = element;
const videoUrl = video.dataset.videoUrl;
const videoType = video.dataset.videoType || 'hls';
if (videoType === 'hls' && videoUrl) {
// 检查是否支持原生HLS
if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoUrl;
}
// 使用HLS.js
else if (window.Hls && window.Hls.isSupported()) {
const HlsClass = window.Hls;
const hls = new HlsClass({
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90,
});
hls.loadSource(videoUrl);
hls.attachMedia(video);
hls.on(HlsClass.Events.MANIFEST_PARSED, () => {
console.log('HLS 清单解析完成');
});
hls.on(HlsClass.Events.ERROR, (_event, data) => {
console.warn('HLS 错误:', data);
if (data.fatal) {
switch (data.type) {
case HlsClass.ErrorTypes.NETWORK_ERROR:
console.log('网络错误,尝试恢复...');
hls.startLoad();
break;
case HlsClass.ErrorTypes.MEDIA_ERROR:
console.log('媒体错误,尝试恢复...');
hls.recoverMediaError();
break;
default:
console.log('致命错误,销毁HLS实例');
hls.destroy();
break;
}
}
});
// 保存hls实例以便后续清理
video.hlsInstance = hls;
}
else {
console.warn('不支持HLS播放');
video.style.display = 'none';
}
}
});
}
/**
* 启用/禁用气泡管理器
*/
setEnabled(enabled) {
this.bubbles.forEach(instance => {
instance.element.style.display =
enabled && instance.visible ? 'block' : 'none';
});
}
/**
* 获取所有气泡信息
*/
getAllBubbles() {
return Array.from(this.bubbles.entries()).map(([id, instance]) => ({
id,
config: instance.config,
visible: instance.visible,
}));
}
/**
* 清理所有气泡
*/
dispose() {
this.bubbles.forEach(instance => {
// 清理视频播放器实例
const videos = instance.element.querySelectorAll('video[data-video-url]');
videos.forEach((video) => {
if (video.hlsInstance) {
video.hlsInstance.destroy();
}
});
instance.element.remove();
});
this.bubbles.clear();
const container = document.getElementById('v2x-bubble-container');
if (container) {
container.remove();
}
}
}
// 导出工厂函数
function createBubbleManager(camera, container) {
return new BubbleManager(camera, container);
}
var BubbleManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
BubbleManager: BubbleManager,
createBubbleManager: createBubbleManager
});
/**
* @license
* Copyright 2010-2025 Three.js Authors
* SPDX-License-Identifier: MIT
*/
const REVISION = '177';
/**
* Represents mouse buttons and interaction types in context of controls.
*
* @type {ConstantsMouse}
* @constant
*/
const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
/**
* Represents touch interaction types in context of controls.
*
* @type {ConstantsTouch}
* @constant
*/
const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
/**
* Disables face culling.
*
* @type {number}
* @constant
*/
const CullFaceNone = 0;
/**
* Culls back faces.
*
* @type {number}
* @constant
*/
const CullFaceBack = 1;
/**
* Culls front faces.
*
* @type {number}
* @constant
*/
const CullFaceFront = 2;
/**
* Culls both front and back faces.
*
* @type {number}
* @constant
*/
const CullFaceFrontBack = 3;
/**
* Gives unfiltered shadow maps - fastest, but lowest quality.
*
* @type {number}
* @constant
*/
const BasicShadowMap = 0;
/**
* Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm.
*
* @type {number}
* @constant
*/
const PCFShadowMap = 1;
/**
* Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm with
* better soft shadows especially when using low-resolution shadow maps.
*
* @type {number}
* @constant
*/
const PCFSoftShadowMap = 2;
/**
* Filters shadow maps using the Variance Shadow Map (VSM) algorithm.
* When using VSMShadowMap all shadow receivers will also cast shadows.
*
* @type {number}
* @constant
*/
const VSMShadowMap = 3;
/**
* Only front faces are rendered.
*
* @type {number}
* @constant
*/
const FrontSide = 0;
/**
* Only back faces are rendered.
*
* @type {number}
* @constant
*/
const BackSide = 1;
/**
* Both front and back faces are rendered.
*
* @type {number}
* @constant
*/
const DoubleSide = 2;
/**
* No blending is performed which effectively disables
* alpha transparency.
*
* @type {number}
* @constant
*/
const NoBlending = 0;
/**
* The default blending.
*
* @type {number}
* @constant
*/
const NormalBlending = 1;
/**
* Represents additive blending.
*
* @type {number}
* @constant
*/
const AdditiveBlending = 2;
/**
* Represents subtractive blending.
*
* @type {number}
* @constant
*/
const SubtractiveBlending = 3;
/**
* Represents multiply blending.
*
* @type {number}
* @constant
*/
const MultiplyBlending = 4;
/**
* Represents custom blending.
*
* @type {number}
* @constant
*/
const CustomBlending = 5;
/**
* A `source + destination` blending equation.
*
* @type {number}
* @constant
*/
const AddEquation = 100;
/**
* A `source - destination` blending equation.
*
* @type {number}
* @constant
*/
const SubtractEquation = 101;
/**
* A `destination - source` blending equation.
*
* @type {number}
* @constant
*/
const ReverseSubtractEquation = 102;
/**
* A blend equation that uses the minimum of source and destination.
*
* @type {number}
* @constant
*/
const MinEquation = 103;
/**
* A blend equation that uses the maximum of source and destination.
*
* @type {number}
* @constant
*/
const MaxEquation = 104;
/**
* Multiplies all colors by `0`.
*
* @type {number}
* @constant
*/
const ZeroFactor = 200;
/**
* Multiplies all colors by `1`.
*
* @type {number}
* @constant
*/
const OneFactor = 201;
/**
* Multiplies all colors by the source colors.
*
* @type {number}
* @constant
*/
const SrcColorFactor = 202;
/**
* Multiplies all colors by `1` minus each source color.
*
* @type {number}
* @constant
*/
const OneMinusSrcColorFactor = 203;
/**
* Multiplies all colors by the source alpha value.
*
* @type {number}
* @constant
*/
const SrcAlphaFactor = 204;
/**
* Multiplies all colors by 1 minus the source alpha value.
*
* @type {number}
* @constant
*/
const OneMinusSrcAlphaFactor = 205;
/**
* Multiplies all colors by the destination alpha value.
*
* @type {number}
* @constant
*/
const DstAlphaFactor = 206;
/**
* Multiplies all colors by `1` minus the destination alpha value.
*
* @type {number}
* @constant
*/
const OneMinusDstAlphaFactor = 207;
/**
* Multiplies all colors by the destination color.
*
* @type {number}
* @constant
*/
const DstColorFactor = 208;
/**
* Multiplies all colors by `1` minus each destination color.
*
* @type {number}
* @constant
*/
const OneMinusDstColorFactor = 209;
/**
* Multiplies the RGB colors by the smaller of either the source alpha
* value or the value of `1` minus the destination alpha value. The alpha
* value is multiplied by `1`.
*
* @type {number}
* @constant
*/
const SrcAlphaSaturateFactor = 210;
/**
* Multiplies all colors by a constant color.
*
* @type {number}
* @constant
*/
const ConstantColorFactor = 211;
/**
* Multiplies all colors by `1` minus a constant color.
*
* @type {number}
* @constant
*/
const OneMinusConstantColorFactor = 212;
/**
* Multiplies all colors by a constant alpha value.
*
* @type {number}
* @constant
*/
const ConstantAlphaFactor = 213;
/**
* Multiplies all colors by 1 minus a constant alpha value.
*
* @type {number}
* @constant
*/
const OneMinusConstantAlphaFactor = 214;
/**
* Never pass.
*
* @type {number}
* @constant
*/
const NeverDepth = 0;
/**
* Always pass.
*
* @type {number}
* @constant
*/
const AlwaysDepth = 1;
/**
* Pass if the incoming value is less than the depth buffer value.
*
* @type {number}
* @constant
*/
const LessDepth = 2;
/**
* Pass if the incoming value is less than or equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const LessEqualDepth = 3;
/**
* Pass if the incoming value equals the depth buffer value.
*
* @type {number}
* @constant
*/
const EqualDepth = 4;
/**
* Pass if the incoming value is greater than or equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const GreaterEqualDepth = 5;
/**
* Pass if the incoming value is greater than the depth buffer value.
*
* @type {number}
* @constant
*/
const GreaterDepth = 6;
/**
* Pass if the incoming value is not equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const NotEqualDepth = 7;
/**
* Multiplies the environment map color with the surface color.
*
* @type {number}
* @constant
*/
const MultiplyOperation = 0;
/**
* Uses reflectivity to blend between the two colors.
*
* @type {number}
* @constant
*/
const MixOperation = 1;
/**
* Adds the two colors.
*
* @type {number}
* @constant
*/
const AddOperation = 2;
/**
* No tone mapping is applied.
*
* @type {number}
* @constant
*/
const NoToneMapping = 0;
/**
* Linear tone mapping.
*
* @type {number}
* @constant
*/
const LinearToneMapping = 1;
/**
* Reinhard tone mapping.
*
* @type {number}
* @constant
*/
const ReinhardToneMapping = 2;
/**
* Cineon tone mapping.
*
* @type {number}
* @constant
*/
const CineonToneMapping = 3;
/**
* ACES Filmic tone mapping.
*
* @type {number}
* @constant
*/
const ACESFilmicToneMapping = 4;
/**
* Custom tone mapping.
*
* Expects a custom implementation by modifying shader code of the material's fragment shader.
*
* @type {number}
* @constant
*/
const CustomToneMapping = 5;
/**
* AgX tone mapping.
*
* @type {number}
* @constant
*/
const AgXToneMapping = 6;
/**
* Neutral tone mapping.
*
* Implementation based on the Khronos 3D Commerce Group standard tone mapping.
*
* @type {number}
* @constant
*/
const NeutralToneMapping = 7;
/**
* The skinned mesh shares the same world space as the skeleton.
*
* @type {string}
* @constant
*/
const AttachedBindMode = 'attached';
/**
* The skinned mesh does not share the same world space as the skeleton.
* This is useful when a skeleton is shared across multiple skinned meshes.
*
* @type {string}
* @constant
*/
const DetachedBindMode = 'detached';
/**
* Maps textures using the geometry's UV coordinates.
*
* @type {number}
* @constant
*/
const UVMapping = 300;
/**
* Reflection mapping for cube textures.
*
* @type {number}
* @constant
*/
const CubeReflectionMapping = 301;
/**
* Refraction mapping for cube textures.
*
* @type {number}
* @constant
*/
const CubeRefractionMapping = 302;
/**
* Reflection mapping for equirectangular textures.
*
* @type {number}
* @constant
*/
const EquirectangularReflectionMapping = 303;
/**
* Refraction mapping for equirectangular textures.
*
* @type {number}
* @constant
*/
const EquirectangularRefractionMapping = 304;
/**
* Reflection mapping for PMREM textures.
*
* @type {number}
* @constant
*/
const CubeUVReflectionMapping = 306;
/**
* The texture will simply repeat to infinity.
*
* @type {number}
* @constant
*/
const RepeatWrapping = 1000;
/**
* The last pixel of the texture stretches to the edge of the mesh.
*
* @type {number}
* @constant
*/
const ClampToEdgeWrapping = 1001;
/**
* The texture will repeats to infinity, mirroring on each repeat.
*
* @type {number}
* @constant
*/
const MirroredRepeatWrapping = 1002;
/**
* Returns the value of the texture element that is nearest (in Manhattan distance)
* to the specified texture coordinates.
*
* @type {number}
* @constant
*/
const NearestFilter = 1003;
/**
* Chooses the mipmap that most closely matches the size of the pixel being textured
* and uses the `NearestFilter` criterion (the texel nearest to the center of the pixel)
* to produce a texture value.
*
* @type {number}
* @constant
*/
const NearestMipmapNearestFilter = 1004;
const NearestMipMapNearestFilter = 1004; // legacy
/**
* Chooses the two mipmaps that most closely match the size of the pixel being textured and
* uses the `NearestFilter` criterion to produce a texture value from each mipmap.
* The final texture value is a weighted average of those two values.
*
* @type {number}
* @constant
*/
const NearestMipmapLinearFilter = 1005;
const NearestMipMapLinearFilter = 1005; // legacy
/**
* Returns the weighted average of the four texture elements that are closest to the specified
* texture coordinates, and can include items wrapped or repeated from other parts of a texture,
* depending on the values of `wrapS` and `wrapT`, and on the exact mapping.
*
* @type {number}
* @constant
*/
const LinearFilter = 1006;
/**
* Chooses the mipmap that most closely matches the size of the pixel being textured and uses
* the `LinearFilter` criterion (a weighted average of the four texels that are closest to the
* center of the pixel) to produce a texture value.
*
* @type {number}
* @constant
*/
const LinearMipmapNearestFilter = 1007;
const LinearMipMapNearestFilter = 1007; // legacy
/**
* Chooses the two mipmaps that most closely match the size of the pixel being textured and uses
* the `LinearFilter` criterion to produce a texture value from each mipmap. The final texture value
* is a weighted average of those two values.
*
* @type {number}
* @constant
*/
const LinearMipmapLinearFilter = 1008;
const LinearMipMapLinearFilter = 1008; // legacy
/**
* An unsigned byte data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedByteType = 1009;
/**
* A byte data type for textures.
*
* @type {number}
* @constant
*/
const ByteType = 1010;
/**
* A short data type for textures.
*
* @type {number}
* @constant
*/
const ShortType = 1011;
/**
* An unsigned short data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShortType = 1012;
/**
* An int data type for textures.
*
* @type {number}
* @constant
*/
const IntType = 1013;
/**
* An unsigned int data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedIntType = 1014;
/**
* A float data type for textures.
*
* @type {number}
* @constant
*/
const FloatType = 1015;
/**
* A half float data type for textures.
*
* @type {number}
* @constant
*/
const HalfFloatType = 1016;
/**
* An unsigned short 4_4_4_4 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShort4444Type = 1017;
/**
* An unsigned short 5_5_5_1 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShort5551Type = 1018;
/**
* An unsigned int 24_8 data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedInt248Type = 1020;
/**
* An unsigned int 5_9_9_9 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedInt5999Type = 35902;
/**
* Discards the red, green and blue components and reads just the alpha component.
*
* @type {number}
* @constant
*/
const AlphaFormat = 1021;
/**
* Discards the alpha component and reads the red, green and blue component.
*
* @type {number}
* @constant
*/
const RGBFormat = 1022;
/**
* Reads the red, green, blue and alpha components.
*
* @type {number}
* @constant
*/
const RGBAFormat = 1023;
/**
* Reads each element as a single depth value, converts it to floating point, and clamps to the range `[0,1]`.
*
* @type {number}
* @constant
*/
const DepthFormat = 1026;
/**
* Reads each element is a pair of depth and stencil values. The depth component of the pair is interpreted as
* in `DepthFormat`. The stencil component is interpreted based on the depth + stencil internal format.
*
* @type {number}
* @constant
*/
const DepthStencilFormat = 1027;
/**
* Discards the green, blue and alpha components and reads just the red component.
*
* @type {number}
* @constant
*/
const RedFormat = 1028;
/**
* Discards the green, blue and alpha components and reads just the red component. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RedIntegerFormat = 1029;
/**
* Discards the alpha, and blue components and reads the red, and green components.
*
* @type {number}
* @constant
*/
const RGFormat = 1030;
/**
* Discards the alpha, and blue components and reads the red, and green components. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RGIntegerFormat = 1031;
/**
* Discards the alpha component and reads the red, green and blue component. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RGBIntegerFormat = 1032;
/**
* Reads the red, green, blue and alpha components. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RGBAIntegerFormat = 1033;
/**
* A DXT1-compressed image in an RGB image format.
*
* @type {number}
* @constant
*/
const RGB_S3TC_DXT1_Format = 33776;
/**
* A DXT1-compressed image in an RGB image format with a simple on/off alpha value.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT1_Format = 33777;
/**
* A DXT3-compressed image in an RGBA image format. Compared to a 32-bit RGBA texture, it offers 4:1 compression.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT3_Format = 33778;
/**
* A DXT5-compressed image in an RGBA image format. It also provides a 4:1 compression, but differs to the DXT3
* compression in how the alpha compression is done.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT5_Format = 33779;
/**
* PVRTC RGB compression in 4-bit mode. One block for each 4×4 pixels.
*
* @type {number}
* @constant
*/
const RGB_PVRTC_4BPPV1_Format = 35840;
/**
* PVRTC RGB compression in 2-bit mode. One block for each 8×4 pixels.
*
* @type {number}
* @constant
*/
const RGB_PVRTC_2BPPV1_Format = 35841;
/**
* PVRTC RGBA compression in 4-bit mode. One block for each 4×4 pixels.
*
* @type {number}
* @constant
*/
const RGBA_PVRTC_4BPPV1_Format = 35842;
/**
* PVRTC RGBA compression in 2-bit mode. One block for each 8×4 pixels.
*
* @type {number}
* @constant
*/
const RGBA_PVRTC_2BPPV1_Format = 35843;
/**
* ETC1 RGB format.
*
* @type {number}
* @constant
*/
const RGB_ETC1_Format = 36196;
/**
* ETC2 RGB format.
*
* @type {number}
* @constant
*/
const RGB_ETC2_Format = 37492;
/**
* ETC2 RGBA format.
*
* @type {number}
* @constant
*/
const RGBA_ETC2_EAC_Format = 37496;
/**
* ASTC RGBA 4x4 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_4x4_Format = 37808;
/**
* ASTC RGBA 5x4 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_5x4_Format = 37809;
/**
* ASTC RGBA 5x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_5x5_Format = 37810;
/**
* ASTC RGBA 6x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_6x5_Format = 37811;
/**
* ASTC RGBA 6x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_6x6_Format = 37812;
/**
* ASTC RGBA 8x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x5_Format = 37813;
/**
* ASTC RGBA 8x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x6_Format = 37814;
/**
* ASTC RGBA 8x8 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x8_Format = 37815;
/**
* ASTC RGBA 10x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x5_Format = 37816;
/**
* ASTC RGBA 10x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x6_Format = 37817;
/**
* ASTC RGBA 10x8 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x8_Format = 37818;
/**
* ASTC RGBA 10x10 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x10_Format = 37819;
/**
* ASTC RGBA 12x10 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_12x10_Format = 37820;
/**
* ASTC RGBA 12x12 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_12x12_Format = 37821;
/**
* BPTC RGBA format.
*
* @type {number}
* @constant
*/
const RGBA_BPTC_Format = 36492;
/**
* BPTC Signed RGB format.
*
* @type {number}
* @constant
*/
const RGB_BPTC_SIGNED_Format = 36494;
/**
* BPTC Unsigned RGB format.
*
* @type {number}
* @constant
*/
const RGB_BPTC_UNSIGNED_Format = 36495;
/**
* RGTC1 Red format.
*
* @type {number}
* @constant
*/
const RED_RGTC1_Format = 36283;
/**
* RGTC1 Signed Red format.
*
* @type {number}
* @constant
*/
const SIGNED_RED_RGTC1_Format = 36284;
/**
* RGTC2 Red Green format.
*
* @type {number}
* @constant
*/
const RED_GREEN_RGTC2_Format = 36285;
/**
* RGTC2 Signed Red Green format.
*
* @type {number}
* @constant
*/
const SIGNED_RED_GREEN_RGTC2_Format = 36286;
/**
* Animations are played once.
*
* @type {number}
* @constant
*/
const LoopOnce = 2200;
/**
* Animations are played with a chosen number of repetitions, each time jumping from
* the end of the clip directly to its beginning.
*
* @type {number}
* @constant
*/
const LoopRepeat = 2201;
/**
* Animations are played with a chosen number of repetitions, alternately playing forward
* and backward.
*
* @type {number}
* @constant
*/
const LoopPingPong = 2202;
/**
* Discrete interpolation mode for keyframe tracks.
*
* @type {number}
* @constant
*/
const InterpolateDiscrete = 2300;
/**
* Linear interpolation mode for keyframe tracks.
*
* @type {number}
* @constant
*/
const InterpolateLinear = 2301;
/**
* Smooth interpolation mode for keyframe tracks.
*
* @type {number}
* @constant
*/
const InterpolateSmooth = 2302;
/**
* Zero curvature ending for animations.
*
* @type {number}
* @constant
*/
const ZeroCurvatureEnding = 2400;
/**
* Zero slope ending for animations.
*
* @type {number}
* @constant
*/
const ZeroSlopeEnding = 2401;
/**
* Wrap around ending for animations.
*
* @type {number}
* @constant
*/
const WrapAroundEnding = 2402;
/**
* Default animation blend mode.
*
* @type {number}
* @constant
*/
const NormalAnimationBlendMode = 2500;
/**
* Additive animation blend mode. Can be used to layer motions on top of
* each other to build complex performances from smaller re-usable assets.
*
* @type {number}
* @constant
*/
const AdditiveAnimationBlendMode = 2501;
/**
* For every three vertices draw a single triangle.
*
* @type {number}
* @constant
*/
const TrianglesDrawMode = 0;
/**
* For each vertex draw a triangle from the last three vertices.
*
* @type {number}
* @constant
*/
const TriangleStripDrawMode = 1;
/**
* For each vertex draw a triangle from the first vertex and the last two vertices.
*
* @type {number}
* @constant
*/
const TriangleFanDrawMode = 2;
/**
* Basic depth packing.
*
* @type {number}
* @constant
*/
const BasicDepthPacking = 3200;
/**
* A depth value is packed into 32 bit RGBA.
*
* @type {number}
* @constant
*/
const RGBADepthPacking = 3201;
/**
* A depth value is packed into 24 bit RGB.
*
* @type {number}
* @constant
*/
const RGBDepthPacking = 3202;
/**
* A depth value is packed into 16 bit RG.
*
* @type {number}
* @constant
*/
const RGDepthPacking = 3203;
/**
* Normal information is relative to the underlying surface.
*
* @type {number}
* @constant
*/
const TangentSpaceNormalMap = 0;
/**
* Normal information is relative to the object orientation.
*
* @type {number}
* @constant
*/
const ObjectSpaceNormalMap = 1;
// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.
/**
* No color space.
*
* @type {string}
* @constant
*/
const NoColorSpace = '';
/**
* sRGB color space.
*
* @type {string}
* @constant
*/
const SRGBColorSpace = 'srgb';
/**
* sRGB-linear color space.
*
* @type {string}
* @constant
*/
const LinearSRGBColorSpace = 'srgb-linear';
/**
* Linear transfer function.
*
* @type {string}
* @constant
*/
const LinearTransfer = 'linear';
/**
* sRGB transfer function.
*
* @type {string}
* @constant
*/
const SRGBTransfer = 'srgb';
/**
* Sets the stencil buffer value to `0`.
*
* @type {number}
* @constant
*/
const ZeroStencilOp = 0;
/**
* Keeps the current value.
*
* @type {number}
* @constant
*/
const KeepStencilOp = 7680;
/**
* Sets the stencil buffer value to the specified reference value.
*
* @type {number}
* @constant
*/
const ReplaceStencilOp = 7681;
/**
* Increments the current stencil buffer value. Clamps to the maximum representable unsigned value.
*
* @type {number}
* @constant
*/
const IncrementStencilOp = 7682;
/**
* Decrements the current stencil buffer value. Clamps to `0`.
*
* @type {number}
* @constant
*/
const DecrementStencilOp = 7683;
/**
* Increments the current stencil buffer value. Wraps stencil buffer value to zero when incrementing
* the maximum representable unsigned value.
*
* @type {number}
* @constant
*/
const IncrementWrapStencilOp = 34055;
/**
* Decrements the current stencil buffer value. Wraps stencil buffer value to the maximum representable
* unsigned value when decrementing a stencil buffer value of `0`.
*
* @type {number}
* @constant
*/
const DecrementWrapStencilOp = 34056;
/**
* Inverts the current stencil buffer value bitwise.
*
* @type {number}
* @constant
*/
const InvertStencilOp = 5386;
/**
* Will never return true.
*
* @type {number}
* @constant
*/
const NeverStencilFunc = 512;
/**
* Will return true if the stencil reference value is less than the current stencil value.
*
* @type {number}
* @constant
*/
const LessStencilFunc = 513;
/**
* Will return true if the stencil reference value is equal to the current stencil value.
*
* @type {number}
* @constant
*/
const EqualStencilFunc = 514;
/**
* Will return true if the stencil reference value is less than or equal to the current stencil value.
*
* @type {number}
* @constant
*/
const LessEqualStencilFunc = 515;
/**
* Will return true if the stencil reference value is greater than the current stencil value.
*
* @type {number}
* @constant
*/
const GreaterStencilFunc = 516;
/**
* Will return true if the stencil reference value is not equal to the current stencil value.
*
* @type {number}
* @constant
*/
const NotEqualStencilFunc = 517;
/**
* Will return true if the stencil reference value is greater than or equal to the current stencil value.
*
* @type {number}
* @constant
*/
const GreaterEqualStencilFunc = 518;
/**
* Will always return true.
*
* @type {number}
* @constant
*/
const AlwaysStencilFunc = 519;
/**
* Never pass.
*
* @type {number}
* @constant
*/
const NeverCompare = 512;
/**
* Pass if the incoming value is less than the texture value.
*
* @type {number}
* @constant
*/
const LessCompare = 513;
/**
* Pass if the incoming value equals the texture value.
*
* @type {number}
* @constant
*/
const EqualCompare = 514;
/**
* Pass if the incoming value is less than or equal to the texture value.
*
* @type {number}
* @constant
*/
const LessEqualCompare = 515;
/**
* Pass if the incoming value is greater than the texture value.
*
* @type {number}
* @constant
*/
const GreaterCompare = 516;
/**
* Pass if the incoming value is not equal to the texture value.
*
* @type {number}
* @constant
*/
const NotEqualCompare = 517;
/**
* Pass if the incoming value is greater than or equal to the texture value.
*
* @type {number}
* @constant
*/
const GreaterEqualCompare = 518;
/**
* Always pass.
*
* @type {number}
* @constant
*/
const AlwaysCompare = 519;
/**
* The contents are intended to be specified once by the application, and used many
* times as the source for drawing and image specification commands.
*
* @type {number}
* @constant
*/
const StaticDrawUsage = 35044;
/**
* The contents are intended to be respecified repeatedly by the application, and
* used many times as the source for drawing and image specification commands.
*
* @type {number}
* @constant
*/
const DynamicDrawUsage = 35048;
/**
* The contents are intended to be specified once by the application, and used at most
* a few times as the source for drawing and image specification commands.
*
* @type {number}
* @constant
*/
const StreamDrawUsage = 35040;
/**
* The contents are intended to be specified once by reading data from the 3D API, and queried
* many times by the application.
*
* @type {number}
* @constant
*/
const StaticReadUsage = 35045;
/**
* The contents are intended to be respecified repeatedly by reading data from the 3D API, and queried
* many times by the application.
*
* @type {number}
* @constant
*/
const DynamicReadUsage = 35049;
/**
* The contents are intended to be specified once by reading data from the 3D API, and queried at most
* a few times by the application
*
* @type {number}
* @constant
*/
const StreamReadUsage = 35041;
/**
* The contents are intended to be specified once by reading data from the 3D API, and used many times as
* the source for WebGL drawing and image specification commands.
*
* @type {number}
* @constant
*/
const StaticCopyUsage = 35046;
/**
* The contents are intended to be respecified repeatedly by reading data from the 3D API, and used many times
* as the source for WebGL drawing and image specification commands.
*
* @type {number}
* @constant
*/
const DynamicCopyUsage = 35050;
/**
* The contents are intended to be specified once by reading data from the 3D API, and used at most a few times
* as the source for WebGL drawing and image specification commands.
*
* @type {number}
* @constant
*/
const StreamCopyUsage = 35042;
/**
* GLSL 1 shader code.
*
* @type {string}
* @constant
*/
const GLSL1 = '100';
/**
* GLSL 3 shader code.
*
* @type {string}
* @constant
*/
const GLSL3 = '300 es';
/**
* WebGL coordinate system.
*
* @type {number}
* @constant
*/
const WebGLCoordinateSystem = 2000;
/**
* WebGPU coordinate system.
*
* @type {number}
* @constant
*/
const WebGPUCoordinateSystem = 2001;
/**
* Represents the different timestamp query types.
*
* @type {ConstantsTimestampQuery}
* @constant
*/
const TimestampQuery = {
COMPUTE: 'compute',
RENDER: 'render'
};
/**
* Represents mouse buttons and interaction types in context of controls.
*
* @type {ConstantsInterpolationSamplingType}
* @constant
*/
const InterpolationSamplingType = {
PERSPECTIVE: 'perspective',
LINEAR: 'linear',
FLAT: 'flat'
};
/**
* Represents the different interpolation sampling modes.
*
* @type {ConstantsInterpolationSamplingMode}
* @constant
*/
const InterpolationSamplingMode = {
NORMAL: 'normal',
CENTROID: 'centroid',
SAMPLE: 'sample',
FLAT_FIRST: 'flat first',
FLAT_EITHER: 'flat either'
};
/**
* This type represents mouse buttons and interaction types in context of controls.
*
* @typedef {Object} ConstantsMouse
* @property {number} MIDDLE - The left mouse button.
* @property {number} LEFT - The middle mouse button.
* @property {number} RIGHT - The right mouse button.
* @property {number} ROTATE - A rotate interaction.
* @property {number} DOLLY - A dolly interaction.
* @property {number} PAN - A pan interaction.
**/
/**
* This type represents touch interaction types in context of controls.
*
* @typedef {Object} ConstantsTouch
* @property {number} ROTATE - A rotate interaction.
* @property {number} PAN - A pan interaction.
* @property {number} DOLLY_PAN - The dolly-pan interaction.
* @property {nu