Skip to content

第三章 创建应用和场景 ​

目录 ​

第一节 获取已发布的图观场景 ​

如果计划开始对图观场景进行二次开发,您当然需要先具备一个已经发布的图观场景。

如果是图观端场景,可以使用在线图观端渲染场景编辑器完成场景的编辑并且发布。发布之后,场景会生成一个全局唯一的 URL(统一资源定位符),通过查看详情可以复制二次开发地址。这个地址是您在进行 TGAPI 开发时需要传入的。

端渲染二次开发地址

如果是图观流场景,需要将打包好的流场景,上传到图观流渲染服务平台。在添加场景后,同样可以获得访问地址和 token。

流渲染场景访问Token

返回本章目录

第二节 图观统一开发 API 的 SDK 获取和引用 ​

对图观场景进行二次开发的另外一个必备条件,就是图观统一开发 API 的 SDK。SDK 是一个纯 Javascript 的包,分为现在引用和离线引用两种方式。

通常如果您具备较好的在线开发网络条件,推荐使用在线的图观统一开发 API 的 SDK,这样会使用比较新的版本和功能。离线开发包一般要和离线部署的场景服务器配合使用。

以下两种引用方式,您选择一种即可。

1. 使用在线版本的引用 ​

如果您希望使用最新版本的 SDK,可以在 HTML 中添加以下脚本引用。

HTML
<script tgapp src="https://www.tuguan.net/public/tgapp/TGApp.min.js"></script>

如果您希望使用指定版本的 SDK,可以通过如下方法引用。

HTML
<script tgapp src="https://www.tuguan.net/public/tgapp3.3.0/TGApp.min.js"></script>

目前可以使用的指定版本只有 tgapp3.3.

2. 使用离线版本的引用 ​

首选,需要下载最新离线 SDK 包。如果您希望使用其他版本的离线 SDK 可以跳转到历史页面查找。

离线 SDK 包解压后目录和文件如下:

  • scenelib
    • lib
    • texture
    • resource
  • TGApp.min.js
  • TGApp.module.min.js

其中 TGApp.min.js 为 HTML 引入方式文件;TGApp.module.min.js 为import引入方式文件;scene 文件夹中的子文件夹 texture、resource 文件为端渲染使用资源文件,离线开发时需放入到项目工程目录下,并且这个目录的位置需要在初始化端渲染场景时通过resourceBasePath属性传入(属性的默认值是./scene)。流渲染开发不使用 scene 文件夹中的任何文件。

最后,在 HTML 页面中引入您项目工程中对应目录的 TGApp.min.js 文件,例如:

HTML
<script src="./lib/TGApp.min.js"></script>

返回本章目录

第三节 服务、场景和状态的关系 ​

图观的场景加载体系,分为服务、场景和状态三层,他们对应的创建方式如下:

端渲染流渲染
服务 Service对应场景编辑器发布的地址,每个发布的场景对应一个已经发布的服务地址。通过场景服务器上已发布的详情对话框查看二次开发地址。对应流渲染服务器上的一个场景,每个已经发布场景对应一个服务 token。
场景 Scene无对应流渲染应用中的不同关卡。每个关卡可以是完全不同的行政区划、建筑或者模型,具有不同的摄像机初始视角。
状态 State在场景编辑器里通过状态功能创建的。每个状态是当前场景中对于模型显隐、关节定义、动画播放状态、灯光、特效、摄像机默认状态等一些列特性的组合。无

这三层加载体系中的服务Service,是端渲染和流渲染场景都有的特性,都是对应场景服务器上的一个终端服务地址。并且都是可以使用initService,loadService和switchService这三个指令进行操作。在端渲染中,loadService 指令非常有用,它可以在不影响当前前端功能使用的情况下,通过浏览器后台下载一个较大的场景,等下载完成后,再使用switchService指令切换,可以缩短场景的加载时间。对于流渲染来说loadService也有一定的意义,可以预先在服务器上启动渲染进程,当然使用这个指令的前提是,您的流渲染服务器上有足够的可用渲染通道,并且您的流渲染场景是按照多个服务地址发布的。

返回本章目录

第四节 端渲染场景操作 ​

引入版本:3.0 起支持

1. 演示场景 ​

在本教程中,您可以使用您自己的场景,或者使用我们为您提供的一个已经发布好的端渲染场景。

场景地址:https://www.tuguan.net/api/sceneEditor/v1/scenes/ed402a51-a00a-48f2-8b60-f51963440e82/loadAVWS

卡通版效果图

这是一个卡通风格的社区端渲染场景,场景内具备一些已经定义好的状态、动画、建筑功能。

2. 初始化场景 ​

端渲染的场景初始化,是两步式加载。第一步:使用场景 ID 获取 token,第二步,使用场景 ID 和 token,通过initService指令创建场景对象。

JavaScript
// 初始化图观App
var appInstance = new TGApp.App();

// 获取场景Token
function onBodyLoaded() {
    // 场景所在服务器
    let server = "www.tuguan.net";
    // 根据选取不同的场景,将ID替换到下方的变量中。
    let id = "ed402a51-a00a-48f2-8b60-f51963440e82";

    // 定义URL模版
    let sceneUrl = `http://${server}/api/sceneEditor/v1/scenes/${id}/loadAVWS`;
    let tokenUrl = `http://${server}/api/user/v1/visitorScene/${id}`;

    let xhr = new XMLHttpRequest();
    // 设置请求地址和参数
    xhr.open("post", tokenUrl, true);
    // 设置请求头
    xhr.setRequestHeader("Content-Type", "application/json charset=utf-8");
    // 监听readystate状态
    xhr.onreadystatechange = () => {
        if (xhr.readyState == 4 && xhr.status == 200) {
            let result = JSON.parse(xhr.responseText);
            // 请求成功后,初始化地图
            loadScene(sceneUrl, result.accessToken);
        }
    };
    // 发送请求
    xhr.send();
}

function loadScene(url, token) {
    appInstance.initService({
        container: document.getElementById("container"),
        mode: "scene",               // mode 属性的值 scene 代表端渲染模式,streaming 代表流渲染模式
        url: url,                    // 端渲染场景二次开发地址
        resourceBasePath: './scene', // 如果是在线引用TGAPP SDK,这一行应该省略。如果是离线SDK,并且scene 文件夹和 html 文件在同一个目录,这一行同样可以省略。
        token: token                 // 上一步中根据场景获得的token
    }, (e) => {
        console.log(e);
        // 可以在这里,加入在场景初始化完成后的后续操作。
      });
    });
}
</script>

在上方代码的loadScene方法的initService指令回调函数中,或者通过addEventListener订阅onServiceInit事件,执行服务初始化完毕之后的相关逻辑,如操作相机,添加图层等。

3. 获取状态和切换状态 ​

当场景初始化完成后,可以使用 getStates 指令,获取在场景编辑器中已经预置好的状态列表:

JavaScript
// 获取状态列表
appInstance.uniCall('getStates', {}, (result) => {
  console.log(result)
})

返回结果如下:

JavaScript
{
  "result": 1,
  "message": "成功。",
  "states": [
    {
      "name": "默认状态",
      "default": true
    },
    {
      "name": "全景展示",
      "default": false
    },
    {
      "name": "顶视图",
      "default": false
    },
    {
      "name": "闸机",
      "default": false
    },
    {
      "name": "住宅",
      "default": false
    },
    {
      "name": "住宅户型剖分",
      "default": false
    }
  ]
}

在以上返回结果中可以看到,当前场景具有 6 个可用的状态,并且 "default": true 的默认状态是切换到当前场景后默认进入的状态。

使用switchState指令,并且传入希望跳转的状态名称,可以切换场景状态。切换场景的后续的相关接口调用,需要通过监听onStateChanged事件成功后,再继续进行。

JavaScript
// 切换状态
let jsonData = {
  name: '顶视图'                        // 状态名称需要先使用 获取状态信息 接口,获取到状态名称填入此处
}

appInstance.uniCall('switchState', jsonData, (result) => {
  console.log(result)
})

注册状态切换完成事件,如下所示。可以根据状态切换事件,完成后续指令调用。

JavaScript
// 注册状态切换事件

appInstance.uniCall(
  'addEventListener',
  {
    eventName: 'onStateChanged',       // 事件名称 见图观官网统一API开发手册
    callback: (res) => {
                                       // 要注册的事件监听回调函数,可对同一个事件注册多个回调函数。注意这个回调函数对应的是第一个参数表示的事件发生时的回调函数,要和本次 addEventListener 调用完成的回调函数区分开。
      console.log('onStateSwitch', res)
    }               
  },
  (result) => {
    console.log(result)
  }
)

4. 预加载服务和切换服务 ​

当实现业务场景的时候,经常会有场景的层级下钻上卷,例如行政区划的中国 - 省地块 - 市地块,再比如园区全局场景 - 单独建筑内部 - 单独房间。虽然通过状态切换,可以完成单个场景内模型的显隐,表达一定程度的建筑剖分或者远景近景切换,但是如果场景范围面积过大,或者场景的细节太多,多个层级的场景都通过使用状态进行切换,整个场景的尺寸会过大,会影响初始化的加载时间和性能。

这时,您应该考虑将多个层级场景拆分为多个场景服务地址进行发布。例如全局场景 - 单独建筑内部 - 单独房间,每个场景都对应一个发布的场景地址。然后通过图观统一开发 TGAPI 的loadService和switchService进行预加载服务和切换服务。

具体的使用方法是,首先您通过initService照常加载第一个发布的场景服务,例如园区全局场景。在初始化完成后,开始调用loadService指令依次加载后续可能使用到的场景服务。loadService指令只是下载场景服务所需要的资源,并不会影响前端已经加载的场景和交互功能使用。您可以通过监听onServiceLoad事件获得场景后台加载完成的消息,并且当用户点击切换场景时调用switchService指令切换场景服务。这时图观 API 会自动从已经缓存的场景服务中完成场景的加载。

预加载场景的代码如下,这里演示预加载一个已经发布好的建筑内部场景。

JavaScript
// 切换服务
function switchService()
{
    window.appInstance.uniCall(
        'switchService',
        { name: 'inner' },  // 传入initService或者loadService指令所声明的服务name
        (res) => {
            console.log('切换服务完成', res)
        }
    )
}

// 第二步,加载服务
function loadService(sceneUrl, token) {
    // 根据参数加载图观服务
    window.appInstance.uniCall('loadService', {
            name: "inner",                  // 服务名称,用于切换服务时使用
            container: document.getElementById('container'),
            mode: 'scene',                  // scene代表端渲染 streaming代表流渲染
            url:  sceneUrl,                 // 服务地址
            token: token,                   // 上一步根据场景获得的token
            editMode: false                 // 是否开启编辑模式,默认不开启,编辑模式的鼠标交互和非编辑模式的不同
        },
        (res) => {
            console.log(res);
            // 加载完成服务,进行下一步切换服务
            switchService();
        }
    );

    // 监听服务预加载事件
    window.appInstance.uniCall('addEventListener', {
        eventName: 'onServiceLoad',
        callback: (res) => {
            console.log('预加载服务完成', res);
        }
    });
}

// 开始执行,第一步,获取token
function begin()
{
    // 场景所在服务器
    let server = "www.tuguan.net";
    // 根据选取不同的场景,将ID替换到下方的变量中。
    let id = "46b798d7-d0b9-41db-8609-bffe90b0d9bf";

    // 定义URL模版
    let sceneUrl = `https://${server}/api/sceneEditor/v1/scenes/${id}/loadAVWS`;
    let tokenUrl = `https://${server}/api/user/v1/visitorScene/${id}`;

    let xhr = new XMLHttpRequest();
    // 设置请求地址和参数
    xhr.open("post", tokenUrl, true);
    // 设置请求头
    xhr.setRequestHeader("Content-Type", "application/json charset=utf-8");
    // 监听readystate状态
    xhr.onreadystatechange = () => {
        if (xhr.readyState == 4 && xhr.status == 200) {
            let result = JSON.parse(xhr.responseText);
            // 请求成功后,初始化地图
            loadService(sceneUrl, result.accessToken);
        }
    };
    // 发送请求
    xhr.send();
}

// 开始执行
begin();

切换场景的指令调用相对就简单很多了,指令当中的关键参数就是上一步中loadService预加载场景时的场景服务名称。

JavaScript
// 切换服务
function switchService()
{
    window.appInstance.uniCall(
        'switchService',
        { name: 'inner' },  // 传入initService或者loadService指令所声明的场景name
        (res) => {
            console.log('切换服务完成', res)
        }
    )
}

5. 销毁场景 ​

当您不再需要使用已经初始化过的场景,可以通过使用destroy指令销毁场景,及时释放资源。

JavaScript
window.appInstance.uniCall('destroy', {}, res => {
    console.log('destroy', res)
})

返回本章目录

第五节 流渲染场景操作 ​

引入版本:3.1 起支持

1. 服务的初始化和销毁 ​

流渲染场景服务的初始化需要两个关键参数,一个是场景服务的 IP 地址,另外一个是场景的 token。

如果您希望测试流渲染场景开发,在没有部署流渲染服务平台的情况下,可以联系客服人员,申请:在线深圳湾流渲染场景开发地址。

初始化流渲染场景服务的代码如下。和端渲染同样,您可以在initService方法的回调函数中,或者通过订阅onServiceInit事件,获得场景切换成功的消息,并且方可继续调用后续图观 API 指令,例如操控相机,添加图层等。

JavaScript
appInstance.initService(
  {
    name: '默认',
    container: document.getElementById('container'),
    mode: 'streaming',                 // 流渲染填写streaming
    token: 'XXX',                      // token每个场景不同
    url: 'http://192.168.X.X:XXXX',    // 流渲染服务器地址
    editMode: false
  },
  (result) => {
    console.log(result)
  }
);
// 注册地图初始化成功事件
appInstance.uniCall(
    'addEventListener',
    {
        eventName: 'onServiceInit',
        callback: (res) => {
            console.log(res);
        },
    },
    (result) => {
        console.log(result);
    }
);

销毁流场景服务,和端渲染的代码一致。

JavaScript
window.appInstance.uniCall('destroy', {}, res => {
    console.log('destroy', res)
})

2. 获取场景和切换场景 ​

流渲染在一个服务内,会有多个场景。获取场景列表,可以调用getScenesInfo指令,根据返回数据的scenes属性,拿到所有的场景名称,后续可以使用这个名称进行场景切换。

JavaScript
appInstance.uniCall('getScenesInfo', {}, (result) => {
    console.log(result.scenes);          // 场景列表
})

切换场景,切换场景的消息发送出去之后,在新场景中的逻辑应该在监听到场景切换成功之后才去执行

JavaScript
appInstance.uniCall('switchScene', {
    name: "sceneName",                     // 场景名称工
    duration: 2                            // 切换场景过渡的时间长度,会插入渐隐渐显效果,单位:秒
}, (result) => {
    console.log(result); 
})

注册切换场景事件,监听到场景成功,执行其他操作,如相机视角,添加图层等等

JavaScript
appInstance.uniCall(
    'addEventListener',
    {
        eventName: 'onSceneSwitch',
        callback: (res) => {
            console.log(res);
        },
    },
    (result) => {
        console.log(result);
    }
);

3. 设置分辨率和码率 ​

流渲染在完成初始化加载后,可以进一步修改视频流的画面大小和码率、帧率信息。

JavaScript
// 设置分辨率、码率
let jsonData = {
    width: 1920,    // 分辨率的宽,不能超过4096
    height: 1080,   // 分辨率的高,不能超过2160
    qp: 33,         //视频质量参数,范围:10至40,值越小画质越清晰,常用数值范围22-30。只会修改当前会话的参数,设置后即时生效。
    fpsMax: 30      // 最大帧率 60
};
appInstance.uniCall('setResolution', jsonData, result => {
    console.log(result);
});