Native model animation via glTF node INDEX (asset node names unavailable); skip stray 'status' LCD square; downloads: allow during scan + clearer feedback/hint

This commit is contained in:
Pixelplanet committed 2026-10-15 12:44:01 +02:00
1 parent 9a26d25961
commit 3629e10b4a
8 files changed
+33 -17

No files matched your search

+6 -4
View File
@@ -530,7 +530,7 @@ int App::run() {
}
std::string status = browser_.status();
if (browser_.onNetwork() && !browser_.isBusy()) {
status = "Point + Trigger: Stream A: Download";
status = "Trigger: Open/Stream Play button: Download";
}
renderer_.setBrowser(true, browser_.title(), rows,
browser_.selected(), status);
@@ -689,9 +689,11 @@ int App::run() {
}
}
}
if (in.togglePlay) {
browser_.downloadFocused();
}
}
// Download works even while the folder is still scanning; only an
// already-running download blocks a new one (checked internally).
if (browser_.isOpen() && in.togglePlay) {
browser_.downloadFocused();
}
if (browser_.isOpen() && in.recenter && in.headValid) {
renderer_.recenterScreen(in.headPos, in.headForward);
+1
View File
@@ -84,6 +84,7 @@ GltfMesh loadGlb(const std::vector<uint8_t>& glb) {
}
GltfSubmesh sm;
sm.nodeName = (node->name != nullptr) ? node->name : "";
sm.nodeIndex = static_cast<int>(n);
sm.firstIndex = static_cast<uint32_t>(out.indices.size());
for (cgltf_size i = 0; i < prim->indices->count; ++i) {
out.indices.push_back(
+1
View File
@@ -20,6 +20,7 @@ struct GltfSubmesh {
int image = -1; // index into GltfMesh::images, or -1 for untextured
float baseColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
std::string nodeName; // source glTF node, matched to render-model node state
int nodeIndex = -1; // source glTF node index (node states are parallel)
};
// A mesh extracted from a glTF/GLB asset: positions + normals + UVs interleaved,
+11 -10
View File
@@ -1873,16 +1873,13 @@ void VulkanRenderer::renderView(uint32_t viewIndex, uint32_t imageIndex,
Quat qNode{0.0f, 0.0f, 0.0f, 1.0f};
Vec3 pNode{0.0f, 0.0f, 0.0f};
bool visible = true;
if (!sm.nodeName.empty()) {
for (const NodeStateVis& ns : modelNodeStates_) {
if (ns.name == sm.nodeName) {
qNode = Quat{ns.quat[0], ns.quat[1], ns.quat[2],
ns.quat[3]};
pNode = Vec3{ns.pos[0], ns.pos[1], ns.pos[2]};
visible = ns.visible;
break;
}
}
if (sm.nodeIndex >= 0 &&
sm.nodeIndex < static_cast<int>(modelNodeStates_.size())) {
const NodeStateVis& ns =
modelNodeStates_[static_cast<std::size_t>(sm.nodeIndex)];
qNode = Quat{ns.quat[0], ns.quat[1], ns.quat[2], ns.quat[3]};
pNode = Vec3{ns.pos[0], ns.pos[1], ns.pos[2]};
visible = ns.visible;
}
if (!visible) continue;
const Vec3 worldPos = oPlace + rotate(qPlace, pNode);
@@ -3211,10 +3208,14 @@ bool VulkanRenderer::loadControllerModel(const std::vector<uint8_t>& glb) {
}
modelSubmeshes_.reserve(mesh.submeshes.size());
for (const GltfSubmesh& s : mesh.submeshes) {
// The 'status' node maps the whole texture atlas onto a small LCD panel,
// which reads as a stray textured square; skip it.
if (s.nodeName == "status") continue;
ModelSubmesh ms;
ms.firstIndex = s.firstIndex;
ms.indexCount = s.indexCount;
ms.nodeName = s.nodeName;
ms.nodeIndex = s.nodeIndex;
ms.set = (s.image >= 0 &&
static_cast<std::size_t>(s.image) < imageSets.size())
? imageSets[s.image]
+1
View File
@@ -449,6 +449,7 @@ private:
VkDescriptorSet set = VK_NULL_HANDLE; // base-color texture descriptor
float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
std::string nodeName; // matched to an animated render-model node state
int nodeIndex = -1; // glTF node index (node states are parallel to it)
};
VkDescriptorSetLayout modelSetLayout_ = VK_NULL_HANDLE;
VkDescriptorPool modelDescPool_ = VK_NULL_HANDLE;
+8 -3
View File
@@ -318,10 +318,15 @@ bool Browser::downloadFocused() {
return false;
}
const Entry e = entries_[static_cast<std::size_t>(selected_)];
if (!e.isVideo || !inSmb_) {
return false; // Only network videos can be downloaded to local storage.
if (!e.isVideo) {
status_ = "Highlight a video file, then press download";
return false;
}
startDownload(e.path, e.label); // async copy to ~/media, then Play via poll()
if (!inSmb_) {
status_ = "Only network (SMB) videos can be downloaded";
return false;
}
startDownload(e.path, e.label); // async copy to the download folder, then play
return true;
}
+4
View File
@@ -192,6 +192,10 @@ bool RenderModelSystem::nodeStates(XrTime time, std::vector<NodeState>& out) {
}
}
out.resize(st.nodeStateCount);
if (!stateLogged_) {
stateLogged_ = true;
PIXELVR_LOG_INFO("Render model node states: %u nodes", st.nodeStateCount);
}
for (uint32_t i = 0; i < st.nodeStateCount; ++i) {
const XrRenderModelNodeStateEXT& n = nodeStateBuf_[i];
NodeState& o = out[i];
+1
View File
@@ -64,6 +64,7 @@ private:
uint32_t animatableNodeCount_ = 0;
std::vector<std::string> nodeNames_;
std::vector<XrRenderModelNodeStateEXT> nodeStateBuf_; // reused per frame
bool stateLogged_ = false; // one-shot diagnostic
PFN_xrEnumerateInteractionRenderModelIdsEXT pfnEnumerateIds_ = nullptr;
PFN_xrCreateRenderModelEXT pfnCreateModel_ = nullptr;