mirror of
https://github.com/Pixelplanet/Pixel-VR-Player.git
synced 2026-10-23 11:01:37 +02:00
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:
1 parent
9a26d25961
commit
3629e10b4a
8 files changed
+33
-17
No files matched your search
+6
-4
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user