Skip to content

Commit 84f58be

Browse files
committed
[WebRTC] Refactor texture handling in ASSIMP export and improve data channel reliability
1 parent 1a46e95 commit 84f58be

3 files changed

Lines changed: 84 additions & 87 deletions

File tree

cpp/open3d/t/io/file_format/FileASSIMP.cpp

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -566,97 +566,97 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename,
566566
ai_mat->AddProperty(&ac, 1, AI_MATKEY_COLOR_EMISSIVE);
567567
}
568568

569-
// Count texture maps...
569+
// Build a list of texture-embed actions in a single pass. Each action
570+
// is a lambda that writes one texture slot into the assimp scene; the
571+
// slot index is passed at call time. .
572+
//
570573
// NOTE: GLTF2 expects a single combined roughness/metal map. If the
571574
// model has one we just export it, otherwise if both roughness and
572575
// metal maps are available we combine them, otherwise if only one or
573576
// the other is available we just export the one map.
574577
const auto& material = w_mesh.GetMaterial();
575-
int n_textures = 0;
576-
if (HasValidTexture(material, "albedo")) ++n_textures;
577-
if (HasValidTexture(material, "normal")) ++n_textures;
578-
if (HasValidTexture(material, "ambient_occlusion")) ++n_textures;
578+
using TextureAction = std::function<void(int /*slot*/)>;
579+
std::vector<TextureAction> texture_actions;
580+
581+
if (HasValidTexture(material, "albedo")) {
582+
texture_actions.push_back([&](int idx) {
583+
auto img = material.GetAlbedoMap();
584+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
585+
aiTextureType_DIFFUSE, img);
586+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
587+
aiTextureType_BASE_COLOR, img);
588+
});
589+
}
590+
if (HasValidTexture(material, "ambient_occlusion")) {
591+
texture_actions.push_back([&](int idx) {
592+
auto img = material.GetAOMap();
593+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
594+
aiTextureType_LIGHTMAP, img);
595+
});
596+
}
579597
if (HasValidTexture(material, "ao_rough_metal")) {
580-
++n_textures;
598+
texture_actions.push_back([&](int idx) {
599+
auto img = material.GetAORoughnessMetalMap();
600+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
601+
aiTextureType_UNKNOWN, img);
602+
});
581603
} else if (HasValidTexture(material, "roughness") &&
582604
HasValidTexture(material, "metallic")) {
583-
++n_textures;
605+
texture_actions.push_back([&](int idx) {
606+
auto rough = material.GetRoughnessMap().AsTensor();
607+
auto metal = material.GetMetallicMap().AsTensor();
608+
if (rough.GetShape() != metal.GetShape()) {
609+
utility::LogError(
610+
"RoughnessMap (shape={}) and MetallicMap "
611+
"(shape={}) must have the same shape.",
612+
rough.GetShape(), metal.GetShape());
613+
}
614+
auto rows = rough.GetShape(0);
615+
auto cols = rough.GetShape(1);
616+
auto rough_metal = core::Tensor::Full({rows, cols, 4}, 255,
617+
core::Dtype::UInt8);
618+
rough_metal.Slice(2, 2, 3) =
619+
metal.Slice(2, 0, 1); // blue channel is metal
620+
rough_metal.Slice(2, 1, 2) =
621+
rough.Slice(2, 0, 1); // green channel is roughness
622+
geometry::Image rough_metal_img(rough_metal);
623+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
624+
aiTextureType_UNKNOWN,
625+
rough_metal_img);
626+
});
584627
} else {
585-
if (HasValidTexture(material, "roughness")) ++n_textures;
586-
if (HasValidTexture(material, "metallic")) ++n_textures;
628+
if (HasValidTexture(material, "roughness")) {
629+
texture_actions.push_back([&](int idx) {
630+
auto img = material.GetRoughnessMap();
631+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
632+
aiTextureType_UNKNOWN, img);
633+
});
634+
}
635+
if (HasValidTexture(material, "metallic")) {
636+
texture_actions.push_back([&](int idx) {
637+
auto img = material.GetMetallicMap();
638+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
639+
aiTextureType_UNKNOWN, img);
640+
});
641+
}
587642
}
643+
if (HasValidTexture(material, "normal")) {
644+
texture_actions.push_back([&](int idx) {
645+
auto img = material.GetNormalMap();
646+
SetTextureMaterialProperty(ai_mat, ai_scene.get(), idx,
647+
aiTextureType_NORMALS, img);
648+
});
649+
}
650+
651+
int n_textures = static_cast<int>(texture_actions.size());
588652
if (n_textures > 0) {
589653
ai_scene->mTextures = new aiTexture*[n_textures];
590654
for (int i = 0; i < n_textures; ++i) {
591655
ai_scene->mTextures[i] = new aiTexture();
656+
texture_actions[i](i);
592657
}
593658
ai_scene->mNumTextures = n_textures;
594659
}
595-
596-
// Now embed the textures that are available...
597-
int current_idx = 0;
598-
if (HasValidTexture(material, "albedo")) {
599-
auto img = material.GetAlbedoMap();
600-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
601-
aiTextureType_DIFFUSE, img);
602-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
603-
aiTextureType_BASE_COLOR, img);
604-
++current_idx;
605-
}
606-
if (HasValidTexture(w_mesh.GetMaterial(), "ambient_occlusion")) {
607-
auto img = w_mesh.GetMaterial().GetAOMap();
608-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
609-
aiTextureType_LIGHTMAP, img);
610-
++current_idx;
611-
}
612-
if (HasValidTexture(w_mesh.GetMaterial(), "ao_rough_metal")) {
613-
auto img = w_mesh.GetMaterial().GetAORoughnessMetalMap();
614-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
615-
aiTextureType_UNKNOWN, img);
616-
++current_idx;
617-
} else if (HasValidTexture(w_mesh.GetMaterial(), "roughness") &&
618-
HasValidTexture(w_mesh.GetMaterial(), "metallic")) {
619-
auto rough = w_mesh.GetMaterial().GetRoughnessMap().AsTensor();
620-
auto metal = w_mesh.GetMaterial().GetMetallicMap().AsTensor();
621-
if (rough.GetShape() != metal.GetShape()) {
622-
utility::LogError(
623-
"RoughnessMap (shape={}) and MetallicMap (shape={}) "
624-
"must have the same shape.",
625-
rough.GetShape(), metal.GetShape());
626-
}
627-
auto rows = rough.GetShape(0);
628-
auto cols = rough.GetShape(1);
629-
auto rough_metal = core::Tensor::Full({rows, cols, 4}, 255,
630-
core::Dtype::UInt8);
631-
rough_metal.Slice(2, 2, 3) =
632-
metal.Slice(2, 0, 1); // blue channel is metal
633-
rough_metal.Slice(2, 1, 2) =
634-
rough.Slice(2, 0, 1); // green channel is roughness
635-
636-
geometry::Image rough_metal_img(rough_metal);
637-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
638-
aiTextureType_UNKNOWN, rough_metal_img);
639-
++current_idx;
640-
} else {
641-
if (HasValidTexture(material, "roughness")) {
642-
auto img = material.GetRoughnessMap();
643-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
644-
aiTextureType_UNKNOWN, img);
645-
++current_idx;
646-
}
647-
if (HasValidTexture(material, "metallic")) {
648-
auto img = material.GetMetallicMap();
649-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
650-
aiTextureType_UNKNOWN, img);
651-
++current_idx;
652-
}
653-
}
654-
if (HasValidTexture(material, "normal")) {
655-
auto img = material.GetNormalMap();
656-
SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx,
657-
aiTextureType_NORMALS, img);
658-
++current_idx;
659-
}
660660
}
661661
ai_scene->mMaterials[0] = ai_mat;
662662

cpp/open3d/visualization/webrtc_server/WebRTCWindowSystem.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,8 @@ std::string WebRTCWindowSystem::OnDataChannelMessage(
364364
if (impl_->data_channel_message_callbacks_.count(class_name) != 0) {
365365
reply = impl_->data_channel_message_callbacks_.at(class_name)(
366366
message);
367-
// Do not call PostRedrawEvent here. Mouse/keyboard callbacks
368-
// already schedule a redraw via Window::OnMouseEvent → PostRedraw.
369-
// An extra PostRedrawEvent here creates a duplicate draw event
370-
// before the input event is even processed, causing backlog.
367+
// Custom callbacks that mutate GUI state (e.g. add/remove geometry)
368+
// must call window->PostRedraw() or post_redraw() themselves.
371369
return reply;
372370
} else {
373371
reply = fmt::format(

cpp/open3d/visualization/webrtc_server/html/webrtcstreamer.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -699,16 +699,15 @@ let WebRtcStreamer = (function() {
699699
};
700700

701701
// Local datachannel sends data.
702-
// Use unordered, unreliable delivery for mouse/input events. Ordered
703-
// reliable delivery causes head-of-line blocking: a lost packet stalls
704-
// all subsequent events until it is retransmitted. For interactive
705-
// mouse events the latest position is all that matters, so a dropped
706-
// message is better than a delayed one. maxRetransmits: 0 means the
707-
// browser sends once and moves on; ordered: false removes sequencing.
702+
// Use reliable ordered delivery (the default). While unordered +
703+
// unreliable would reduce head-of-line blocking for mouse MOVE/DRAG,
704+
// the same channel carries discrete events (BUTTON_DOWN/UP, key events,
705+
// resize) and application-level RPC (tensorboard/update_geometry etc.)
706+
// that must not be lost or reordered. Mouse coalescing (rAF + server-
707+
// side replace_or_merge_mouse) already prevents event backlog, so the
708+
// extra reliability is free in practice on a local network.
708709
try {
709-
this.dataChannel = pc.createDataChannel(
710-
'ClientDataChannel',
711-
{ordered: false, maxRetransmits: 0});
710+
this.dataChannel = pc.createDataChannel('ClientDataChannel');
712711
var dataChannel = this.dataChannel;
713712
dataChannel.onopen = function() {
714713
console.log('local datachannel open');

0 commit comments

Comments
 (0)