将3D Movie Maker移植到Linux
摘要
一位开发者利用最近发布的源代码,成功将 Microsoft 3D Movie Maker 移植到 Linux,使其成为首个已知的能在 Windows 之外原生运行的分支版本。
暂无内容
查看缓存全文
缓存时间: 2026/05/14 21:26
# 将3D电影制作工具移植到Linux - Ben Stone Online
来源:https://benstoneonline.com/posts/porting-3d-movie-maker-to-linux/
---
- 2026年5月9日
- 博客 (https://benstoneonline.com/categories/blog/)
- 软件开发 (https://benstoneonline.com/tags/software-development/)
你知道吗?现在你可以在Linux上原生运行微软3D电影制作工具了。在过去18个月里,我一直在开发3DMMEx (https://github.com/benstone/3DMMEx),这是我对3D电影制作工具的源代码移植版本。我的分支的一个目标就是可移植性。该项目最近达到了一个重要的里程碑:我们可以在Linux上编译和运行了,这使得3DMMEx成为第一个在Windows之外运行的3D电影制作工具分支!在这篇文章中,我将探讨将一款30年前的多媒体应用移植到新平台时所遇到的一些挑战。
#### 现在你可以在Linux上制作有趣的动画电影了!
## 背景
早在2020年,我就写过一篇关于逆向工程微软3D电影制作工具的博客系列文章 (https://benstoneonline.com/posts/reverse-engineering-3d-movie-maker-part-one/)。自那以后,有了一个重大进展:2022年5月,微软发布了该应用的完整源代码 (https://github.com/microsoft/Microsoft-3D-Movie-Maker)。这完全出乎意料——我从没想过有一天能看到原始源代码,甚至怀疑它是否还存在于微软的档案中,但现在它已经在GitHub上了……而且附带宽松的MIT许可证!
在进一步深入之前,我必须衷心感谢Alice Averlong (原名Foone Turing) (https://digipres.club/@foone)对获取3DMM源代码的不懈追求,以及Scott Hanselman (https://twitter.com/shanselman)、Jeff Wilcox (https://twitter.com/jeffwilcox)以及微软团队的其他成员促成了这次发布。如果你对源代码发布背后的故事感兴趣,可以听听关于这次发布的Hanselminutes播客 (https://hanselminutes.com/844/3d-movie-maker-forever-with-foone-turing)。
该存储库包含3DMM应用程序(代号“Socrates”)、3DMM和Creative Writer 2所使用的“Kauai”应用程序框架、开发/创作工具(包括我此前逆向工程过的脚本语言编译器 (https://benstoneonline.com/posts/reverse-engineering-3d-movie-maker-part-three/))、一些文档以及预渲染资源。这次发布的内容足够完整,你可以编译出自己的`3DMOVIE.EXE`版本。
发布之后,我花了一些时间深入研究了存储库,并写了一些关于源代码的笔记 (https://github.com/benstone/3dmm-source-notes),包括如何使用原始的开发工具进行构建。我立刻注意到的是这个代码库的工程质量有多高:代码风格一致,注释详尽,并且到处都是断言检查。在1995年,开发者们没有奢侈的条件去推送更新来修复bug,所以他们必须在发布前确保代码足够优秀。3DMM的代码库比我看过的其他一些90年代中期游戏源代码发布版要干净得多。
#### Visual Studio Code中显示的3D电影制作工具源代码
源代码发布后,我首先想到的事情之一就是尝试将其移植到一个新平台。我不想自己搞一个分支(那工作量太大了!),所以我加入了3DMMForever (https://github.com/foone/3dmmforever)项目,该项目在发布后不久就启动了,旨在对3DMM进行现代化改造。我们用CMake替换了旧的makefile,并解决了一系列问题,使得3DMM能用现代编译器Visual Studio 2022进行编译。这些都是迈向可移植3DMM的良好第一步。
不幸的是,在那之后不久项目就停滞了……所以2024年底,我分支了3DMMForever,创建了3DMMEx (https://github.com/benstone/3DMMEx)。我最初的计划是修复3DMMForever中的一些小问题,并对移植3DMM到其他平台所需的工作进行初步探索。有几个主要问题使得移植变得困难:
- 代码使用了预标准化的C++方言,只能用微软Visual C++编译。
- Kauai应用程序框架是“跨平台”的,但它只支持两个平台:Windows和Macintosh 68K。不过Macintosh的支持非常不完整。
- 3DMM应用程序建立在Kauai的跨平台抽象之上,但有时会突破抽象层直接调用Win32 API。
- 框架在一些性能敏感的函数中包含了内联x86汇编语言。
- 项目对指针大小做了假设,导致在64位系统上产生编译错误。
- 框架有很多功能3DMM并没有用到,但却是创作工具所必需的。
- 一些外部依赖以预编译的静态库形式链接到构建中。
- 大部分代码没有测试,这使得捕捉回归问题变得困难。3DMM开发者在开发过程中使用了自动化测试 (https://3dmm.com/showthread.php?t=43377),甚至留下了一些自动化测试钩子 (https://github.com/microsoft/Microsoft-3D-Movie-Maker/blob/main/kauai/SRC/APPBWIN.CPP#L483),但这些测试并未包含在开源发布中。
如果你拿现有的源代码直接尝试在另一个平台上编译,你会被迫同时面对所有这些问题。所以,我决定尝试在保持原始Windows x86构建的同时,逐个解决其中一些问题。我的想法是,如果我解决了其中一部分问题,也许我的工作对其他想要将其移植到另一个平台的人会有用。
果然,在我开始分支后不久,我收到了一位名叫Mark Cave-Ayland (https://www.ilande.co.uk/)的出色软件开发者的邮件。Mark对将3DMM移植到Linux很感兴趣,这样他就可以在树莓派上运行它,供他的孩子们使用。我觉得这非常酷!过去一年里,我们共同努力让3DMM在Linux上运行起来,Mark专注于Linux移植,而我则专注于Windows上的SDL移植。
## 静态库
3DMM项目有两个外部依赖以静态库形式打包:BRender和AudioMan。BRender是一个由Argonaut Technologies (https://en.wikipedia.org/wiki/Argonaut_Games)开发的3D渲染引擎。BRender的源代码 (https://github.com/foone/BRender-v1.3.2)大约与3DMM同时发布,是在Alice Averlong获得Argonaut Technologies前CEO Jez San的批准后发布的。BRender可以从源代码构建,但它包含了大量手工编写的x86汇编代码。3DMMForever的一位贡献者@prettytofugirl (https://github.com/prettytofugirl)在将所有x86汇编替换为可移植的C代码 (https://github.com/prettytofugirl/3DMM-BRender)方面做了出色的工作,我现在正在3DMMEx中使用这些代码。
AudioMan是一个声音混合器库,用于90年代中期微软的许多多媒体产品。在3DMM中,AudioMan不仅用于播放声音,还用于转换导入和录制的声音。当编译目标不是Visual Studio下的Windows x86时,静态库将无法工作。所以,我决定启动自己的小型反编译项目!花了几个周末的时间,我使用Ghidra (https://github.com/nationalsecurityagency/ghidra)逆向工程了`AUDIOD.LIB`静态库,并将其反编译为C++ (https://github.com/benstone/audioman-decomp)。完全反编译通常相当困难,但这次我有该库的调试版本,其中包含完整的符号信息。而且,我也并不需要整个库才能让3DMM正常工作,所以只反编译了关键组件。这是一个相当有趣的旁支项目,我或许应该为此写篇博客。
反编译AudioMan是构建3DMMEx的Windows x64和ARM64版本的重要一步。然而,事实证明它与Windows声音API和COM接口紧密耦合,因此很难使其在非Windows平台上运行。相反,我为非Windows平台添加了一个使用miniaudio (https://miniaud.io/)的新波形声音播放模块。集成miniaudio也解决了应用内录音功能所需的跨平台音频输入问题。
## 移除汇编语言
Kauai中的一些函数包含手写优化的汇编语言。这些函数用于对性能敏感的操作,例如内存复制、位图图像处理和数据压缩。编译时的`#define`用于在C++和汇编语言实现之间切换。一些函数有针对Windows的Intel x86和针对Macintosh的Motorola 68020的优化版本。
原始代码库还包括一个代码生成器 (https://github.com/benstone/3DMMEx/blob/main/kauai/src/kcd2_386.c),它能够生成Kauai中使用的两种自定义压缩算法的x86汇编语言版本。当我第一次开始逆向工程3D电影制作工具时,我记得在IDA中发现了KCD2解压缩函数,并对它的复杂程度感到有点害怕。我当时想,“这不可能是人类写出来的”。事实证明我是对的!
#### IDA中KCD2解压缩函数的控制流图
切换到C++版本相当容易,因为我只需要移除一个`#define`,但这确实暴露了相应C++实现中的一些bug。移除汇编版本还带来了一些微小的性能提升,因为应用程序现在可以利用C运行时针对现代处理器优化的memcpy/memmove实现了。
## 枯燥的工程工作
将3DMM移植到新平台需要进行大量更改,这些更改很可能会破坏现有代码。在进行重大更改之前,我决定花一些时间来改进调试和测试体验。这有望帮助我在进行更改时捕捉回归问题。这些更改包括:
- Kauai框架有少量单元测试。我将这些测试移植到了Google Test,并将其集成到构建中。
- Kauai使用了自定义数据类型,在Visual Studio调试器中难以阅读。我编写了一些NatVis可视化工具 (https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=visualstudio),为Visual Studio提供如何渲染某些类型的提示。例如,字符串类(STN)现在显示字符串的值,而不是指向缓冲区的指针。
- 我希望避免任何可能破坏文件格式兼容性的更改,以确保你仍然可以加载和保存30年前制作的电影文件。因此,我在文件格式结构周围添加了静态断言,以确保它们在为其他平台编译时大小不会改变。这些断言帮助识别了将3DMM移植到64位系统时的问题。
- 调试3DMM的UI很有挑战性,因为大多数逻辑发生在脚本内部。我添加了额外的日志记录来显示当前正在执行的脚本,以便更容易地跟踪UI逻辑的执行过程。
- 进行了大量小的重构,以在组件之间添加接缝,这样我就可以为SDL/跨平台构建添加新的实现,而无需删除原本正常工作的Win32实现。
#### 使用和不使用NatVis可视化工具时,Visual Studio调试器显示的自定义字符串对象
我还决定保留原始开发者在1990年代使用的Apps匈牙利命名法 (https://idleloop.com/hungarian/)代码风格。在2026年这样做可能有点奇怪,但我希望新代码与原始代码保持一致。刚开始需要一些时间来适应,但在代码库中工作一段时间后,你就不会对像`mpgrfchpsz`这样的变量名多加考虑了!😄
## 用SDL替换Win32
Kauai的GUI库在Windows上使用Win32 API,在System 7上使用Macintosh Toolbox。我使用了SDL (https://www.libsdl.org/)来替换Win32 GUI实现,SDL是一个流行的跨平台多媒体应用开源库。
为了简化SDL测试,我首先编写了一个小的“hello world”Kauai应用程序,用于测试事件分发和基本渲染。这样就不需要让整个应用程序都正常工作才能测试框架的更改。我使用这个测试应用先实现了窗口创建和基本输入处理,然后开始处理渲染。
SDL后端的大部分工作相当直接:Kauai已经在GUI特性上有了相当不错的抽象。例如,所有图形都由GNV(图形环境)和GPT(图形端口)类处理。这些类有类似DrawRcs(绘制矩形)或DrawRgch(渲染文本)的方法。我所要做的就是用SDL等效代码替换Win32 GDI渲染代码。
有时这有点挑战性,因为Win32中一行代码就能完成的操作,可能要用数百行新代码来为SDL实现。字体管理就是一个很好的例子。许多90年代的多媒体应用只使用一组固定的字体,这些字体随应用的资源一起打包。然而,3DMM希望能够发现并使用系统上可用的任何TrueType字体。在Win32 API中,枚举字体只需简单调用`EnumFonts`,它会为每个新字体回调。之后你可以调用`CreateFontIndirect`来获取渲染文本的字体句柄。SDL没有枚举字体的方法,所以这必须针对每个平台实现。
另一个例子是键盘快捷键处理。Win32有加速键表,提供虚拟键码与WM_COMMAND消息之间的映射。加速键表通常存储在可执行文件的资源部分。应用启动时,你调用`LoadAccelerators`加载表,然后在事件循环中调用`TranslateAccelerator`将按键命令转换为WM_COMMAND。所有管理表的实际工作都由Windows为你处理。为了让键盘快捷键在SDL构建中工作,我必须重新实现加速键表系统。这次重新实现的一个好处是,它使得支持自定义键绑定变得容易得多。我计划在未来的版本中添加对该功能的支持。
有一点我后来才发现,SDL中的字符串需要是UTF-8编码的。3DMM默认使用系统的默认代码页(英文系统上是CP1252)来处理字符串。这使得渲染的文本看起来几乎正确,直到我发现UI中有一个部分,em-dash被渲染成了一个方框。所以,我添加了额外的转换方法,用于与UTF-8之间的转换。3DMM也使用UCS-2支持Unicode,但在原始源代码发布中非常不完整,直到1996年的日文版才完成。将来,我希望将所有字符串处理内部改为UTF-8,但这需要对电影序列化方式进行一些根本性的更改。
## 画出剩下的猫头鹰企鹅
随着所有内联汇编语言被移除以及SDL后端工作正常,3DMMEx已经接近在另一个平台上原生运行的可能性了。就在这个时候,Mark做了大量工作来解决许多问题,从处理区分大小写的文件名和错误的反斜杠,到为POSIX平台支持编写新模块,以及集成FluidSynth用于MIDI播放和GStreamer用于播放过场动画。几个月后,Mark让3DMMEx在Linux上启动成功了!请查看Mark关于将3DMM移植到Linux的博客系列文章 (https://www.ilande.co.uk/2026/04/06/porting-3dmm-to-linux-part1.html),其中详细介绍了我们遇到的一些其他技术挑战。
非常感谢Mark,不仅因为他在Linux移植方面的出色工作,还因为他一直让我保持清醒……我是说,他一直让我脚踏实地、专注于我们真正需要解决的问题。我倾向于在次要的事情上过度工程化,而Mark一再提醒我“让我们先让该死的东西跑起来”,这绝对是我们需要的务实作风!
## 获取代码
3DMMEx的源代码在GitHub上的3DMMEx存储库 (https://github.com/benstone/3DMMEx)中。要构建它,请按照README中的说明操作。你还需要原始3DMM安装中的资源文件。如果你没有原始光盘,这些资源文件可以从存档站点获取。
现在你可以去制作一些有趣的动画电影了!
相似文章
在一个单GPU上构建了开源的一提示生成电影级短片流程——使用FLUX.2 [klein]生成角色关键帧,Wan2.2-I2V生成动画,视觉评论器带自动重试,同一流程中集成音乐+9种语言旁白
构建了一个开源流程,仅需输入一句话即可生成带有角色、动画、音乐和旁白的电影级短片,使用FLUX.2、Wan2.2及其他模型,在单个AMD GPU上运行。该流程包含导演代理、角色生成、关键帧动画、视觉评论器、音乐和旁白等阶段。
@0xluffy_eth: 有人为Claude Code开发了免费视频编辑工具...太疯狂了。 只需把原始素材和资源放入文件夹。 就这样。 它会处理一切: - 剪辑片段 - 移除冗余词 - 添加字幕 - 应用色彩分级和滤镜 - 处理动画 - 渲染最终视频 无时间线。…
A free, open-source video editing tool built for Claude Code that fully automates editing from raw footage—clipping, filler word removal, subtitles, color grading, animation, and final rendering—all without a timeline or manual edits.
@oliviscusAI: 有人刚刚开源了一款桌面应用,它可以通过图像生成3D模型,并且完全本地运行。它叫Modly。…
Modly是一款开源桌面应用,可从图像生成完全纹理化的3D网格,完全在本地GPU上运行,并支持可插拔的AI模型扩展。
在 Linux 上运行 Space Cadet Pinball
本文介绍了 Linux 用户如何通过 Flatpak 安装并游玩 Space Cadet Pinball,并提供了使用 Full Tilt! 数据文件来增强画面的操作指南。
Linux游戏性能提升,因为Windows API正成为Linux内核功能
随着Windows特定的同步API通过NTSYNC驱动直接实现在Linux内核中,Linux游戏性能得以提升。该驱动现已默认加载于Steam Deck上,有助于提高游戏兼容性和速度。