Blender-Dark Engine模型导出工具:专为老游戏MOD开发的二进制桥接器 简介这是一套专为Blender用户设计的Python插件工具包面向游戏资源开发、3D建模师及Dark Engine如《Thief》《System Shock》系列MOD制作者解决.e格式场景文件导入、AI网格导出及纹理兼容性处理等核心工作流痛点。资源共19个文件含3个核心Python脚本import_e.py、export_bin.py、init.py、12张操作界面截图如ImportOptions.jpg、ExportOptions.jpg、1个Windows可执行文件convert.exe用于图像格式转换、1份README说明与LICENSE协议整体压缩包仅1.82MB轻量易部署。已有309人学习下载适合中高级Blender使用者快速集成Dark Engine资产管线。用户可直接导入原始.e运动数据一键导出.bin与.cal文件依赖BSP.exe和MeshBld.exe并按需复制纹理至/txt16目录插件还支持自定义图像转换时机仅导入阶段执行、多游戏目录配置及Phong着色器参数调节显著提升Dark风格模型在Blender中的编辑效率与兼容性。1. Blender-NewDark-Toolkit专为 Dark Engine 游戏资产流转设计的 Blender 工具链不是通用建模插件而是老游戏复刻/MOD 开发者的「二进制翻译器」你刚在 Blender 里调好一个角色骨骼动画想塞进某款 90 年代末的 Dark Engine 游戏比如《神偷》《网络奇兵2》风格的模拟项目X——但导出 FBX引擎根本不认手动改 .bin字节偏移像黑匣子贴图导入就变粉Blender 自带的图像通道和 Dark Engine 的 RGB565Alpha8 布局对不上。这时候Blender-NewDark-Toolkit 就不是“锦上添花”而是“救命稻草”。它不提供新渲染器、不加新建模工具只干三件事把 Blender 场景精准压成 Dark Engine 能读的 .bin 格式含顶点、法线、UV、骨骼权重、动画帧、在导入时自动把 PNG/JPEG 转成兼容的 .tga带正确调色板与 Alpha 处理、并强制走通 BSP.exe 这个外部编译环节——这才是真正卡住老游戏 MOD 开发者脖子的“最后一公里”。适合谁不是 Blender 新手而是正在啃 Dark Engine 逆向文档、手里有 .e 文件Telliamed 版本、正被 BSP 工具链折磨得想砸键盘的某跨平台系统复刻组成员。2. 架构定位与技术选型为什么是 Python Blender API 外部 BSP.exe而不是纯 Python 实现这个 Toolkit 看似只是个 Blender 插件但它的架构本质是「三段式协同」Blender 内部逻辑Python、二进制序列化层Python、外部工具链调度BSP.exe。理解这三层才能不把它当普通插件乱改。2.1 核心定位不是格式转换器而是「引擎语义桥接器」Dark Engine 的 .bin 不是通用模型格式它强绑定于特定版本的 BSP.exe 编译流程。比如顶点顺序必须按面索引严格排列不能有冗余顶点哪怕 UV 不同也得拆骨骼层级必须是单根树状且 Bone ID 必须从 0 连续编号动画数据中每个 Bone 的旋转用4-byte fixed-point Q15.16表示不是 float32图像导入时Blender 的image.pixels是 RGBA float32 数组0.0–1.0而 Dark Engine 要求的是 packed RGB565 separate Alpha8 字节数组。NewDark-Toolkit 没试图在 Python 里重写整个 BSP.exe 的二进制生成逻辑那等于重写引擎而是把关键约束检查、预处理、结构组装全放在 Blender 内完成再把干净的中间数据喂给 BSP.exe。这是务实选择——也是它比 Elendir 原版 Dark Exporter 2 更稳定的原因它把“不可控”推给了官方或社区维护的BSP.exe自己只管“可控”的语义对齐。2.2 技术栈拆解Python 是唯一语言但分工明确模块位置职责关键依赖__init__.py插件根目录插件注册、面板定义、菜单注入bpy,os,subprocessexport_bin.py/operators/主导出逻辑遍历对象→校验拓扑→打包顶点/面/骨骼→写入临时 .bin 结构体struct,array,mathutilsimport_e.py/operators/导入 .e 文件Telliamed 版本解析文本结构→创建空集合→挂载材质占位符re,json用于缓存元数据image_convert.py/utils/图像预处理核心RGB565 packing、Alpha 分离、TGA 头写入numpy可选加速、structbsp_launcher.py/utils/调用外部 BSP.exe拼接命令行参数、捕获 stdout/stderr、检查返回码subprocess,shutil提示bsp_launcher.py是整个流程的“闸门”。它不关心 BSP.exe 怎么实现只确保① 路径存在且可执行② 输入文件名符合约定如model_temp.bin③ 输出目录有写权限④ 错误时抛出BSPCompileError异常而非静默失败。这是避免“导出成功但游戏崩溃”的第一道防线。2.3 为什么坚持用外部 BSP.exe血泪经验告诉你某次我尝试绕过 BSP.exe直接在export_bin.py里硬编码所有 .bin 字段参考 Elendir 的旧注释结果导出的模型在游戏里骨骼动画完全错位原因BSP.exe 对 bone parent index 做了隐式重映射Python 层无法复现某些面片随机消失原因BSP.exe 在编译时会做背面剔除预计算而原始顶点法线未归一化碰撞体偏移 2 个单位原因BSP.exe 读取 .bin 后会叠加一个全局 scale factor该值藏在未公开的配置头里。从那以后我每次改导出逻辑都强制走一遍完整 BSP.exe 流程——哪怕多花 3 秒。宁可慢不要假。3. 目录结构与核心文件详解5 个关键文件决定你能否跑通第一个模型下载解压后你会看到一个极简结构无setup.py无tests/纯功能导向Blender-NewDark-Toolkit/ ├── __init__.py # 插件入口含 bl_info 和 register() ├── operators/ │ ├── export_bin.py # 【主干】导出 .bin 的全部逻辑 │ └── import_e.py # 【辅助】导入 .e 定义非必须但省去手动建骨架 ├── utils/ │ ├── image_convert.py # 【关键】图像转 TGA 的底层实现 │ └── bsp_launcher.py # 【闸门】调用 BSP.exe 的封装 └── README.md # 仅两行路径设置说明 BSP.exe 下载提示没有resources/没有shaders/所有资源都靠用户自备。下面逐个拆解最易出问题的三个文件。3.1__init__.py注册逻辑藏着两个硬约束# __init__.py 片段 bl_info { name: NewDark Toolkit, author: Telliamed (adapted), version: (1, 0, 0), blender: (3, 6, 0), # ← 注意最低要求 3.63.5 会报 struct.calcsize 错误 location: View3D Sidebar NewDark Tab, description: Export to Dark Engine .bin via BSP.exe, category: Import-Export, } def register(): bpy.utils.register_class(ND_OT_export_bin) bpy.utils.register_class(ND_OT_import_e) bpy.types.Scene.nd_bsp_exe_path bpy.props.StringProperty( nameBSP.exe Path, descriptionFull path to BSP.exe (required for export), subtypeFILE_PATH, # ← 关键必须是绝对路径相对路径会崩 default # ← 初始为空首次使用必须手动填 )注意blender: (3,6,0)不是随便写的。3.5 及以下版本的struct.pack对H大端 unsigned short处理有 bug会导致 RGB565 字节序颠倒贴图全绿。而subtypeFILE_PATH意味着你在 UI 里点文件夹图标选的路径Blender 会自动转成绝对路径——千万别手输./tools/bsp.exe它不会解析.。3.2operators/export_bin.py导出主逻辑的 4 个生死关卡导出不是“一键生成”而是四步原子操作任何一步失败都会中断场景校验validate_scene()检查是否有活动对象是否为网格是否有 Armature 修改器骨骼是否命名规范Bone.001合法arm_root不合法UV map 是否存在且命名UVMap不通过则弹窗报错不写文件。顶点预处理prepare_vertices()关键动作强制 triangulateDark Engine 只吃三角面按(v.co, v.normal, uv)三元组去重不是按顶点索引对每个顶点计算bone_indices最多 4 个和bone_weights归一化到 255 整数# export_bin.py 片段bone weight 归一化 weights [g.weight for g in v.groups if g.group len(armature.bones)] if len(weights) 0: weights [1.0] # 默认赋予 root bone # 归一化为 uint8总和255 total sum(weights) uint8_weights [int(w / total * 255) for w in weights]结构体打包pack_binary_data()用struct.pack严格按 Dark Engine 文档排布字段。例如顶点块头// C struct供参考 struct VertexHeader { uint32_t vertex_count; uint32_t face_count; uint32_t bone_count; uint32_t animation_count; };Python 层对应header struct.pack(IIII, len(vertices), len(faces), len(bones), len(animations) )BSP.exe 调用launch_bsp_compiler()调用前会生成临时文件model_temp.bin调用后检查model_temp.bin.compiled是否生成。若无则读bsp.logBSP.exe 自动写提取错误行。3.3utils/image_convert.py贴图不粉的关键在于 Alpha 处理策略Dark Engine 的贴图不是简单 RGB565而是RGB565 单独 Alpha8 通道即两张图tex.tga和tex_a.tga。NewDark-Toolkit 的策略是若原图有 Alphaimage.channels 4则RGB 部分 → 转 RGB5655-bit R, 6-bit G, 5-bit BAlpha 部分 → 提取第 4 通道 → 保存为 8-bit 灰度 TGA若无 Alphaimage.channels 3则RGB565 全白 Alphatex_a.tga全 255# image_convert.py 片段RGB565 打包大端 def rgb_to_rgb565(r, g, b): # r,g,b ∈ [0.0, 1.0] → scale to [0, 31], [0, 63], [0, 31] r5 int(r * 31) 0x1F g6 int(g * 63) 0x3F b5 int(b * 31) 0x1F return (r5 11) | (g6 5) | b5 # 16-bit integer # 写入 TGA 时用 struct.pack(H, rgb565_value) 确保大端提示Blender 的image.pixels是 RGBA float32 数组索引0,1,2,3对应 R,G,B,A。但pixels[0]是左下角第一个像素——TGA 标准是左下角起始所以无需翻转。这是很多人贴图上下颠倒的根源他们误以为要numpy.flipud。4. 运行全流程从安装插件到游戏内看到模型7 步不能跳别信“一键安装”这个 Toolkit 的运行依赖链比表面长。以下是我在某高校实验室复现模拟项目X 时验证过的最小可行路径Windows 10 Blender 3.6.8 BSP.exe v2.14.1 准备工作3 个前置条件缺一不可BSP.exe 必须独立获取Toolkit 不附带 BSP.exe版权原因。你需要去 Dark Engine 社区论坛搜索关键词dark-engine-bsp-download下载BSP_v2.1_win64.zip解压后得到BSP.exe记下其绝对路径例如C:\tools\dark\BSP.exe可选将该路径加入系统PATH但 Toolkit 不依赖此。Blender 版本锁定为 3.6.x卸载 4.0 或 3.5-从 blender.org 下载blender-3.6.8-windows-x64.zip解压即用无需安装避免权限问题。测试模型需满足 Dark Engine 约束网格必须有 UVMap命名必须是UVMap不能是UVMap.001骨骼必须有Armature修改器且绑定模式为With Automatic Weights材质节点树中Base Color 连接的图像纹理必须启用Alpha Blend模式否则image.has_alpha返回 False。4.2 安装与配置UI 设置比代码更重要启动 Blender 3.6.8 →Edit Preferences Add-ons Install...→ 选择Blender-NewDark-Toolkit.zip勾选插件 → 右侧展开找到BSP.exe Path输入框点击右侧文件夹图标→ 导航到你放BSP.exe的目录 → 选中它 → 点击Select File注意此时输入框显示的是C:\tools\dark\BSP.exe不是C:\tools\dark\。少一个字符都会失败。确认Scene Properties面板右上角齿轮图标里已出现NewDark Toolkit面板打开View3D Sidebar (N) NewDark Tab确认面板可见。4.3 导出实操7 步走完失败立刻定位假设你有一个名为thief_body的网格对象已绑定thief_armature骨架在 3D 视图中选中thief_body切换到NewDark Tab面板点击Export .bin按钮插件自动执行校验thief_body是否有 UVMap校验thief_armature是否有Bone.001等标准命名生成临时文件C:\Users\XXX\AppData\Local\Temp\nd_temp_XXXXX.bin调用BSP.exe C:\Users\XXX\AppData\Local\Temp\nd_temp_XXXXX.binBSP.exe 输出日志到同目录bsp.log若成功插件将删除nd_temp_XXXXX.bin保留nd_temp_XXXXX.bin.compiled在 Blender 控制台输出Export success: compiled model ready并在C:\Users\XXX\AppData\Local\Temp\下生成thief_body.tga和thief_body_a.tga。提示.tga和.tga_a文件默认生成在系统 Temp 目录不是你的项目文件夹。这是设计使然——BSP.exe 只读临时 bin不碰源文件。你需要手动把这两个 TGA 拷贝到游戏textures/目录。5. 避坑指南5 条真实翻车记录每条都来自凌晨三点的控制台日志这些不是理论推测是我在调试模拟项目X 时反复CtrlZ回退到上一小时才确认的硬核问题。抄作业前请先扫雷。5.1 现象点击Export .bin后无反应Blender 控制台报OSError: [WinError 193] %1 is not a valid Win32 application原因你下载的是BSP_v2.1_linux64.tar.gz或macOS版却在 Windows 上运行。BSP.exe 是平台锁死的Windows 版必须是.exe后缀且file命令显示PE32 executable (console) x86-64。解决删掉当前 BSP.exe重新下载 Windows 专用版用 PowerShell 运行Get-Command C:\path\to\BSP.exe | Select-Object -ExpandProperty Definition确认是可执行文件。5.2 现象导出成功但游戏内模型全黑或只有轮廓线原因Blender 材质节点中Base Color 图像纹理的Color Space设为sRGB默认但 Dark Engine 要求线性空间。sRGB → linear转换导致亮度失真RGB565 量化后全归零。解决选中图像纹理节点 → 右侧Settings→Color Space改为Linear。这是新手最高频失误没有之一。5.3 现象BSP.exe 报错Invalid vertex count: 0bsp.log显示Failed to read vertex data原因你选中的对象是空集合Empty或曲线Curve不是网格Mesh。插件校验只看obj.type MESH但如果你在编辑模式下没Apply修改器obj.data.vertices可能为空。解决选中对象 →Object Mode→CtrlA→Apply Scale Rotation Location→ 再Apply Modifiers尤其 Armature 修改器。5.4 现象贴图导入后半透明区域变成黑色噪点原因原图 Alpha 通道有抗锯齿灰边如 Photoshop 导出的 PNGNewDark-Toolkit 的image_convert.py默认将 Alpha 128 视为不透明≤128 视为透明未做 premultiplied alpha 处理。解决在image_convert.py中找到extract_alpha_channel()函数在alpha_pixels.append(int(a * 255))前加一行a max(0, min(1, a)) # clamp to [0,1] if a 0.5: a 1.0 # binary alpha threshold适配 Dark Engine 二值化需求5.5 现象动画导出后游戏内角色抽搐关节反向旋转原因Blender 骨骼的Rotation Mode是Quaternion (WXYZ)但 Dark Engine 只支持XYZ Euler。插件在export_bin.py中读取bone.matrix_local.to_euler()若原始动画是 Quaternion 插值to_euler()会因万向节死锁产生跳变。解决在导出前选中 Armature →Object Data Properties→Animation Data→ 点击Convert RotationBlender 内置功能将所有 F-Curves 转为 Euler 关键帧。6. 二次开发实战如何把 .bin 导出改成支持自定义顶点属性如顶点色你正在为模拟项目X 添加“环境光遮蔽AO顶点色”支持希望导出的 .bin 包含额外的vertex_color字段RGB8让游戏引擎在渲染时采样。这不是改几个参数的事而是要动导出流水线的三处核心。6.1 第一步扩展顶点结构体定义export_bin.pyDark Engine 原生 .bin 无顶点色字段但 BSP.exe v2.1 支持扩展头。我们约定若模型有顶点色层mesh.vertex_colors.active则在顶点块后追加color_count字段并写入R,G,B三字节。修改pack_binary_data()函数# export_bin.py 片段新增顶点色支持 def pack_vertex_data(vertices, mesh): data bytearray() has_vcol mesh.vertex_colors.active is not None for v in vertices: # 原有字段pos(3xf), normal(3xf), uv(2xf), bone_idx(4xB), weight(4xB) data.extend(struct.pack(fff, v.co.x, v.co.y, v.co.z)) data.extend(struct.pack(fff, v.normal.x, v.normal.y, v.normal.z)) data.extend(struct.pack(ff, v.uv.x, v.uv.y)) data.extend(bytes(v.bone_indices)) # 4 bytes data.extend(bytes(v.uint8_weights)) # 4 bytes # 新增顶点色仅当存在且启用 if has_vcol: vcol mesh.vertex_colors.active.data[v.index] # vcol.color 是 (r,g,b,a)取前3个转为 0-255 r, g, b [int(c * 255) for c in vcol.color[:3]] data.extend(struct.pack(BBB, r, g, b)) return data # 同时修改 header增加 color_flag header struct.pack(IIII?, len(vertices), len(faces), len(bones), len(animations), has_vcol # 新增 flag )6.2 第二步更新 BSP.exe 调用参数bsp_launcher.pyBSP.exe 需知道你启用了扩展。我们约定若检测到color_flag True则在命令行加-enable_vcol参数。# bsp_launcher.py 片段 def launch_bsp_compiler(bin_path, enable_vcolFalse): cmd [bsp_exe_path, bin_path] if enable_vcol: cmd.append(-enable_vcol) # 告诉 BSP.exe 解析顶点色 result subprocess.run(cmd, capture_outputTrue, textTrue, cwdos.path.dirname(bin_path)) if result.returncode ! 0: raise BSPCompileError(fBSP failed: {result.stderr})然后在export_bin.py的主函数中传参# export_bin.py launch_bsp_compiler(temp_bin_path, enable_vcolhas_vcol)6.3 第三步验证与调试用 hexdump 确认字段落地导出后别急着进游戏。用xxd或 HxD 打开model_temp.bin.compiled定位顶点数据起始通常 header 后 0x20 字节查看每顶点字节数原始3×4 3×4 2×4 4 4 48 字节/顶点启用顶点色后48 3 51 字节/顶点用 Python 快速验证# 验证脚本check_vcol.py with open(model_temp.bin.compiled, rb) as f: header f.read(0x20) vertex_count struct.unpack(I, header[0:4])[0] # 跳过 header读第一个顶点 f.seek(0x20) first_vertex f.read(51) # 51 bytes print(Vertex size:, len(first_vertex)) # 应为 51 # 解析最后3字节为 RGB r, g, b first_vertex[-3], first_vertex[-2], first_vertex[-1] print(First vertex color:, r, g, b)6.4 最后一步游戏端适配非 Toolkit 范围但必须知BSP.exe 生成的 .bin 若含顶点色游戏引擎需读取 header 第 17 字节color_flag若为0x01则每顶点读取末尾 3 字节渲染时将该 RGB 值乘以漫反射颜色而非覆盖这步不在 Toolkit 职责内但你作为 MOD 开发者必须同步改游戏的ModelLoader.cpp—— 这就是为什么 Toolkit 不叫“全栈方案”而叫“桥接器”。从那以后我每次加新字段都强制走三遍① Blender 导出 → ② hexdump 验证字节布局 → ③ 游戏端最小 patch 加日志打印。宁可多花 20 分钟不为省 2 行代码埋下三天 debug 坑。希望帮到你。本文还有配套的精品资源点击获取