diff --git a/QPanda3D/QPanda3DWidget.py b/QPanda3D/QPanda3DWidget.py index 25a6158c..249fc355 100644 --- a/QPanda3D/QPanda3DWidget.py +++ b/QPanda3D/QPanda3DWidget.py @@ -160,8 +160,8 @@ class QPanda3DWidget(QWidget): def resizeEvent(self, evt): width = evt.size().width() height = evt.size().height() - print(f"width:{width}") - print(f"height:{height}") + #print(f"width:{width}") + #print(f"height:{height}") from Panda3DWorld import resize_buffer #resize_buffer(width, height) diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index 731e5beb..a1862356 100644 --- a/RenderPipelineFile/config/daytime.yaml +++ b/RenderPipelineFile/config/daytime.yaml @@ -17,8 +17,8 @@ control_points: scattering: sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5089595376,0.9690650222],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] sun_color: [[[0.5010435645,0.5818710306],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] - sun_azimuth: [[[0.5000000000,0.0000000000]]] - sun_altitude: [[[0.5000000000,0.9333333333]]] + sun_azimuth: [[[0.5000000000,0.5000000000]]] + sun_altitude: [[[0.5000000000,0.9666666667]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: fog_ramp_size: [[[0.5510597303,0.7409470752]]] diff --git a/core/InfoPanelManager.py b/core/InfoPanelManager.py index 3083408d..9363d567 100644 --- a/core/InfoPanelManager.py +++ b/core/InfoPanelManager.py @@ -1079,6 +1079,135 @@ class InfoPanelManager(DirectObject): property_panel_instance.createHTTPInfoPanel = types.MethodType(createHTTPInfoPanel, property_panel_instance) property_panel_instance.updateHTTPInfoPanel = types.MethodType(updateHTTPInfoPanel, property_panel_instance) + def serializePanelData(self, panel_id): + """序列化面板数据用于保存""" + if panel_id not in self.panels: + return None + + panel_data = self.panels[panel_id] + props = panel_data['properties'] + + # 获取面板类型 + panel_type = "2d" + if panel_data['node'].hasTag("gui_type"): + gui_type = panel_data['node'].getTag("gui_type") + if "3d" in gui_type.lower(): + panel_type = "3d" + + # 构建序列化数据 + serialized_data = { + 'panel_id': panel_id, + 'panel_type': panel_type, + 'position': props.get('position', (0, 0) if panel_type == "2d" else (0, 0, 0)), + 'size': props.get('size', (1.0, 0.6)), + 'bg_color': props.get('bg_color', (0.15, 0.15, 0.15, 0.9)), + 'border_color': props.get('border_color', (0.3, 0.3, 0.3, 1.0)), + 'title_color': props.get('title_color', (1.0, 1.0, 1.0, 1.0)), + 'content_color': props.get('content_color', (0.9, 0.9, 0.9, 1.0)), + 'title': panel_data['title_label'].getText() if 'title_label' in panel_data else "信息面板", + 'content': panel_data[ + 'content_label'].getText() if 'content_label' in panel_data else "" if 'content_node' not in panel_data else + panel_data['content_node'].getText(), + 'font_path': props.get('font', None), + 'bg_image': props.get('bg_image', None), + 'visible': not panel_data['node'].isHidden() + } + + # 添加HTTP面板特有数据 + if panel_id in self.data_sources and 'http_info' in self.data_sources[panel_id]: + serialized_data['http_info'] = self.data_sources[panel_id]['http_info'] + + return serialized_data + + def getAllPanelData(self): + """获取所有面板的序列化数据""" + panel_data_list = [] + for panel_id in self.panels: + data = self.serializePanelData(panel_id) + if data: + panel_data_list.append(data) + return panel_data_list + + def recreatePanelFromData(self, panel_data): + """从序列化数据重新创建面板""" + try: + panel_id = panel_data['panel_id'] + panel_type = panel_data['panel_type'] + position = panel_data['position'] + size = panel_data['size'] + + # 重建面板 + if panel_type == "3d": + panel_node = self.create3DInfoPanel( + panel_id=panel_id, + position=position, + size=size, + bg_color=panel_data.get('bg_color', (0.15, 0.15, 0.15, 0.9)), + border_color=panel_data.get('border_color', (0.3, 0.3, 0.3, 1.0)), + title_color=panel_data.get('title_color', (1.0, 1.0, 1.0, 1.0)), + content_color=panel_data.get('content_color', (0.9, 0.9, 0.9, 1.0)), + visible=panel_data.get('visible', True), + font=panel_data.get('font_path', None), + bg_image=panel_data.get('bg_image', None) + ) + + # 更新内容 + self.update3DPanelContent( + panel_id, + title=panel_data.get('title', '信息面板'), + content=panel_data.get('content', '') + ) + else: + panel_node = self.createInfoPanel( + panel_id=panel_id, + position=(position[0], position[1]) if len(position) >= 2 else (0, 0), + size=size, + bg_color=panel_data.get('bg_color', (0.15, 0.15, 0.15, 0.9)), + border_color=panel_data.get('border_color', (0.3, 0.3, 0.3, 1.0)), + title_color=panel_data.get('title_color', (1.0, 1.0, 1.0, 1.0)), + content_color=panel_data.get('content_color', (0.9, 0.9, 0.9, 1.0)), + visible=panel_data.get('visible', True), + font=panel_data.get('font_path', None), + bg_image=panel_data.get('bg_image', None) + ) + + # 更新内容 + self.updatePanelContent( + panel_id, + title=panel_data.get('title', '信息面板'), + content=panel_data.get('content', '') + ) + + # 设置标签 + if panel_node: + panel_node.setTag("element_type", "info_panel") + panel_node.setTag("is_scene_element", "1") + panel_node.setTag("supports_3d_position_editing", "1") + + # 如果是HTTP面板,重新注册数据源 + if 'http_info' in panel_data: + http_info = panel_data['http_info'] + self.createHTTPInfoPanel( + panel_id=panel_id, + url=http_info['url'], + method=http_info.get('method', 'GET'), + headers=http_info.get('headers', None), + data=http_info.get('data', None), + position=position, + size=size, + update_interval=self.data_sources.get(panel_id, {}).get('interval', + 30.0) if panel_id in self.data_sources else 30.0 + ) + + print(f"✓ 信息面板 {panel_id} 已重建") + return panel_node + + except Exception as e: + print(f"✗ 重建信息面板 {panel_data.get('panel_id', 'unknown')} 失败: {e}") + import traceback + traceback.print_exc() + return None + # 示例数据源函数 def getRealtimeData(): diff --git a/core/selection.py b/core/selection.py index f3d87b37..6f160c1e 100644 --- a/core/selection.py +++ b/core/selection.py @@ -212,7 +212,7 @@ class SelectionSystem: return # 获取边界框的最小和最大点(世界坐标) - print(f"世界边界框: min={minPoint}, max={maxPoint}") + #print(f"世界边界框: min={minPoint}, max={maxPoint}") # 创建线段对象 lines = LineSegs() diff --git a/core/terrain_manager.py b/core/terrain_manager.py index bb7e0e47..6c7f0564 100644 --- a/core/terrain_manager.py +++ b/core/terrain_manager.py @@ -567,3 +567,88 @@ class TerrainManager: except Exception as e: print(f"应用地形纹理时出错: {e}") return False + + def saveTerrainData(self, terrain_info, filename): + """保存地形数据到文件""" + try: + terrain_node = terrain_info['node'] + heightfield = terrain_info['heightfield'] + + # 保存高度图到文件 + if heightfield: + # 创建保存路径 + terrain_dir = os.path.join(os.path.dirname(filename), "terrains") + if not os.path.exists(terrain_dir): + os.makedirs(terrain_dir) + + # 生成唯一的地形文件名 + terrain_filename = f"terrain_{terrain_info.get('name', 'unnamed')}_{int(time.time())}.png" + terrain_path = os.path.join(terrain_dir, terrain_filename) + + # 保存高度图 + heightfield.write(Filename.fromOsSpecific(terrain_path)) + + # 保存地形信息到标签 + terrain_node.setTag("terrain_heightmap_path", terrain_path) + terrain_node.setTag("terrain_scale", str(terrain_info['scale'])) + + print(f"✓ 地形数据已保存: {terrain_path}") + return terrain_path + return None + except Exception as e: + print(f"保存地形数据时出错: {e}") + return None + + def _recreateTerrain(self, terrain_node): + """重新创建地形""" + try: + print(f"重新创建地形: {terrain_node.getName()}") + + # 获取保存的地形信息 + heightmap_path = terrain_node.getTag("terrain_heightmap_path") if terrain_node.hasTag( + "terrain_heightmap_path") else None + scale_str = terrain_node.getTag("terrain_scale") if terrain_node.hasTag("terrain_scale") else "(1, 1, 1)" + + # 解析缩放信息 + try: + scale_str = scale_str.strip("()") + scale_values = [float(x.strip()) for x in scale_str.split(",")] + scale = (scale_values[0], scale_values[1], scale_values[2]) if len(scale_values) >= 3 else (1, 1, 1) + except: + scale = (1, 1, 1) + + # 恢复位置信息 + if terrain_node.hasTag("transform_pos"): + try: + pos_str = terrain_node.getTag("transform_pos") + pos_str = pos_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()') + pos_values = [float(x.strip()) for x in pos_str.split(',')] + if len(pos_values) >= 3: + terrain_node.setPos(pos_values[0], pos_values[1], pos_values[2]) + except Exception as e: + print(f"恢复地形位置失败: {e}") + + # 如果有高度图路径,重新创建地形 + if heightmap_path and os.path.exists(heightmap_path): + # 使用现有方法重新创建地形 + new_terrain = self.createTerrainFromHeightMap(heightmap_path, scale) + if new_terrain: + # 删除旧的地形节点 + if not terrain_node.isEmpty(): + terrain_node.removeNode() + return new_terrain + + # 如果没有高度图或创建失败,创建一个平面地形作为替代 + print("使用平面地形作为替代") + new_terrain = self.createFlatTerrain(size=(scale[0], scale[1]), resolution=129) + if new_terrain: + # 删除旧的地形节点 + if not terrain_node.isEmpty(): + terrain_node.removeNode() + return new_terrain + + return terrain_node + except Exception as e: + print(f"重新创建地形失败: {e}") + return terrain_node + diff --git a/gui/gui_manager.py b/gui/gui_manager.py index 7f7fdfb8..7b4aea60 100644 --- a/gui/gui_manager.py +++ b/gui/gui_manager.py @@ -124,7 +124,7 @@ class GUIManager: # ==================== GUI元素创建方法 ==================== - def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=0.1): + def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=(0.1,0.1,0.1)): """创建2D GUI按钮 - 支持多选创建和GUI父子关系,优化版本""" try: from direct.gui.DirectGui import DirectButton @@ -180,9 +180,23 @@ class GUIManager: text_font=self.world.getChineseFont() if self.world.getChineseFont() else None, rolloverSound=None, clickSound=None, - parent=parent_gui_node # 设置GUI父节点 + parent=parent_gui_node ) + if not hasattr(button,'_tags'): + button._tags = {} + + # button._tags["gui_type"] = "button" + # button._tags["gui_id"] = f"button_{len(self.gui_elements)}" + # button._tags["gui_text"] = text + # button._tags["is_gui_element"] = "1" + # button._tags["is_scene_element"] = "1" + # button._tags["saved_gui_type"] = "button" + # button._tags["gui_element_type"] = "button" + # button._tags["created_by_user"] = "1" + # button._tags["name"] = button_name + # button.setName(button_name) + # 设置节点标签 button.setTag("gui_type", "button") button.setTag("gui_id", f"button_{len(self.gui_elements)}") @@ -190,7 +204,7 @@ class GUIManager: button.setTag("is_gui_element", "1") button.setTag("is_scene_element", "1") # 确保这个标签被设置 button.setTag("saved_gui_type", "button") # 添加这个标签以确保兼容性 - button.setTag("gui_element_type","button") + button.setTag("gui_element_type", "button") button.setTag("created_by_user", "1") button.setTag("gui_parent_type", "gui" if parent_gui_node else "3d") button.setTag("name", button_name) @@ -225,11 +239,11 @@ class GUIManager: return None # 选中最后创建的按钮并更新场景树 - if created_buttons: - last_button, last_qt_item = created_buttons[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_button, last_qt_item) + # if created_buttons: + # last_button, last_qt_item = created_buttons[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_button, last_qt_item) print(f"🎉 总共创建了 {len(created_buttons)} 个GUI按钮") @@ -338,11 +352,11 @@ class GUIManager: return None # 选中最后创建的标签并更新场景树 - if created_labels: - last_label, last_qt_item = created_labels[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_label, last_qt_item) + # if created_labels: + # last_label, last_qt_item = created_labels[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_label, last_qt_item) print(f"🎉 总共创建了 {len(created_labels)} 个GUI标签") @@ -449,11 +463,11 @@ class GUIManager: return None # 选中最后创建的输入框并更新场景树 - if created_entries: - last_entry, last_qt_item = created_entries[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_entry, last_qt_item) + # if created_entries: + # last_entry, last_qt_item = created_entries[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_entry, last_qt_item) print(f"🎉 总共创建了 {len(created_entries)} 个GUI输入框") @@ -530,6 +544,7 @@ class GUIManager: # 如果提供了图像路径,则加载纹理 if image_path: try: + image_node.setTag("image_path", image_path) texture = self.world.loader.loadTexture(image_path) if texture: image_node.setTexture(texture, 1) @@ -581,11 +596,11 @@ class GUIManager: return None # 选中最后创建的按钮并更新场景树 - if created_2dimage: - last_button, last_qt_item = created_2dimage[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_button, last_qt_item) + # if created_2dimage: + # last_button, last_qt_item = created_2dimage[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_button, last_qt_item) print(f"🎉 总共创建了 {len(created_2dimage)} 个GUI按钮") @@ -725,11 +740,11 @@ class GUIManager: return None # 选中最后创建的文本并更新场景树 - if created_texts: - last_text, last_qt_item = created_texts[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_text, last_qt_item) + # if created_texts: + # last_text, last_qt_item = created_texts[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_text, last_qt_item) print(f"🎉 总共创建了 {len(created_texts)} 个3D文本") @@ -833,7 +848,7 @@ class GUIManager: image_node.setTag("gui_type", "3d_image") image_node.setTag("gui_id", f"3d_image_{len(self.gui_elements)}") if image_path: - image_node.setTag("gui_image_path", image_path) + image_node.setTag("image_path", image_path) image_node.setTag("is_gui_element", "1") image_node.setTag("is_scene_element", "1") image_node.setTag("created_by_user", "1") @@ -861,11 +876,11 @@ class GUIManager: return None # 选中最后创建的文本并更新场景树 - if created_3dimage: - last_image, last_qt_item = created_3dimage[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - tree_widget.update_selection_and_properties(last_image, last_qt_item) + # if created_3dimage: + # last_image, last_qt_item = created_3dimage[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # tree_widget.update_selection_and_properties(last_image, last_qt_item) print(f"🎉 总共创建了 {len(created_3dimage)} 个3D文本") @@ -881,7 +896,7 @@ class GUIManager: traceback.print_exc() return None - def createVideoScreen(self, pos=(0, 0, 0), size=0.2, video_path=None): + def createVideoScreen(self, pos=(0, 0, 0), size=1, video_path=None): """创建3D视频播放屏幕 - 添加占位符纹理支持""" try: from panda3d.core import CardMaker, TransparencyAttrib, Texture, TextureStage @@ -904,7 +919,7 @@ class GUIManager: size = float(size) except (ValueError, TypeError): print(f"⚠️ 尺寸参数无效,使用默认值 0.2,原始值: {size}") - size = 0.2 + size = 0.2*5 print(f"📺 开始创建视频屏幕,位置: {pos}, 尺寸: {size}, 视频路径: {video_path}") @@ -1051,12 +1066,12 @@ class GUIManager: return None # 选中最后创建的视频屏幕 - if created_videoscreens: - last_screen_np, last_qt_item = created_videoscreens[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - # 更新选择和属性面板 - tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) + # if created_videoscreens: + # last_screen_np, last_qt_item = created_videoscreens[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # # 更新选择和属性面板 + # tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) print(f"🎉 总共创建了 {len(created_videoscreens)} 个视频屏幕") @@ -1273,7 +1288,6 @@ class GUIManager: def loadVideoFile(self, video_screen, video_path): """为视频屏幕加载新的视频文件""" try: - from panda3d.core import Texture, TextureStage import os if not os.path.exists(video_path): @@ -1431,8 +1445,6 @@ class GUIManager: print(f"⚠️ 尺寸参数无效,使用默认值 0.2,原始值: {size}, 错误: {e}") size = 0.2 - print(f"📺 开始创建2D视频屏幕,位置: {pos}, 尺寸: {size}, 视频路径: {video_path}") - # 获取树形控件 tree_widget = self._get_tree_widget() if not tree_widget: @@ -1459,7 +1471,9 @@ class GUIManager: frameColor=(1, 1, 1, 1), # 默认背景色 pos=(pos[0] * 0.1, 0, pos[1] * 0.1), # 转换为屏幕坐标 parent=parent_node if tree_widget.is_gui_element(parent_node) else self.world.aspect2d, - suppressMouse=True + suppressMouse=True, + + ) video_screen.setName(screen_name) @@ -1467,7 +1481,7 @@ class GUIManager: # 设置透明度支持 video_screen.setTransparency(TransparencyAttrib.MAlpha) - # 设置2D视频屏幕特有的标签 + #设置2D视频屏幕特有的标签 video_screen.setTag("gui_type", "2d_video_screen") video_screen.setTag("gui_id", f"2d_video_screen_{len(self.gui_elements)}") video_screen.setTag("gui_text", f"2D视频屏幕_{len(self.gui_elements)}") @@ -1475,11 +1489,8 @@ class GUIManager: video_screen.setTag("is_scene_element", "1") video_screen.setTag("created_by_user", "1") - # 设置视频路径标签 - if video_path and os.path.exists(video_path): - video_screen.setTag("video_path", video_path) - else: - video_screen.setTag("video_path", "") + video_screen.setTag("video_path", video_path if video_path else "") + print(f"🔧 设置2D视频屏幕标签 - video_path: {video_path if video_path else '空'}") # 关键修改:预先创建一个占位符纹理,为后续视频播放做准备 placeholder_texture = Texture(f"placeholder_video_texture_{len(self.gui_elements)}") @@ -1552,12 +1563,12 @@ class GUIManager: return None # 选中最后创建的视频屏幕 - if created_videoscreens: - last_screen_np, last_qt_item = created_videoscreens[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - # 更新选择和属性面板 - tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) + # if created_videoscreens: + # last_screen_np, last_qt_item = created_videoscreens[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # # 更新选择和属性面板 + # tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) print(f"🎉 总共创建了 {len(created_videoscreens)} 个2D视频屏幕") @@ -1578,7 +1589,10 @@ class GUIManager: try: import os - if not os.path.exists(video_path): + video_screen.setTag("video_path",video_path) + print(f"🔧 更新2D视频屏幕标签 - video_path: {video_path if video_path else '空'}") + + if not video_path or not os.path.exists(video_path): print(f"❌ 2D视频文件不存在: {video_path}") return False @@ -1590,7 +1604,6 @@ class GUIManager: # 保存视频纹理引用 video_screen.setPythonTag("movie_texture", movie_texture) - video_screen.setTag("video_path", video_path) print(f"✅ 成功加载新2D视频: {video_path}") return True @@ -1817,12 +1830,12 @@ class GUIManager: return None # 选中最后创建的球形视频 - if created_spherical_videos: - last_sphere_np, last_qt_item = created_spherical_videos[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - # 更新选择和属性面板 - tree_widget.update_selection_and_properties(last_sphere_np, last_qt_item) + # if created_spherical_videos: + # last_sphere_np, last_qt_item = created_spherical_videos[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # # 更新选择和属性面板 + # tree_widget.update_selection_and_properties(last_sphere_np, last_qt_item) print(f"🎉 总共创建了 {len(created_spherical_videos)} 个球形视频") @@ -2022,12 +2035,12 @@ class GUIManager: return None # 选中最后创建的虚拟屏幕 - if created_screens: - last_screen_np, last_qt_item = created_screens[-1] - if last_qt_item: - tree_widget.setCurrentItem(last_qt_item) - # 更新选择和属性面板 - tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) + # if created_screens: + # last_screen_np, last_qt_item = created_screens[-1] + # if last_qt_item: + # tree_widget.setCurrentItem(last_qt_item) + # # 更新选择和属性面板 + # tree_widget.update_selection_and_properties(last_screen_np, last_qt_item) print(f"🎉 总共创建了 {len(created_screens)} 个虚拟屏幕") diff --git a/main.py b/main.py index 6b11ce53..9bca08bc 100644 --- a/main.py +++ b/main.py @@ -233,9 +233,9 @@ class MyWorld(CoreWorld): """创建3D图片""" return self.gui_manager.createGUI3DImage(pos,text,size) - def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2): + def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=1): """创建2D GUI图片""" - return self.gui_manager.createGUI2DImage(pos, image_path, size) + return self.gui_manager.createGUI2DImage(pos, image_path, size*0.2) def createVideoScreen(self,pos=(0,0,0),size=1,video_path=None): """创建视频屏幕""" @@ -492,10 +492,6 @@ class MyWorld(CoreWorld): """异步导入模型""" return self.scene_manager.importModelAsync(filepath) - def loadAnimatedModel(self, model_path, anims=None): - """加载带动画的模型""" - return self.scene_manager.loadAnimatedModel(model_path, anims) - # 材质和几何体处理方法 - 代理到scene_manager def processMaterials(self, model): """处理模型材质""" diff --git a/scene/scene_manager.py b/scene/scene_manager.py index 261b8e73..fa4bc075 100644 --- a/scene/scene_manager.py +++ b/scene/scene_manager.py @@ -17,6 +17,7 @@ from panda3d.core import ( import json import aiohttp import asyncio +import inspect from pathlib import Path from panda3d.egg import EggData, EggVertexPool from direct.actor.Actor import Actor @@ -97,6 +98,7 @@ class SceneManager: normalize_scales: 是否标准化子节点缩放(推荐开启) auto_convert_to_glb: 是否自动将非GLB格式转换为GLB以获得更好的动画支持 """ + try: print(f"\n=== 开始导入模型: {filepath} ===") print(f"单位转换: {'开启' if apply_unit_conversion else '关闭'}") @@ -144,18 +146,39 @@ class SceneManager: model.setTag("converted_from", os.path.splitext(original_filepath)[1]) model.setTag("converted_to_glb", "true") - # 可选的单位转换(主要针对FBX) - if apply_unit_conversion and filepath.lower().endswith('.fbx'): - print("应用FBX单位转换(厘米到米)...") - self._applyUnitConversion(model, 0.01) + # file_extension = os.path.splitext(filepath)[1].lower() + # if file_extension == '.fbx': + # print("应用FBX特定缩放 (0.01)...") + # self._applyModelScale(model, 0.01) + # model.setTag("format_scale_applied", "fbx_0.01") + # current_hpr = model.getHpr() + # model.setHpr(current_hpr.getX(), 90, current_hpr.getZ()) + # elif file_extension == '.glb': + # print("应用GLB特定缩放 (100)...") + # self._applyModelScale(model, 100.0) + # model.setTag("format_scale_applied", "glb_100") + # else: + # print(f"应用默认缩放 (1.0) 到 {file_extension} 格式...") + # self._applyModelScale(model, 1.0) + # model.setTag("format_scale_applied", f"{file_extension}_1.0") - # 智能缩放标准化(处理FBX子节点的大缩放值) - if normalize_scales and filepath.lower().endswith('.fbx'): - print("标准化FBX模型缩放层级...") - self._normalizeModelScales(model) + # 可选的单位转换(主要针对FBX) + # if apply_unit_conversion and filepath.lower().endswith('.fbx'): + # print("应用FBX单位转换(厘米到米)...") + # self._applyUnitConversion(model, 0.01) + + # # 可选的单位转换(主要针对FBX) + # if apply_unit_conversion and filepath.lower().endswith('.fbx'): + # print("应用FBX单位转换(厘米到米)...") + # self._applyUnitConversion(model, 0.01) + # + # # 智能缩放标准化(处理FBX子节点的大缩放值) + # if normalize_scales and filepath.lower().endswith('.fbx'): + # print("标准化FBX模型缩放层级...") + # self._normalizeModelScales(model) # 调整模型位置到地面 - self._adjustModelToGround(model) + #self._adjustModelToGround(model) # 创建并设置基础材质 print("\n=== 开始设置材质 ===") @@ -211,6 +234,35 @@ class SceneManager: print(f"导入模型失败: {str(e)}") return None + def _applyModelScale(self, model, scale_factor): + """应用模型特定缩放 + + Args: + model: 要缩放的模型 + scale_factor: 缩放因子 + """ + try: + print(f"应用模型缩放因子: {scale_factor}") + + # 获取当前边界用于后续位置调整 + original_bounds = model.getBounds() + + # 应用缩放 + model.setScale(scale_factor) + + # 重新调整位置(因为缩放会影响边界) + if original_bounds and not original_bounds.isEmpty(): + new_bounds = model.getBounds() + min_point = new_bounds.getMin() + ground_offset = -min_point.getZ() + model.setZ(ground_offset) + print(f"缩放后重新调整位置: Z偏移 = {ground_offset}") + + print(f"模型缩放完成,缩放因子: {scale_factor}") + + except Exception as e: + print(f"应用模型缩放失败: {str(e)}") + def _applyMaterialsToModel(self, model): """递归应用材质到模型的所有GeomNode""" @@ -554,59 +606,6 @@ class SceneManager: except Exception as e: print(f"异步加载模型失败: {str(e)}") - def loadAnimatedModel(self, model_path, anims=None, auto_play=True): - """加载带动画的模型 - - Args: - model_path: 模型文件路径 - anims: 动画字典,格式为 {"动画名": "动画文件路径"} - auto_play: 是否自动播放第一个动画 - """ - try: - print(f"🎬 加载动画模型: {model_path}") - - # 如果没有指定动画,尝试自动检测 - if anims is None: - anims = self._detectAnimations(model_path) - - # 创建Actor对象 - actor = Actor(model_path, anims) - if actor: - actor.reparentTo(self.world.render) - - # 设置碰撞检测 - self.setupCollision(actor) - - # 获取可用的动画列表 - available_anims = actor.getAnimNames() - print(f"📋 检测到动画: {available_anims}") - - # 自动播放第一个动画 - if auto_play and available_anims: - first_anim = available_anims[0] - actor.loop(first_anim) - print(f"▶️ 自动播放动画: {first_anim}") - - # 添加动画控制标签 - actor.setTag("animated", "true") - actor.setTag("current_anim", first_anim) - actor.setTag("available_anims", str(available_anims)) - - # 调整模型位置(让它站在地面上) - self._adjustModelPosition(actor) - - self.models.append(actor) - # 更新场景树 - self.updateSceneTree() - - print(f"✅ 动画模型加载完成: {actor.getName()}") - return actor - except Exception as e: - print(f"❌ 加载动画模型失败: {str(e)}") - import traceback - traceback.print_exc() - return None - # ==================== 材质和几何体处理 ==================== def processMaterials(self, model): @@ -724,8 +723,135 @@ class SceneManager: # ==================== 场景保存和加载 ==================== + def _collectGUIElementInfo(self, gui_node): + """收集GUI元素的信息用于保存""" + try: + # 获取GUI元素类型 + gui_type = "unknown" + if hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_type"): + gui_type = gui_node.getTag("gui_type") + elif hasattr(gui_node, 'hasTag') and gui_node.hasTag("saved_gui_type"): + gui_type = gui_node.getTag("saved_gui_type") + else: + # 尝试从节点名称推断类型 + name_lower = gui_node.getName().lower() + if "button" in name_lower: + gui_type = "button" + elif "label" in name_lower: + gui_type = "label" + elif "entry" in name_lower: + gui_type = "entry" + elif "image" in name_lower: + gui_type = "2d_image" + elif "videoscreen" in name_lower: + if "2d" in name_lower: + gui_type = "2d_video_screen" + else: + gui_type = "video_screen" + else: + # 如果无法识别类型,跳过该元素 + print(f"跳过无法识别类型的GUI元素: {gui_node.getName()}") + return None + + gui_info = { + "name": gui_node.getName(), + "type": gui_type, + "position": list(gui_node.getPos()), + "scale": list(gui_node.getScale()), + "tags": {} + } + + # 收集所有标签(仅对NodePath类型的对象) + if hasattr(gui_node, 'getTagNames'): + for tag in gui_node.getTagNames(): + gui_info["tags"][tag] = gui_node.getTag(tag) + elif hasattr(gui_node, 'getTags'): # 对于DirectGUI对象 + # DirectGUI对象使用不同的方法存储标签 + if hasattr(gui_node, '_tags'): + gui_info["tags"] = gui_node._tags.copy() + + # 根据类型收集特定信息 + if gui_type == "button": + if hasattr(gui_node, 'get'): # DirectButton + gui_info["text"] = gui_node.get() + elif hasattr(gui_node, 'getText'): # 其他类型 + gui_info["text"] = gui_node.getText() + elif hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_text"): + gui_info["text"] = gui_node.getTag("gui_text") + elif gui_type == "label": + if hasattr(gui_node, 'getText'): + gui_info["text"] = gui_node.getText() + elif hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_text"): + gui_info["text"] = gui_node.getTag("gui_text") + elif gui_type == "entry": + if hasattr(gui_node, 'get'): + gui_info["text"] = gui_node.get() + elif hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_text"): + gui_info["text"] = gui_node.getTag("gui_text") + elif gui_type == "2d_image": + if hasattr(gui_node, 'hasTag') and gui_node.hasTag("image_path"): + gui_info["image_path"] = gui_node.getTag("image_path") + elif hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_image_path"): + gui_info["image_path"] = gui_node.getTag("gui_image_path") + elif gui_type == "3d_text": + if hasattr(gui_node,'hasTag') and gui_node.hasTag("gui_text"): + gui_info["text"] = gui_node.getTag("gui_text") + elif hasattr(gui_node,'node') and hasattr(gui_node.node(),'getText'): + gui_info["text"] = gui_node.node().getText() + elif gui_type == "3d_image": + if hasattr(gui_node,'hasTag') and gui_node.hasTag("gui_image_path"): + gui_info["image_path"] = gui_node.getTag("gui_image_path") + elif gui_type == "video_screen": + if hasattr(gui_node,'hasTag') and gui_node.hasTag("video_path"): + gui_info["video_path"] = gui_node.getTag("video_path") + elif gui_type == "2d_video_screen": + if hasattr(gui_node, 'hasTag') and gui_node.hasTag("video_path"): + gui_info["video_path"] = gui_node.getTag("video_path") + else: + print(f"无法保存2D视频屏幕: {gui_node.getName()}") + if hasattr(gui_node,'hasTag'): + video_path = gui_node.getTag("video_path") + try: + print(f"正在保存2D视频路径: {video_path}") + except Exception as e: + print(f"保存2D视频屏幕失败: {e}") + + elif gui_type == "virtual_screen": + if hasattr(gui_node, 'hasTag') and gui_node.hasTag("gui_text"): + gui_info["text"] = gui_node.getTag("gui_text") + elif gui_type == "info_panel": + if hasattr(gui_node, 'hasTag') and gui_node.hasTag("panel_data"): + gui_info["panel_data"] = gui_node.getTag("panel_data") + + if hasattr(self.world, 'script_manager') and self.world.script_manager: + script_manager = self.world.script_manager + # 获取挂载在此节点上的所有脚本 + scripts = script_manager.get_scripts_on_object(gui_node) + if scripts: + gui_info["scripts"] = [] + for script_component in scripts: + script_name = script_component.script_name # 使用脚本组件的名称 + # 获取脚本类的文件路径 + script_class = script_component.script_instance.__class__ + try: + script_file = inspect.getfile(script_class) + except: + script_file = "" + gui_info["scripts"].append({ + "name": script_name, + "file": script_file + }) + + print(f"成功收集GUI元素信息: {gui_info}") + return gui_info + except Exception as e: + print(f"收集GUI元素信息失败: {e}") + import traceback + traceback.print_exc() + return None + def saveScene(self, filename): - """保存场景到BAM文件 - 完整版,支持GUI元素""" + """保存场景到BAM文件 - 完整版,支持GUI元素,地形""" try: print(f"\n=== 开始保存场景到: {filename} ===") @@ -764,7 +890,7 @@ class SceneManager: all_nodes.extend(self.Spotlight) all_nodes.extend(self.Pointlight) - # 添加GUI元素节点(关键修改) + # 添加GUI元素节点 gui_elements = [] if hasattr(self.world, 'gui_elements'): # 过滤掉空的或重复的GUI元素 @@ -773,14 +899,35 @@ class SceneManager: for elem in self.world.gui_elements: if elem and not elem.isEmpty(): - elem_name = elem.getName() if hasattr(elem, 'getName') else str(id(elem)) - if elem_name not in seen_names: + if not elem.isEmpty() and elem.getName() not in seen_names: unique_gui_elements.append(elem) - seen_names.add(elem_name) - + seen_names.add(elem.getName()) gui_elements = unique_gui_elements - all_nodes.extend(gui_elements) - print(f"找到 {len(gui_elements)} 个唯一的GUI元素待保存") + + print(f"保存时GUI元素列表=>>>>>>>>>>>>{self.world.gui_elements}") + all_nodes.extend(gui_elements) + + # 创建用于保存GUI信息的JSON文件路径 + gui_info_file = filename.replace('.bam', '_gui.json') + + # 收集GUI元素信息(排除3D文本和3D图像) + gui_data = [] + for gui_node in gui_elements: + gui_info = self._collectGUIElementInfo(gui_node) + if gui_info: + gui_data.append(gui_info) + print(f"添加GUI信息{gui_info['name']}") + + # 保存GUI信息到JSON文件(确保即使没有GUI元素也创建有效的空JSON数组) + try: + import json + with open(gui_info_file, 'w', encoding='utf-8') as f: + json.dump(gui_data, f, ensure_ascii=False, indent=2) + print(f"✓ GUI信息已保存到: {gui_info_file}") + except Exception as e: + print(f"✗ 保存GUI信息失败: {e}") + import traceback + traceback.print_exc() # 添加tilesets节点 for tileset_info in self.tilesets: @@ -792,7 +939,6 @@ class SceneManager: if tileset_info.get('node') and not tileset_info['node'].isEmpty(): all_nodes.append(tileset_info['node']) - # 保存所有节点的信息 for node in all_nodes: if node.isEmpty(): @@ -821,12 +967,6 @@ class SceneManager: if material.hasBaseColor(): node.setTag("material_basecolor", str(material.getBaseColor())) - # 如果有颜色属性,保存为标签 - # if state.hasAttrib(ColorAttrib.getClassType()): - # color_attrib = state.getAttrib(ColorAttrib.getClassType()) - # if not color_attrib.isOff(): - # node.setTag("color", str(color_attrib.getColor())) - # 保存特定类型节点的额外信息 if node.hasTag("light_type"): # 保存光源特定信息 @@ -848,14 +988,6 @@ class SceneManager: node.getTag("saved_gui_type") if node.hasTag("saved_gui_type") else "unknown" node.setTag("saved_gui_type", gui_type) - # 特别记录3D文本的内容 - if gui_type == "3d_text": - text_content = "未知内容" - if node.hasTag("gui_text"): - text_content = node.getTag("gui_text") - elif hasattr(node.node(), 'getText'): - text_content = node.node().getText() - print(f"保存3D文本信息: {node.getName()} (内容: '{text_content}')") # 保存GUI元素的通用属性 if hasattr(node, 'getPythonTag'): @@ -866,8 +998,15 @@ class SceneManager: node.setTag(f"python_tag_{tag_name}", str(tag_value)) except: pass - print(f"保存GUI元素信息: {node.getName()} (类型: {gui_type})") - print(f"11111111111111{self.world.gui_elements}") + elif node.hasTag("element_type") and node.getTag("element_type") == "info_panel": + # 保存信息面板特定信息 + print(f"保存信息面板信息: {node.getName()}") + panel_id = node.getTag("panel_id") if node.hasTag("panel_id") else node.getName() + if hasattr(self.world, 'info_panel_manager'): + panel_data = self.world.info_panel_manager.serializePanelData(panel_id) + if panel_data: + import json + node.setTag("info_panel_data", json.dumps(panel_data, ensure_ascii=False)) try: print("--- 打印当前场景图 (render) ---") @@ -876,7 +1015,6 @@ class SceneManager: # 保存场景 success = self.world.render.writeBamFile(Filename.fromOsSpecific(filename)) - #success_2d = self.world.render2d.writeBamFile(Filename.fromOsSpecific(filename)) if success: print(f"✓ 场景保存成功: {filename}") @@ -903,7 +1041,7 @@ class SceneManager: return False def loadScene(self, filename): - """从BAM文件加载场景 - 修复版""" + """从BAM文件加载场景""" try: print(f"\n=== 开始加载场景: {filename} ===") @@ -962,6 +1100,8 @@ class SceneManager: gui.removeNode() self.world.gui_elements.clear() + if hasattr(self.world,'info_panel_manager'): + self.world.info_panel_manager.removeAllPanels() # 清理可能存在的辅助节点 self._cleanupAuxiliaryNodes() @@ -977,7 +1117,6 @@ class SceneManager: # 用于存储处理后的灯光节点,避免重复处理 processed_lights = [] # 用于存储处理后的GUI元素,避免重复处理 - processed_gui_elements = [] # 遍历场景中的所有节点 def processNode(nodePath, depth=0): @@ -1016,7 +1155,8 @@ class SceneManager: nodePath.hasTag("light_type") or nodePath.hasTag("gui_type") or # 检查gui_type标签 nodePath.hasTag("is_gui_element") or - nodePath.hasTag("saved_gui_type") + nodePath.hasTag("saved_gui_type") or + (nodePath.hasTag("element_type") and nodePath.getTag("element_type") == "info_panel") ) # 特殊处理:检查节点名称是否包含GUI相关关键词 @@ -1150,52 +1290,6 @@ class SceneManager: self.tilesets.append(tileset_info) self.cesium_integration.tilesets[nodePath.getName()] = tileset_info - elif nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element") or nodePath.hasTag( - "saved_gui_type"): - # 获取GUI元素类型 - gui_type = "unknown" - if nodePath.hasTag("gui_type"): - gui_type = nodePath.getTag("gui_type") - elif nodePath.hasTag("saved_gui_type"): - gui_type = nodePath.getTag("saved_gui_type") - else: - gui_type = "unknown_gui_element" - - print(f"{indent}检测到GUI元素类型: {gui_type}") - - # 检查是否已经处理过这个GUI元素 - if nodePath not in processed_gui_elements: - # 重新创建GUI元素 - recreate_success = self._recreateGUIElement(nodePath) - - # 确保GUI元素被正确标记 - if not nodePath.hasTag("is_scene_element"): - nodePath.setTag("is_scene_element", "1") - - # 检查是否已经存在于gui_elements列表中,避免重复添加 - already_in_list = False - if hasattr(self.world, 'gui_elements'): - # 检查节点是否已经在列表中(通过名称或对象引用) - for existing_gui in self.world.gui_elements: - if existing_gui == nodePath or (hasattr(existing_gui, 'getName') and - hasattr(nodePath, 'getName') and - existing_gui.getName() == nodePath.getName()): - already_in_list = True - break - - # 只有当元素不在列表中时才添加 - if not already_in_list: - if hasattr(self.world, 'gui_elements'): - self.world.gui_elements.append(nodePath) - print( - f"{indent}GUI元素已添加到管理列表,当前列表大小: {len(self.world.gui_elements)}") - else: - print(f"{indent}GUI元素已存在于管理列表中,跳过添加") - - # 标记为已处理 - processed_gui_elements.append(nodePath) - print(f"{indent}GUI元素已处理并标记") - # 将节点重新挂载到render下(如果需要) # 注意:GUI元素可能需要挂载到特定的父节点上 if nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element"): @@ -1221,6 +1315,31 @@ class SceneManager: print("\n开始处理场景节点...") processNode(scene) + # 加载GUI信息并重新创建非3D的GUI元素 + gui_info_file = filename.replace('.bam', '_gui.json') + if os.path.exists(gui_info_file): + try: + with open(gui_info_file, 'r', encoding='utf-8') as f: + content = f.read().strip() + if content: # 检查文件是否为空 + import json + gui_data = json.loads(content) + print(f"✓ 成功加载GUI信息文件: {gui_info_file}") + print(f" 发现 {len(gui_data)} 个GUI元素需要重建") + + # 使用gui_manager重新创建GUI元素 + self._recreateGUIElementsFromData(gui_data) + else: + print("ℹ️ GUI信息文件为空") + except json.JSONDecodeError as e: + print(f"✗ GUI信息文件格式错误: {e}") + except Exception as e: + print(f"✗ 加载GUI信息失败: {e}") + import traceback + traceback.print_exc() + else: + print("ℹ️ 未找到GUI信息文件") + # 移除临时场景节点 if not scene.isEmpty(): scene.removeNode() @@ -1245,6 +1364,181 @@ class SceneManager: traceback.print_exc() return False + def _recreateGUIElementsFromData(self, gui_data): + """根据保存的GUI数据重新创建GUI元素""" + try: + gui_manager = getattr(self.world, 'gui_manager', None) + if not gui_manager: + print("GUI管理器未找到,无法重建GUI元素") + return + + print(f"开始重建 {len(gui_data)} 个GUI元素...") + + # 用于跟踪已处理的元素名称,防止重复创建 + processed_names = set() + + for i, gui_info in enumerate(gui_data): + try: + gui_type = gui_info.get("type", "unknown") + name = gui_info.get("name", f"gui_element_{i}") + position = gui_info.get("position", [0, 0, 0]) + scale = gui_info.get("scale", [1, 1, 1]) + tags = gui_info.get("tags", {}) + text = gui_info.get("text", "") + image_path = gui_info.get("image_path", "") + video_path = gui_info.get("video_path","") + + # 检查是否已经处理过同名元素 + if name in processed_names: + print(f"跳过重复元素: {name}") + continue + + processed_names.add(name) + + print(f"重建GUI元素: {name} (类型: {gui_type})") + print(f" 位置: {position}") + print(f" 缩放: {scale}") + print(f" 文本: {text}") + print(f" 图像路径: {image_path}") + print(f"视频路径:{video_path}") + + # 根据类型创建相应的GUI元素 + new_element = None + + if gui_type == "button" and hasattr(gui_manager, 'createGUIButton'): + new_element = gui_manager.createGUIButton( + pos=tuple(position), + text=text, + size=scale[0] if scale and len(scale) > 0 else 1.0 + ) + elif gui_type == "label" and hasattr(gui_manager, 'createGUILabel'): + scale_value = scale[0] if scale and len(scale) > 0 else 1.0 + new_element = gui_manager.createGUILabel( + pos=tuple(position), + text=text, + size=scale_value + ) + elif gui_type == "entry" and hasattr(gui_manager, 'createGUIEntry'): + new_element = gui_manager.createGUIEntry( + pos=tuple(position), + placeholder=text, + size=scale[0] if scale and len(scale) > 0 else 1.0 + ) + elif gui_type == "2d_image" and hasattr(gui_manager, 'createGUI2DImage'): + scale_value = scale[0] if scale and len(scale) > 0 else 0.2 + new_element = gui_manager.createGUI2DImage( + pos=tuple(position), + image_path=image_path, + size=scale_value*0.2 + ) + elif gui_type == "3d_text" and hasattr(gui_manager,'createGUI3DText'): + size = scale[0] if scale and len(scale) > 0 else 0.5 + new_element = gui_manager.createGUI3DText( + pos=tuple(position), + text=text, + size=size + ) + elif gui_type == "3d_image" and hasattr(gui_manager, 'createGUI3DImage'): + # 处理3D图像 + # 根据缩放值的数量处理尺寸 + if len(scale) >= 3: + size = (scale[0] * 2, scale[1] * 2) + elif len(scale) >= 2: + size = (scale[0] * 2, scale[1] * 2) + elif len(scale) >= 1: + size = (scale[0] * 2, scale[0] * 2) + else: + size = (1.0, 1.0) + + new_element = gui_manager.createGUI3DImage( + pos=tuple(position), + image_path=image_path, + size=size + ) + elif gui_type == "video_screen" and hasattr(gui_manager,'createVideoScreen'): + new_element = gui_manager.createVideoScreen( + pos=tuple(position), + size=scale, + video_path=video_path + ) + if video_path and new_element and hasattr(gui_manager,'loadVideoFile'): + # 延迟一帧执行,确保节点完全初始化 + from direct.task.TaskManagerGlobal import taskMgr + def load_video_task(task): + gui_manager.loadVideoFile(new_element, video_path) + return task.done + + taskMgr.doMethodLater(0.1, load_video_task, 'loadVideoTask') + + elif gui_type == "2d_video_screen" and hasattr(gui_manager,'createGUI2DVideoScreen'): + new_element = gui_manager.createGUI2DVideoScreen( + pos=tuple(position), + size=scale, + video_path=video_path + ) + + + # 如果创建成功,设置属性 + if new_element: + # 如果返回的是列表(多选创建),取第一个 + if isinstance(new_element, list): + new_element = new_element[0] + + # 设置名称 + new_element.setName(name) + + # 设置变换 + new_element.setPos(*position) + + if len(scale) >= 3: + new_element.setScale(scale[0], scale[1], scale[2]) + elif len(scale) >= 1: + new_element.setScale(scale[0]) + + # 设置标签 + # 对于NodePath对象 + if hasattr(new_element, 'setTag'): + for tag_name, tag_value in tags.items(): + # 跳过变换标签,因为我们已经设置了 + if tag_name not in ["transform_pos", "transform_hpr", "transform_scale"]: + new_element.setTag(tag_name, tag_value) + # 对于DirectGUI对象,使用自定义标签存储 + elif hasattr(new_element, '_tags'): + new_element._tags.update(tags) + + # 重新挂载脚本(如果有的话) + if "scripts" in gui_info and hasattr(self.world, + 'script_manager') and self.world.script_manager: + script_manager = self.world.script_manager + for script_info in gui_info["scripts"]: + script_name = script_info["name"] + # 检查脚本是否已加载,如果没有则尝试加载 + if script_name not in script_manager.loader.script_classes: + script_file = script_info.get("file", "") + if script_file and os.path.exists(script_file): + script_manager.load_script_from_file(script_file) + + # 为元素添加脚本 + script_manager.add_script_to_object(new_element, script_name) + + + print(f"GUI元素重建成功: {name}") + else: + print(f"无法重建GUI元素: {name} (类型: {gui_type})") + + except Exception as e: + print(f"重建GUI元素失败 {name}: {e}") + import traceback + traceback.print_exc() + continue + + print("GUI元素重建完成") + + except Exception as e: + print(f"重建GUI元素时发生错误: {e}") + import traceback + traceback.print_exc() + def _recreate3DText(self, text_node): """重新创建3D文本元素 - 使用gui_manager中的createGUI3DText方法""" try: @@ -1313,59 +1607,15 @@ class SceneManager: traceback.print_exc() return None - def _recreateGUIElement(self, gui_node): - """重新创建GUI元素的通用方法""" - try: - # 获取GUI元素类型 - gui_type = "unknown" - if gui_node.hasTag("gui_type"): - gui_type = gui_node.getTag("gui_type") - elif gui_node.hasTag("saved_gui_type"): - gui_type = gui_node.getTag("saved_gui_type") - else: - gui_type = "unknown_gui_element" - - print(f"重新创建GUI元素: {gui_node.getName()} (类型: {gui_type})") - - # 根据类型重新创建GUI元素 - recreated_node = None - if gui_type == "3d_text": - recreated_node = self._recreate3DText(gui_node) - elif gui_type == "3d_image": - recreated_node = self._recreate3DImage(gui_node) - # 可以在这里添加其他类型的GUI元素重建逻辑 - else: - # 对于其他类型的GUI元素,使用备用方法 - recreated_node = self._recreateGUIElementFallback(gui_node, gui_type) - - # 如果没有特定的重建方法,至少确保基本属性 - target_node = recreated_node if recreated_node else gui_node - - # 确保节点被正确标记 - if not target_node.hasTag("is_scene_element"): - target_node.setTag("is_scene_element", "1") - - # 注意:这里不再直接添加到gui_elements列表,因为gui_manager.createGUI3DText已经添加了 - # 只有在使用备用方法且元素不在列表中时才添加 - - # 恢复GUI特定属性 - self._restoreGUIProperties(target_node) - - print(f"GUI元素 {target_node.getName()} 重建完成") - return True - except Exception as e: - print(f"重新创建GUI元素失败: {str(e)}") - return False def _recreate3DImage(self, image_node): - """重新创建3D图像元素""" + """重新创建3D图像元素 - 使用gui_manager中的createGUI3DImage方法""" try: - from panda3d.core import Texture, TextureStage - from panda3d.core import CardMaker, Material, LColor, TransparencyAttrib - print(f"重新创建3D图像: {image_node.getName()}") - # 获取保存的属性 - image_path = image_node.getTag("gui_image_path") if image_node.hasTag("gui_image_path") else None + # 获取保存的图像路径 + image_path = None + if image_node.hasTag("gui_image_path"): + image_path = image_node.getTag("gui_image_path") # 获取位置 pos = (0, 0, 0) @@ -1377,76 +1627,54 @@ class SceneManager: if len(pos_values) >= 3: pos = (pos_values[0], pos_values[1], pos_values[2]) except Exception as e: - print(f"解析位置失败: {e}") + print(f"恢复位置失败: {e}") - # 获取尺寸 - size = 1.0 + # 获取尺寸 - 改进的尺寸处理逻辑 + size = (1.0, 1.0) # 默认尺寸为1x1 if image_node.hasTag("transform_scale"): try: scale_str = image_node.getTag("transform_scale") scale_str = scale_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()') scales = [float(x.strip()) for x in scale_str.split(',')] - size = scales[0] if scales else 1.0 + + # 根据缩放值的数量处理尺寸 + if len(scales) >= 3: + # 3D缩放 (x, y, z) + size = (scales[0]*2, scales[1]*2) # 取前两个值作为宽度和高度 + elif len(scales) >= 2: + # 2D缩放 (x, y) + size = (scales[0]*2, scales[1]*2) + elif len(scales) >= 1: + # 均匀缩放 + size = (scales[0]*2, scales[0]*2) + else: + size = (1.0, 1.0) + + print(f"恢复尺寸: {size} (来自缩放: {scales})") except Exception as e: - print(f"解析尺寸失败: {e}") + print(f"恢复尺寸失败: {e}") - # 创建卡片 - cm = CardMaker('gui_3d_image_recreated') - x_size = y_size = float(size) - cm.setFrame(-x_size / 2, x_size / 2, -y_size / 2, y_size / 2) + # 使用gui_manager中的createGUI3DImage方法创建新的3D图像 + # 首先需要找到gui_manager + gui_manager = None + if hasattr(self.world, 'gui_manager'): + gui_manager = self.world.gui_manager - # 创建3D图像节点 - new_image_node = image_node.getParent().attachNewNode(cm.generate()) - new_image_node.setName(image_node.getName()) - new_image_node.setPos(*pos) + if gui_manager and hasattr(gui_manager, 'createGUI3DImage'): + # 调用gui_manager的createGUI3DImage方法创建新的3D图像 + new_image_node = gui_manager.createGUI3DImage(pos=pos, image_path=image_path, size=size) - # 设置材质 - material = Material(f"image-material-recreated-{len(getattr(self, '_recreated_image_count', [0]))}") - material.setBaseColor(LColor(1, 1, 1, 1)) - material.setDiffuse(LColor(1, 1, 1, 1)) - material.setAmbient(LColor(0.5, 0.5, 0.5, 1)) - material.setSpecular(LColor(0.1, 0.1, 0.1, 1.0)) - material.setShininess(10.0) - material.setEmission(LColor(0, 0, 0, 1)) - new_image_node.setMaterial(material, 1) + if new_image_node: + # 如果返回的是列表(多选创建),取第一个 + if isinstance(new_image_node, list): + created_node = new_image_node[0] + else: + created_node = new_image_node - new_image_node.setTransparency(TransparencyAttrib.MAlpha) - - # 如果有图像路径,加载纹理 - if image_path: - try: - texture = self.world.loader.loadTexture(image_path) - if texture: - new_image_node.setTexture(texture, 1) - texture.setWrapU(Texture.WM_clamp) - texture.setWrapV(Texture.WM_clamp) - texture.setMinfilter(Texture.FT_linear) - texture.setMagfilter(Texture.FT_linear) - except Exception as e: - print(f"加载纹理失败: {e}") - - # 设置标签 - new_image_node.setTag("gui_type", "3d_image") - new_image_node.setTag("gui_id", image_node.getTag("gui_id") if image_node.hasTag( - "gui_id") else f"3d_image_{len(getattr(self.world, 'gui_elements', []))}") - if image_path: - new_image_node.setTag("gui_image_path", image_path) - new_image_node.setTag("is_gui_element", "1") - new_image_node.setTag("is_scene_element", "1") - new_image_node.setTag("created_by_user", "1") - - # 复制变换标签 - for tag in ["transform_pos", "transform_hpr", "transform_scale"]: - if image_node.hasTag(tag): - new_image_node.setTag(tag, image_node.getTag(tag)) - - # 添加到GUI元素列表 - if hasattr(self.world, 'gui_elements'): - if new_image_node not in self.world.gui_elements: - self.world.gui_elements.append(new_image_node) - - print(f"3D图像重建完成: {new_image_node.getName()}") - return new_image_node + print(f"3D图像重建完成: {created_node.getName()}, 路径: '{image_path}', 尺寸: {size}") + return created_node + else: + print("gui_manager.createGUI3DImage返回None") except Exception as e: print(f"重新创建3D图像失败: {str(e)}") @@ -1454,6 +1682,7 @@ class SceneManager: traceback.print_exc() return None + def _recreateGUIElement(self, gui_node): """重新创建GUI元素的通用方法""" try: @@ -1461,12 +1690,6 @@ class SceneManager: gui_type = "unknown" if gui_node.hasTag("gui_type"): gui_type = gui_node.getTag("gui_type") - elif gui_node.hasTag("saved_gui_type"): - gui_type = gui_node.getTag("saved_gui_type") - else: - gui_type = "unknown_gui_element" - - print(f"重新创建GUI元素: {gui_node.getName()} (类型: {gui_type})") # 根据类型重新创建GUI元素 recreated_node = None @@ -1474,10 +1697,8 @@ class SceneManager: recreated_node = self._recreate3DText(gui_node) elif gui_type == "3d_image": recreated_node = self._recreate3DImage(gui_node) - # 可以在这里添加其他类型的GUI元素重建逻辑 else: - # 对于其他类型的GUI元素,使用备用方法 - recreated_node = self._recreateGUIElementFallback(gui_node, gui_type) + return None # 如果没有特定的重建方法,至少确保基本属性 target_node = recreated_node if recreated_node else gui_node @@ -1490,6 +1711,7 @@ class SceneManager: if hasattr(self.world, 'gui_elements'): if target_node not in self.world.gui_elements: self.world.gui_elements.append(target_node) + print(f"将{target_node}添加至gui_elements") # 恢复GUI特定属性 self._restoreGUIProperties(target_node) @@ -1500,31 +1722,6 @@ class SceneManager: print(f"重新创建GUI元素失败: {str(e)}") return False - def _recreateGUIElementFallback(self, gui_node, gui_type): - """GUI元素重建的备用方法""" - try: - # 对于3D文本,确保使用正确的重建方法 - if gui_type == "3d_text": - return self._recreate3DText(gui_node) - - # 对于其他类型,保持原有节点但确保标签正确 - # 确保必要的标签 - required_tags = { - "gui_type": gui_type, - "is_gui_element": "1", - "is_scene_element": "1", - "created_by_user": "1" - } - - for tag, value in required_tags.items(): - if not gui_node.hasTag(tag): - gui_node.setTag(tag, value) - - return gui_node - except Exception as e: - print(f"GUI元素备用重建失败: {str(e)}") - return gui_node - def _restoreGUIProperties(self, gui_node): """恢复GUI元素的特定属性""" try: @@ -2001,92 +2198,6 @@ class SceneManager: pass return None - def _importModelSingle(self, filepath, apply_unit_conversion=False, normalize_scales=True, auto_convert_to_glb=True): - """传统单一模型导入方法(兼容性保留)""" - try: - print(f"\n=== 使用传统模式导入模型: {filepath} ===") - - filepath = util.normalize_model_path(filepath) - original_filepath = filepath - - # 检查是否需要转换为GLB - if auto_convert_to_glb and self._shouldConvertToGLB(filepath): - print(f"🔄 检测到需要转换的格式,尝试转换为GLB...") - converted_path = self._convertToGLBWithProgress(filepath) - if converted_path: - print(f"✅ 转换成功: {converted_path}") - filepath = converted_path - try: - from PyQt5.QtWidgets import QMessageBox - original_ext = os.path.splitext(original_filepath)[1].upper() - QMessageBox.information(None, "转换成功", - f"已将 {original_ext} 格式自动转换为 GLB 格式\n以获得更好的动画支持!") - except: - pass - else: - print(f"⚠️ 转换失败,使用原始文件") - - # 加载模型 - print("直接从文件加载模型...") - model = self.world.loader.loadModel(filepath) - if not model: - print("加载模型失败") - return None - - # 设置模型名称 - model_name = os.path.basename(filepath) - model.setName(model_name) - - # 将模型添加到场景 - model.reparentTo(self.world.render) - - # 设置标签和路径信息 - model.setTag("model_path", filepath) - model.setTag("original_path", original_filepath) - model.setTag("file", model_name) - model.setTag("is_model_root", "1") - model.setTag("is_scene_element", "1") - model.setTag("created_by_user", "1") - - if filepath != original_filepath: - model.setTag("converted_from", os.path.splitext(original_filepath)[1]) - model.setTag("converted_to_glb", "true") - - # 应用处理选项 - if apply_unit_conversion and filepath.lower().endswith('.fbx'): - print("应用FBX单位转换(厘米到米)...") - self._applyUnitConversion(model, 0.01) - model.setTag("unit_conversion_applied", "true") - - if normalize_scales and filepath.lower().endswith('.fbx'): - print("标准化FBX模型缩放层级...") - self._normalizeModelScales(model) - model.setTag("scale_normalization_applied", "true") - - # 调整模型位置到地面 - self._adjustModelToGround(model) - - # 创建并设置基础材质 - print("\n=== 开始设置材质 ===") - self._applyMaterialsToModel(model) - - # 设置碰撞检测(重要!用于选择功能) - print("\n=== 设置碰撞检测 ===") - self.setupCollision(model) - - # 添加到模型列表 - self.models.append(model) - - # 更新场景树 - self.updateSceneTree() - - print(f"=== 模型导入成功: {model_name} ===\n") - return model - - except Exception as e: - print(f"导入模型失败: {str(e)}") - return None - # def createSpotLight(self, pos=(0, 0, 0)): # """创建聚光灯 - 使用统一的create_item方法""" # try: diff --git a/ui/interface_manager.py b/ui/interface_manager.py index da36ce6d..17042936 100644 --- a/ui/interface_manager.py +++ b/ui/interface_manager.py @@ -296,7 +296,6 @@ class InterfaceManager: addNodeToTree(model, sceneRoot,force=True) # 添加所有GUI元素 - print(f"GUIGUIGUIGUIGUIGUIGUIGUIGUIGUIGUIGUIUGIUGI{self.world.gui_elements}") for gui in self.world.gui_elements: # 检查是否是有效的GUI节点(具有getTag方法的NodePath) if hasattr(gui, 'getTag') and hasattr(gui, 'getName'): diff --git a/ui/main_window.py b/ui/main_window.py index 9f935eaf..67fc0413 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -639,10 +639,10 @@ class MainWindow(QMainWindow): self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock) # 创建底部停靠控制台 - self.consoleDock = QDockWidget("控制台", self) - self.consoleView = CustomConsoleDockWidget(self.world) - self.consoleDock.setWidget(self.consoleView) - self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock) + # self.consoleDock = QDockWidget("控制台", self) + # self.consoleView = CustomConsoleDockWidget(self.world) + # self.consoleDock.setWidget(self.consoleView) + # self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock) def setupToolbar(self): """创建工具栏""" diff --git a/ui/property_panel.py b/ui/property_panel.py index 0678dd6b..3305cd7f 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -14,7 +14,7 @@ from direct.actor.Actor import Actor from direct.gui import DirectGui from idna import check_label from jinja2.compiler import has_safe_repr -from panda3d.core import Vec3, Vec4, transpose, TransparencyAttrib, PartGroup, ColorAttrib +from panda3d.core import Vec3, Vec4, transpose, TransparencyAttrib, PartGroup, ColorAttrib, NodePath from scene import util from direct.gui.DirectGui import DirectLabel, DirectFrame from panda3d.core import TextNode @@ -3774,54 +3774,6 @@ class PropertyPanelManager: print(f"加载新视频失败: {e}") return False - def load2DVideoFile(self, video_screen, video_path): - """为2D视频屏幕加载新的视频文件""" - try: - import os - - # 处理空路径情况 - 显示空白 - if not video_path or video_path == "": - print("ℹ️ 空视频路径,显示空白") - video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景 - video_screen.clearPythonTag("movie_texture") - video_screen.setTag("video_path", "") - return True - - # 检查文件是否存在 - if not os.path.exists(video_path): - print(f"❌ 2D视频文件不存在: {video_path}") - # 显示空白而不是红色错误提示 - video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景 - return False - - # 加载新的视频纹理 - movie_texture = self._loadMovieTexture(video_path) - if movie_texture: - # 应用纹理到2D视频屏幕 - video_screen["frameTexture"] = movie_texture - # 设置白色背景以正确显示视频 - video_screen["frameColor"] = (1, 1, 1, 1) - - # 保存视频纹理引用 - video_screen.setPythonTag("movie_texture", movie_texture) - video_screen.setTag("video_path", video_path) - - print(f"✅ 成功加载新2D视频: {video_path}") - return True - else: - print(f"❌ 无法加载2D视频文件: {video_path}") - # 显示空白而不是红色错误提示 - video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景 - return False - - except Exception as e: - print(f"❌ 加载2D视频文件失败: {e}") - import traceback - traceback.print_exc() - # 显示空白而不是红色错误提示 - video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景 - return False - def _loadVideoFromURLWithOpenCV_3D(self, video_screen, url): """使用OpenCV从URL加载视频流并在3D视频屏幕上显示""" try: @@ -8224,17 +8176,19 @@ class PropertyPanelManager: # 其他格式使用标准 Actor 加载 try: - test_actor=Actor(filepath) + import gltf + print(f"[GLTF加载] 尝试加载: {filepath}") + # test_actor=Actor(NodePath(gltf._loader.GltfLoader.load_file(filepath,None))) + test_actor=Actor(NodePath(gltf.load_model(filepath,None))) anims = test_actor.getAnimNames() + test_actor.reparentTo(self.world.render) + self._actor_cache[origin_model] = test_actor print(f"[Actor加载] 标准加载检测到动画: {anims}") if not anims: test_actor.cleanup() test_actor.removeNode() return None - actor = Actor(filepath) - actor.reparentTo(self.world.render) - self._actor_cache[origin_model] = actor - return actor + return test_actor except Exception as e: print(f"创建Actor失败: {e}") return None @@ -8590,6 +8544,8 @@ except Exception as e: print(f"[系统转换] 系统转换失败: {e}") return None + + def _playAnimation(self,origin_model): actor=self._getActor(origin_model) if not actor: @@ -8702,7 +8658,7 @@ except Exception as e: anim_name = self.animation_combo.currentText() actor.setPos(origin_model.getPos()) actor.setHpr(origin_model.getHpr()) - actor.setScale(origin_model.getScale()) + actor.setScale(origin_model.getScale()/100) if cmd == "play": origin_model.hide()