了解如何编写自定义 HTML 覆盖 UI 以进行 VRED Core 流交互。
视频字幕:大家好,欢迎学习这一 VRED Core 教程。我是主持人 Christopher,今天我将介绍如何为 VRED Core 应用程序创建简单的自定义 HTML 界面。
VRED Core 的开发是为了满足各种不同的需求,包括提供渲染工作流程、准备数据或充当专用的实时渲染服务器,涵盖了一系列的用例。由于 VRED Core 本身不提供用户界面,因此我们必须使用 Python Web 界面与它进行交互。
不过,VRED Core 附带了一个流式传输应用程序。这使您能够访问产品演示或设计审阅所需的所有功能。通过流式传输应用程序,可以访问场景中的所有变量集和视点,还可以使用摄影机自由导航。这适用于许多应用程序,此外,VRED Core 还提供了用于自己构建完全自定义的 HTML 用户界面的工具。这意味着,您可以构建一个专门适合您需求的应用程序,并可以与公司设计相结合。
在本教程中,我将向大家介绍如何构建自己的 VRED 应用程序,包括实时视频流、简单的 Python 终端和变量集列表。为与 VRED 建立通信,我们将使用其 Python 端点。
首先,我们创建一个仅包含简单文本段落的 HTML 文档。此文档不能命名为 index.html,因为 VRED 已保留该名称。因此,我们称其为 myapp.html。
接下来,我们需要设置 HTML Web 界面首选项。这可以通过使用命令行参数 editPreferences 启动 VRED Core 来完成。我们切换到 Web 界面首选项选项卡并设定以下设置。我们启用文件访问,并将目录设置为存储自定义应用程序的目录。在底部,我们启用跨域请求。如果将端口号设置为 8888 以外的值,则记录该端口号,因为我们需要使用它来向 VRED 发出 Python 请求。
这些设置将初始化 VRED 的 Web 界面,我们可以通过运行 VRED Core 并使用 localhost 提供的端口和文件 myapp.html 打开浏览器来调整设置。现在,我们应该会看到示例文本,从而知道 Web 界面工作正常。之后,我们可以切换回编辑器,并继续实现我们的应用程序。
首先,我们要实现一个带有导航的实时视频流导航。有多种方法可以做到这一点,但为了简单起见,我们可以使用端点应用程序的 VRED 流提供的流。此端点提供了一个实时视频流以及我们从 VRED Pro 和 Designer 获知的默认导航行为。
我们可以将此端点用作 iframe 的源,并将其配置为涵盖整个页面。为此,我们创建一个脚本,获取我们的文档,添加一些 JavaScript 代码,重新创建 initializeStream 函数,使用流容器按 ID 搜索元素,并将其源设置为流式传输端点。在这里,我们使用的是在 Web 首选项中设置的端口号。
我们在 windowUnload 函数中调用此函数,以确保在加载页面时始终初始化该流。当然,我们还需要一个具有匹配 ID 属性的 iframe 元素,它将显示此视频流。使用一些 CSS,我们可以将 iframe 的大小定义为 100%,并防止任何边框或滚动条可见。
当在浏览器中加载应用程序时,我们会看到我们的流是否正常工作。很好!现在,我们已将实时视频流式传输到 VRED,并可以使用鼠标导航场景。
拥有实时视频流是第一步,当然,我们还希望在应用程序中实现一些交互。在本教程中,我想集成一个简单的 Python 终端,使我们能够将 Python 命令发送到 VRED 以操纵场景。
我们首先添加几个新的 div 容器,使我们能够将 Python 终端放置在视频流上。然后,我们将一个文本输入和一个按钮以及输入元素的内容添加到 JavaScript 函数。我还想显示 VRED 要发送回的任何返回值,因此我在输入字段和按钮下添加该文本区域。我们可以设置这些新 div 容器的样式,以便 Python 界面位于浏览器窗口的右下角。
要发送 Python 命令,我们可以在 JavaScript 区域中创建一个名为 sendAndReceivePython 的新函数。此函数使用 XML HTTP 请求对象将 get 请求发送到 Python 端点 pythoneval2。此端点接受 Python 命令,并以字符串形式返回 Python 操作的结果。
此时,我还想在将来容易更改的单独变量中保存端口和主机。请务必将 Python 命令编码为 URI,以便于 VRED 识别。
我们还使用 successCallback 和 errorCallback 函数以提高灵活性。为了响应 get 请求,将调用 successCallback,如果出错,将调用 errorCallback。
接下来,我们实现函数 executeTerminalCode,该函数实际上是将 Python 命令发送到 VRED,并将结果消息附加到我们的文本区域元素。我们只需在单击提交按钮时调用该函数,并将输入字段的内容作为参数进行传递。当我们正确连接所有内容后,我们应该能够将简单的打印命令发送到 VRED,或从场景请求所有变量集。
变量集列表也会显示在 Python 终端窗口中,因为 VRED 返回变量集列表以及一条返回消息。我们看到,我们可以获得应用程序中所有可用变量集的列表,那么我们为什么不在界面中显示这些变量集并使它们可选择?
我们来创建一个名为 updateVariantSets 的新 JavaScript 函数。在此函数中,我们使用 Python 命令 getVariantSets 调用 sendAndReceivePython 函数,然后为响应提供回调方法。
现在,我们只想将变量集输出到控制台。我们在 windowUnload 函数中调用此函数,重新加载网页,并在浏览器中按 F12 键打开网页的控制台。当重新加载或打开该页面时,现在可以看到当前加载的场景中的变量集的列表。如果没有变量集,您将在此处看到一个空错误。
在这里,我使用 VRED 的 Genesis 示例进行测试,该示例包含各种变量集。我们的应用程序已通过列表形式接收所有变量集,但我们还必须将它们添加到 HTML。下一步,我们将另一个带有 ID 变量集容器的 div 容器添加到 HTML。此容器将保存从 VRED 接收的所有变量集的列表。
回到 updateVariantSets 函数,我们首先必须使用 eval 函数将变量集数组作为实数 JavaScript 数组进行传递,然后使用 getElementID 函数获得对容器的引用。我们可以迭代所有变量集数组,并创建一个 HTML 元素节点,然后将此节点附加到我们的容器。
我们还希望能够单击变量集名称以在 VRED 中选择它。为此,我们可以向变量集节点添加一个 onClick 函数。只要用户单击变量集节点,我们就使用参数 selectVariantSet 和变量集名称调用 sendAndReceive Python 函数。然后,添加一些 CSS,以便将新的变量集列表放置在页面上。这次在左侧。当我们重新加载页面时,左侧会显示变量集列表,当我们单击其中一个变量集时,也会在 VRED 中选中该变量集。
在本教程中,我们借助一些 HTML、JavaScript 和 CSS,从头开始构建了一个完全自定义的流式传输应用程序。VRED Core 提供了多个端点,我们可以使用它们来添加带有导航的实时视频流,并使用 Python 命令在 VRED 中更改我们的请求数据。还有更多的可能性,但我们在这里不一一赘述。
在下一个教程中,我们将使用新的 Web API 扩展 HTML 应用程序并实现更多功能。今天就到这里。感谢您参加本课程,下次见。
下面是教程 5:如何编写自定义 HTML 覆盖 UI 以进行 VRED Core 流交互视频随附的示例 Python 脚本。
要下载这些压缩文件,请单击此处。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Custom App</title>
</head>
<body>
<p>This is my custom VRED App</p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Custom App</title>
<style>
html, body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}
#page {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#stream-container {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script>
function initializeStream() {
document.getElementById("stream-container").src = "http://localhost:8888/apps/VREDStream/index.html";
}
window.onload = function() {
initializeStream();
updateVariantSes();
}
</script>
</head>
<body>
<div id="page">
<iframe id="stream-container" frameborder="0" overflow="hidden" scroll="no"></iframe>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Custom App</title>
<style>
html, body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}
#page {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#stream-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.python-interface {
padding: 1em 2em;
position: absolute;
right: 1em;
bottom: 1em;
background-color: #2d2d2d;
display: flex;
flex-direction: column;
}
.python-send-wrapper {
margin-bottom: 0.5em;
}
.python-send-wrapper > input {
width: 25em;
}
#python-response {
height: 10em;
resize: none;
}
</style>
<script>
var host = "localhost";
var port = "8888";
function initializeStream() {
document.getElementById("stream-container").src = 'http://' + host + ':' + port + '/apps/VREDStream/index.html';
}
function executeTerminalCode(python) {
console.log(python);
sendAndReceivePython(python,
(response) => {
var text = document.getElementById('python-response').value;
text += '\n' + response;
document.getElementById('python-response').value = text;
},
(error) => console.error("Error sending pyhton command.", python)
);
}
function sendAndReceivePython(command, successCallback, errorCallback) {
var http = new XMLHttpRequest();
var url = 'http://' + host + ':' + port + '/pythoneval2?value=' + encodeURI(command);
http.open('GET', url, true);
http.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(this.responseText && successCallback) {
successCallback(this.responseText)
}
}
};
http.onerror = function() {
if(errorCallback) {
errorCallback();
}
}
http.send();
}
window.onload = function() {
initializeStream();
}
</script>
</head>
<body>
<div id="page">
Variant Set Container
<div id="variant-set-container"></div>
Video Stream Container
<iframe id="stream-container" frameborder="0" overflow="hidden" scroll="no"></iframe>
Python Terminal Container
<div class="python-interface">
<div class="python-send-wrapper">
<input id="python-value" type="text" placeholder="Enter your python code..."></input>
<button onclick="executeTerminalCode(document.getElementById('python-value').value)">Send Python</button>
</div>
<textarea id="python-response"></textarea>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Custom App</title>
<style>
html, body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}
#page {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#stream-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.python-interface {
padding: 1em 2em;
position: absolute;
right: 1em;
bottom: 1em;
background-color: #2d2d2d;
display: flex;
flex-direction: column;
}
.python-send-wrapper {
margin-bottom: 0.5em;
}
.python-send-wrapper > input {
width: 25em;
}
textarea#python-response {
height: 10em;
resize: none;
}
</style>
<script>
var host = "localhost";
var port = "8888";
function initializeStream() {
document.getElementById("stream-container").src = 'http://' + host + ':' + port + '/apps/VREDStream/index.html';
}
function updateVariantSets() {
sendAndReceivePython("getVariantSets()",
(response) => {
console.log(response);
}
);
}
function executeTerminalCode(python) {
console.log(python);
sendAndReceivePython(python,
(response) => {
var text = document.getElementById('python-response').value;
text += '\n' + response;
document.getElementById('python-response').value = text;
},
(error) => console.error("Error sending pyhton command.", python)
);
}
function sendAndReceivePython(command, successCallback, errorCallback) {
var http = new XMLHttpRequest();
var url = 'http://' + host + ':' + port + '/pythoneval2?value=' + encodeURI(command);
http.open('GET', url, true);
http.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(this.responseText && successCallback) {
successCallback(this.responseText)
}
}
};
http.onerror = function() {
if(errorCallback) {
errorCallback();
}
}
http.send();
}
window.onload = function() {
initializeStream();
updateVariantSets();
}
</script>
</head>
<body>
<div id="page">
Variant Set Container
<div id="variant-set-container"></div>
Video Stream Container
<iframe id="stream-container" frameborder="0" overflow="hidden" scroll="no"></iframe>
Python Terminal Container
<div class="python-interface">
<div class="python-send-wrapper">
<input id="python-value" type="text" placeholder="Enter your python code..."></input>
<button onclick="executeTerminalCode(document.getElementById('python-value').value)">Send Python</button>
</div>
<textarea id="python-response"></textarea>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Custom App</title>
<style>
html, body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}
#page {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#variant-set-container {
padding: 1em 2em;
position: absolute;
left: 1em;
bottom: 1em;
background-color: #2d2d2d;
display: flex;
flex-direction: column;
height: 20em;
overflow-y: scroll;
color: white;
}
#variant-set-container > div {
padding: 0.1em 0.2em;
background-color: #4b4b4b;
margin-bottom: 0.1em;
cursor: pointer;
}
#stream-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.python-interface {
padding: 1em 2em;
position: absolute;
right: 1em;
bottom: 1em;
background-color: #2d2d2d;
display: flex;
flex-direction: column;
}
.python-send-wrapper {
margin-bottom: 0.5em;
}
.python-send-wrapper > input {
width: 25em;
}
textarea#python-response {
height: 10em;
resize: none;
}
</style>
<script>
var host = "localhost";
var port = "8888";
function initializeStream() {
document.getElementById("stream-container").src = 'http://' + host + ':' + port + '/apps/VREDStream/index.html';
}
function updateVariantSets() {
sendAndReceivePython("getVariantSets()",
(response) => {
var variantSets = eval(response);
if(variantSets) {
// get a reference to our variant set container
var variantSetContainer = document.getElementById('variant-set-container');
// delete variant set list
variantSetContainer.textContent = '';
// add element for each variant set
variantSets.forEach(variantSet => {
var variantSetNode = document.createElement('div');
variantSetNode.innerHTML = variantSet;
variantSetNode.onclick = function() {
sendAndReceivePython("selectVariantSet('"+variantSet+"')");
}
variantSetContainer.appendChild(variantSetNode);
});
}
}
);
}
function executeTerminalCode(python) {
console.log(python);
sendAndReceivePython(python,
(response) => {
var text = document.getElementById('python-response').value;
text += '\n' + response;
document.getElementById('python-response').value = text;
},
(error) => console.error("Error sending pyhton command.", python)
);
}
function sendAndReceivePython(command, successCallback, errorCallback) {
var http = new XMLHttpRequest();
var url = 'http://' + host + ':' + port + '/pythoneval2?value=' + encodeURI(command);
http.open('GET', url, true);
http.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if(this.responseText && successCallback) {
successCallback(this.responseText)
}
}
};
http.onerror = function() {
if(errorCallback) {
errorCallback();
}
}
http.send();
}
window.onload = function() {
initializeStream();
updateVariantSets();
}
</script>
</head>
<body>
<div id="page">
Variant Set Container
<div id="variant-set-container"></div>
Video Stream Container
<iframe id="stream-container" frameborder="0" overflow="hidden" scroll="no"></iframe>
Python Terminal Container
<div class="python-interface">
<div class="python-send-wrapper">
<input id="python-value" type="text" placeholder="Enter your python code..."></input>
<button onclick="executeTerminalCode(document.getElementById('python-value').value)">Send Python</button>
</div>
<textarea id="python-response"></textarea>
</div>
</div>
</body>
</html>