三维重建(临云镜)SDK 使用文档
1. 背景介绍
三维重建(TDSR)SDK 主要为三维重建(TDSR)提供了平台生成的三维模型的:模型播放能力、
与模型进行互动的能力、以及标签标注能力。整个 SDK 分为 PanoramaScenePlayer 和
PanoramaSceneEdtior 两部分:
- PanoramaScenePlayer 提供了基本模型加载,模型交互(如场景漫游,飞入标签,多视图
切换,事件监听等能力)
- PanoramaSceneEditor 继承了 PanoramaScenePlayer 对象,该对象除了提供模型加载,
模型交互,还提供了标签编辑的能力,可以跟进 API 进行标签创建、标签删除、标签修
改等操作。
该 SDK 与框架无关,您可在 React 框架中使用,也可嵌入到 Angular\Vue 等任何框架中
使用。本文档将以嵌入 React 的方式演示如何利用该 SDK 加载三维模型以及如何与三维模
型进行交互。
2. 关键术语
- 模型: 用户将全景照片上传到三维重建(TDSR)平台,利用平台的模型重建能力创造出来的
模型;用户可以通过 3D 交互的方式查看该模型的结构。也可以飞入到模型中以全景视角
产看场景。
- 场景: 场景跟模型是一一对应的
- 子场景: 与平台子场景含义相同,一张全景图即一个子场景,用户可以在不同子场景进行
漫游。
- 三维模式:传说中的“上帝视角“,在该模式下,用户可以较清晰看到模型结构,以及各个
子场景之间的关联关系
- 全景模式: 子场景模型,可通过鼠标点击/SDK 提供的 API 的操作实现场景间切换从而实
现场景漫游
- 标签:SDK 允许在编辑模式下进行图像的标注,标注后会一标签的形势存在,目前支持标
注模式有图片,图像,文本,链接。
3. 快速上手
3.1 PanoramaScenePlayer 快速上手
PanoramaScenePlayer 相关,详见第 5 章
-
初始化 PlayerService 对象, 该对象封装了前后端通信的能力,提供与后端接口交互
API,详见章节 4。
const service = new PanoramaScenePlayerService();
service.registerHook({
async request(method, url, data, header) {
const prefix = '//localhost/aoding/demo';
return [`${prefix}${url}`, data, header];
},
async response(result, context) {
return {
code: result.code,
message: result.message,
data: result.data,
};
},
});
-
初始化播放器对象
const player = new PanoramaScenePlayer(document, {
service: service,
mode: '1',
});
-
模型加载
await player.load(token);
-
设置监听事件,详见 5.3 节
player.registerSceneHook({
onOrbitRotate: (quaternion) => {
console.log('Current Quaternion', quaternion);
},
onChangePanorama: (panoId) => {
console.log('Current Panorama', panoId);
},
});
-
基于 React 的完整代码如下:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {
PanoramaScenePlayer,
PanoramaScenePlayerService,
utils,
DefaultSceneData,
} from 'alibabacloud-tdsr-js-sdk';
import axios from 'axios';
function usePlayer(token, target) {
const [myPlayer, setMyPlayer] = React.useState(null);
const init = async () => {
const service = new PanoramaScenePlayerService();
service.registerHook({
async request(method, url, data, header) {
const prefix = '//localhost/aoding/demo';
return [`${prefix}${url}`, data, header];
},
async response(result, context) {
return {
code: result.code,
message: result.message,
data: result.data,
};
},
});
const player = new PanoramaScenePlayer(target.current, {
service: service,
mode: '1',
});
await player.load(token);
player.registerSceneHook({
onOrbitRotate: (quaternion) => {
console.log('Current Quaternion', quaternion);
},
onChangePanorama: (panoId) => {
console.log('Current Panorama', panoId);
},
});
return player;
};
React.useEffect(() => {
init().then((p) => setMyPlayer(p));
}, [token]);
return myPlayer;
}
function Player(props) {
const token = '58b51071d0a446318607443bd2135d74';
const target = React.useRef();
const player = usePlayer(token, target);
const style = {
width: '100%',
height: 450,
position: 'relative',
};
return <div ref={target} style={style} />;
}
ReactDOM.render(<Player />, mountNode);
3.2 PanoramaSceneEditor 快速上手
PanoramaSceneEditor 相关,详见第 6 章
-
初始化 PanoramaSceneEditorService 对象, 该对象封装了前后端通信的能力,提供与
后端接口交互 API,详见下一章节。
const service = new PanoramaSceneEditorService();
service.registerFetch(utils.mock);
-
初始化播放器对象
const editor = new PanoramaSceneEditor(document, {
service,
autoSave: false,
});
-
模型加载
await editor.load(token);
-
设置监听事件,详见 5.3 节
editor.registerTagHook({
onSelected: (tag) => {
console.log('tag', tag);
},
});
-
基于 React 的完整代码如下:
import React, { useCallback, useState, useEffect } from 'react';
import {
PanoramaSceneEditor,
PanoramaSceneEditorService,
} from 'alibabacloud-tdsr-js-sdk';
function useEditor(token, target) {
const [editor, setEditor] = useState(null);
const init = async () => {
const service = new PanoramaSceneEditorService();
service.registerHook({
async request(method, url, data, header) {
const prefix = '//localhost/aoding/demo';
return [`${prefix}${url}`, data, header];
},
async response(result, context) {
return {
code: result.code,
message: result.message,
data: result.data,
};
},
});
const editor = new PanoramaSceneEditor(target.current, {
service,
autoSave: false,
});
await editor.load(token);
editor.registerTagHook({
onCreating: () => {
const tag = {
type: 'TEXT',
config: {
backgroundColor: '#000',
content: '444',
title: '1231',
},
};
return tag;
},
onCreated: (tag) => {
console.log(tag);
},
});
return editor;
};
useEffect(() => {
init().then((editor) => {
setEditor(editor);
});
}, [token]);
return editor;
}
function Editor(props) {
const token = 'f9d19ae0541f4131bb787d8a4521d46d';
const target = React.useRef();
const editor = useEditor(token, target);
const addTag = useCallback(() => {
if (!editor) return;
editor.setState(2);
}, [editor]);
const go3D = useCallback(() => {
if (!editor) return;
editor.changeView('DollHouse');
}, [editor]);
const save = useCallback(() => {
if (!editor) return;
editor.save();
}, [editor]);
const publish = useCallback(() => {
if (!editor) return;
editor.publish();
}, [editor]);
return (
<div>
<div
ref={target}
style={{
width: '100%',
height: 450,
position: 'relative',
}}
/>
<button onClick={() => addTag()}>ADD Tag</button>
<button onClick={() => go3D()}>go 3D</button>
<button onClick={() => save()}>save</button>
<button onClick={() => publish()}>publish</button>
</div>
);
}
export default Editor;
4. 通信
SDK 需要依赖后端接口进行模型的加载以及标签的增删改查,考虑到安全性,需要业务
方对依赖接口进行转发,其架构如下图。三维重建服务端把依赖的后端接口通过 Open
API 的方式开放给业务方,业务方可以通过 Open API SDK 方式与这些接口进行交互,并通
过 WEB API 方式转发给 WEB 端的播放器。播放器通过转发后的 API 实现三维重建服务端
通信, 又能保证其安全性,以及各平台兼容性。
PanoramaScenePlayerService、PanoramaSceneEditorService 对象封装了通信能力,开发
者可以通过 const service = new PanoramaSceneEditorService();
和
const service = new PanoramaSceneEditorService();
的方式创建该对象。
-
Mock:为了方便开发者进行本地调试
,PanoramaScenePlayerService、PanoramaSceneEditorService 提供了 Mock 数据的能
力,开发者在开发前期可以通过如下的方式加载 Mock 数据进行本地开发
-
协议适配: 考虑到不同业务方前后端通信协议会有较大差异
,PanoramaScenePlayerService、PanoramaSceneEditorService 开发了协议转换的能力
,用户可以通过 registerHook 接口进行协议转换。比如以下 Code 给 URL 增加前缀,
同时对返回结果进行适配
-
service.registerHook: ({request, response}) => viod;
-
参数说明:
-
request 请求拦截器
-
response 响应拦截器
-
示例
service.registerHook({
async request(method, url, data, header) {
const prefix = '//localhost/biz'
return [`${prefix}${url}`, data, header];
},
async response(result, context) {
return {
code: result.errorCode,
message: result.errorMessage,
data: result.data
}
});
-
API: 以下 API 均为 SDK 内部已封装集成,无需手动调用(域名需业务方自己通过
registerHook 配置),API 内会自动调用服务端接口。接口参数可参考临云镜服务端
SDK 接口文档。
- PanoramaScenePlayerService
> player.load()时,SDK内部调用
- service.getPanoramaConfigData: `(token: string) => response;` 获得全局配置信息,调用接口:`/getWindowConfig`
- service.getSceneData: `(token: string) => response;` 获取场景渲染数据,调用接口:`/getSceneData`
- service.getTags: `(subSceneUuid: number, previewToken: string, type: number) => response;` 获取所有热点标注数据,调用接口:`/getHotspotTag`
- PanoramaSceneEditorService
> tips: 继承 PanoramaScenePlayerService 所有 API,无需手动调用,save()&publish()时自动调用
- service.saveTags: `(subSceneUuid: number, tags: ICombinedTag[]) => response;` 保存标签。tips: ICombinedTag为标签数据,调用接口:`/saveHotspotTag`
- service.publish: `(subSceneUuid: number) => response;` 发布标签,调用接口:`/publishHotspot`
5. Panorama Scene Player
PanoramaScenePlayer 对象提供了基本模型加载,模型交互(如场景漫游,飞入标签,多视
图切换,事件监听等能力)
5.1 Constructor
constructor(target: HTMLDivElement, protected config: IPanoramaScenePlayerConfig)
- target: Player 的 DOM 容器
- config
- service: 前后端通信对象,见 4. 通信 章节内容
- mode:
- Player 的模式,支持 Preview = '2', Online = '1',
- 其中 Preview 加载已经创建,但是还没有发布的标签和已经发布的标签;Online 只
会加载已经发布的标签
- view: DollHouse(3D 模式)\Panorama(全景模式), 默认为 DollHouse 模式
5.2 Load/Unload
5.3 注册监听事件
registerSceneHook(hooks);
- hooks.onLoad:
() => void;
加载完成事件 - hooks.onChangeView:
(type: EnumPanoramaSceneView) => void;
切换全景/3D 模式
事件 - hooks.onChangePanorama:
(panoId: string) => void;
切换子场景事件 - hooks.onOrbitRotate:
(quanertion: TypeQuaternion) => void;
场景旋转, 目前暂
不支持三维空间旋转
5.4 API
player.getTagList()
获得所有标签player.getTagListByPanoramaId(panoId)
获得当前子场景下标签player.getPanoramaList()
获得所有子场景player.changeView(view)
切换视图,支持 DollHouse(3D 模式)\Panorama(全景模
式),player.flyToPanoramaById(panoId)
跳转到指定子场景player.hideTag()
隐藏标签player.showTag()
显示标签player.flyToTag(tagId)
跳转到指定标签位置player.getTagClientPosition(tagId)
获得 Tag 的屏幕坐标player.getCurrentPanorama()
获得当前子场景,若在 Dollhouse(3D)模式下,返
回为 undefinedplayer.isTagInViewScene(tagId)
判断标签是否当前视口可见player.getRotateQuaternion()
获得当前场景旋转四元组player.setRotateQuaternion(quaternion)
设置当前场景的旋转四元组
6. Panorama Scene Editor
PanoramaSceneEditor 继承了 PanoramaScenePlayer 对象,该对象除了提供模型加载,模
型交互,还提供了标签编辑的能力,可以跟进 API 进行标签创建、标签删除、标签修改等
操作。
6.1 Constructor
constructor(target: HTMLDivElement, protected config: IPanoramaSceneEditorConfig)
- target: Player 的 DOM 容器
- config
- service: 前后端通信对象,见 4. 通信 章节内容
- autoSave: 是否自动保存 TAG, 若 autoSave 为 true, 模型创建、模型取消选中、模
型更新、模型拖拽、
- view: DollHouse(3D 模式)\Panorama(全景模式), 默认为 DollHouse 模式
6.2 Load/UnLoad
6.3 注册监听事件
registerTagHook(hooks);
- hooks.onClick:
(tag: ICombinedTag) => void;
点击标签事件 - hooks.onSelected:
(tag: ICombinedTag) => boolean;
标签选中事件 - hooks.onWillUnselected:
(tag: ICombinedTag) => boolean;
标签取消选中事件 - hooks.onCreating:
() => ICombinedTag;
创建标签事件 - hooks.onCreated:
(tag: ICombinedTag) => void;
标签创建完成事件 - hooks.onUpdatePositions:
(tag: ICombinedTagWithScenePosition[]) => void;
标
签位置更新事件 - hooks.onDragStart:
(tag: ICombinedTag) => void;
标签拖拽事件 - hooks.onDragStop:
(tag: ICombinedTag) => void;
标签拖拽事件 - hooks.onDragging:
(tag: ICombinedTag) => void;
标签拖拽事件
6.4 API
PanoramaSceneEditor 继承了 PanoramaScenePlayer 所有 API 除此之外
,PanoramaSceneEditor 还提供了跟标签编辑有关的 API
创建标签目前只能在全景模型下创建,标签创建必须通过 onCreating 事件。
自定义创建 tips(参考 3.2 示例): 1.通过 onCreating 创建默认标签。2.通过
onCreated 拿到标签 Id。3. 通过editor.updateTag(tagId, tag)
方法自定义标签内容
-
通过注册 onCreating 事件回填标签内容
-
tag 参数说明:
-
type: tag 类型支持 IMAGE | TEXT | VIDEO
tips: VIDEO 类型的暂时无法播放!如果有播放需求,需业务方前端额外添加弹
出层,视频在弹出框中播放
-
config: tag 配置
-
formSelectImgType: 'default'(默认) | 'point'(小标) | 'mural'(壁纸); 图片
类型,仅 IMAGE 支持
-
title: string 标题
-
content: string 内容
-
backgroundColor: string 背景色
-
images: string[] 图片 CDN 地址
-
video: string 视频 CDN 地址
-
tag 示例:
-
图片:
{
type: 'IMAGE',
config: {
backgroundColor: '#000', // 背景色
images: [ // 图片cdn地址
'http://s.alicdn.com/@lyj/pano_src/100070-82766/hotspot/image/4bbc-06439_LD.png',
],
title: '22222',
formSelectImgType: 'mural',
},
}
-
文本:
{
type: 'TEXT',
config: {
backgroundColor: '#000',
title: '22222',
content: '11111'
},
}
-
视频:
{
type: 'VIEDO',
config: {
backgroundColor: '#000',
title: '22222',
content: '11111',
video: '' // 视频cdn地址
},
}
-
onCreating 示例:
editor.registerTagHook({
onCreating: () => {
const tag = {
type: 'IMAGE',
config: {
backgroundColor: '#000',
images: [
'http://s.alicdn.com/@lyj/pano_src/100070-82766/hotspot/image/4bbc-06439_LD.png',
],
title: '60152892558',
formSelectImgType: 'mural',
},
};
return tag;
},
});
-
通过 createTagInView(position?: { x: number, y: number })
接口创建标签,默
认在视口中心创建
editor.createTagInView();
-
另外也支持通过鼠标点击方式在指定位置创建标签,开启鼠标点击交互能力
editor.setState(1)
editor.setState(2)
const state = editor.getState()
已知问题
- 暂时无法支持天花板打标
- flyToTag 接口会触发页面闪动问题