AVI Materials
Используя статью Procedural Materials (Процедурные материалы) возможно воспроизводить фильмы (в частности, в формате AVI) на месте монитора на карте, который представляет собой обычную текстуру. Используя эту технику, вы смогли бы создать, например, кинотеатр на вашей карте, где можно было бы просматривать фильм на экране, или сделав небольшую надстройку вы могли бы добавить возможность воспроизведения видео на VGUI-панелях. В то время как Source Engine уже позволяет легко смотреть происходящее в других местах на карте (с использованием Point_camera s и func_monitor s), этот метод позволяет воспроизводить реальные видеофайлы с жесткого диска. Этот код / мини-тутор даст вам все необходимое, чтобы достаточно легко добавить точечные объекты (entities) для поддержки AVI-материалов на вашу карту. Эти объекты также позволят использовать определенные текстуры в качестве монитора, и выполнять различные действия - воспроизвденеие, пауза, смена видеотрека, предварительно один кадр, и т.д. Данный код во многом опирается на статью Procedural Materials (Процедурные материалы), которая предоставлена ребятами из Valve (еще раз спасибо, Том и Майк!)
Contents
Принцип работы
Procedural Materials (Процедурные материалы) позволяют определить некоторые материалы как "procedural" в том смысле, что значения их пикселей задаются программно во время работы; мы получаем 2D массив пикселей, представляющий собой текстуру, которую можно динамично изменять (например, цвет). В данном случае, мы читаем кадр из фильма (который меняется в зависимости от времени воспроизведения), считываем поочередно пиксели и записываем их в текстуру. Так как это делается примерно 60 раз в секунду, мы получаем хорошее, плавное воспроизведение фильма.
Установка
К сожалению, требуется установить немалое количество кода и изменить некоторые параметры проекта. Это не сложно, и если вы будете следовать этим инструкциям, то все будет работать.
- Прочитайте тутор Procedural Materials (Процедурные материалы). Все в этом уроке строится от этого.
- Скачайте этот архив (~15 МБ). AVIMaterial.zip. В нем есть все необходимые файлы.
- Скопируйте файл vf32.lib из C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Lib (or wherever your VS2003 is installed) в C:\MyMod\src\lib\public (изменить путь для вашего мода.) Интегрируйте vf32.lib в ваш проект Visual Studio. vfw32.lib это библиотека видео для Windows, функции которой мы будем использовать для чтения AVI.
- Скопируйте AVIMaterial.h и AVIMaterial.cpp в C:\MyMod\src\dlls. Эти файлы объявляют и определяют CAVIMaterial на серверной стороне, объекты которой управляют фильмом.
- Скопируйте c_aviMaterial.h, c_aviMaterial.cpp, AVIMaterialProxy.h, AVIMaterialProxy.cpp, AVIMaterialRegen.h, и AVIMaterial.cpp в C:\SecondCity\src\cl_dll. c_aviMaterial объявляет и определяет C_AVIMaterial, CAVIMaterial. AVIMaterialProxy объявляет и определяет CAviMaterialProxy, который контролирует экземпляры CAviTextureRegen, который фактически создает графику фильма.
- Добавить запись о SecondCity.fgd к fgd-файлу(-ам) вашего мода в Hammer. Это позволит видеть AVIMaterial в Hammer как отдельный точечный объект (entity).
- Скопируйте avi_panel1.vmt, avi_panel1.vtf, avi_panel2.vmt, и avi_panel2.vtf в папку materials вашего мода. Они должны быть в корне.
- Скопируйте quickone.vmf и quickone.bsp в папку maps.
- Скопируйте newyorkmap.avi и category.avi в корень диска c: Вы можете поместить их в другой каталог, но тогда придется у AVIMaterial в параметре quickone указать новый путь. Не забывайте об этом.
- Откройте Visual Studio, и добавьте vfw32.lib в ваш проект. Правой кнопкой мыши по клиенту->Add->Add Existing Item (Добавить имеющийся элемент).
- В Visual Studio, добавьте c_aviMaterial.h, AVIMaterialProxy.h, и AVIMaterialRegen.h в папку Header Files (Заголовочные файлы) вашего проекта.
- В Visual Studio, добавьте c_aviMaterial.cpp, AVIMaterialProxy.cpp, и AVIMaterialRegen.cpp в папку с исходниками вашего проекта (клиент).
- В Visual Studio, добавьте AVIMaterial.h в папку Header Files (Заголовочные файлы) вашего проекта (сервер)
- В Visual Studio, добавьте AVIMaterial.cpp в папку Header Files (Заголовочные файлы) вашего проекта (сервер)
- Скомпилируйте ваш проект и запустите его. На карте quickone вы должны увидеть два фильма по центру комнаты.
Как это работает
CAVIMaterial и C_AVIMaterial являются типичными клиент/сервер объектами (те самые entity). Пожалуйста, ознакомьтесь со статьями Networking entities для получения дополнительной информации. Когда карта загружена (на примере quickone), avi_panel1 и avi_panel2 имеют соответствующие CAviMaterialProxy экземпляры для них. (The line
EXPOSE_INTERFACE( CAviMaterialProxy, IMaterialProxy, "AviRenderer" IMATERIAL_PROXY_INTERFACE_VERSION );
links them to the AviRenderer listed as the proxy in avi_panel1.vmt and avi_panel2.vmt. When Init()ted, each proxy is passed in a pointer to the IMaterial they are in charge of. CAviMaterialProxy also has a static function that searches a static array of CAviMaterialProxy on the basis of their texture names. This is a clunky way of allowing C_AVIMaterials to link up to the appropriate textures (otherwise you couldn't control which movie was getting drawn on which texture.) I'm sure there's a better way to do this, please let me know if you know of one. The CAviMaterialProxy instances are used internally by Source to draw the procedural textures; we mainly use it to passthrough commands to its CAviMaterialRegen, however. Once a C_AVIMaterial is passed its movie filename and texture, it searches the proxies for the right one (meaning the proxy that is drawing to its texture), and records it. It then tells the proxy (which passes it to the regenerator) the movie file to load. From then on, the CAVIMaterial entity can be manipulated using the normal Source I/O system . The code is commented, please see it for implementation details.
How to use it
Currently, the CAVIMaterial entity has the following inputs:
- Play - This will start playing whatever movie is set as the current movie. If the movie has not played before, it will start from the beginning; if the movie is paused, it will resume playing back from where it was paused. If the movie is already playing, it has no effect. If no movie is loaded, nothing will happen.
- Pause - This will pause the currently-playing movie, leaving the last frame visible. If the movie is already paused, nothing will happen. If there is no currently-playing movie, nothing will happen.
- Stop - This will end and clear the currently playing movie.
- AdvanceFrame - This will advance the movie one frame. If no movie is currently loaded, nothing will happen. This input assumes the movie is already paused, but doesn't enforce it.
- SetMovie - This input takes a string which should be the absolute path to an AVI on disk, and sets that AVI to be the currently loaded movie. You should stop playing any active movies before setting a new movie.
The CAVIMaterial entity has the following KeyValues:
- TextureName - This is the name of the material that this CAVIMaterial will draw to. If a material is in more than one place of the map, the movie will be shown on all of them.
- MovieName - This is the absolute path on disk to the AVI that will be initially loaded by the CAviMaterialRegen.
The CAVIMaterial entity has the following spawn flags:
- Play movie immediately - This will start the movie specified in MovieName playing as soon as the map loads.
- Loop movie - This will cause the movie to begin playing again immediately after it is completed.
Potential improvements
There are a lot of improvements one could make to this code. A few of these are listed below.
- Fix bugs in the existing code. I am only moderately familiar with C++ and the Source engine, so if something looks wrong or unnecessarily awkward, it definitely may be. Please let me know, and I'll be happy to update the code.
- Add various movie controls. You could straight-forwardly add fast-forward, rewind, slo-mo, etc.
- Optimize this code. This code has not been tweaked for performance at all; it runs acceptably, but there is definitely room for speedup in CAviMaterialRegen::RegenerateTextureBits().
- Have the path for the movies use Steam directories. Right now, the path to the movie is always an absolute directory, but it should probably be specified in relation to the Steam directories if you actually want to use movies in your mod.
- Add support for other movie types You could presumably add support for other movie types (Divx, WMV, etc.), but you're probably better off using a program to convert other movie types to AVIs offline. We've had luck with the open source | ffmpeg, but YMMV.
- Improve entity use It's kind of weird/clunky to have to specify the texture name to draw to in the Hammer entries for the CAVIMaterials. There's probably a better way to do this; figure it out!
- Add more avi_panels With avi_panel1 and avi_panel2, you can only show two distinct movies at the same time; if you need more than that, simply copy and paste avi_panel1.vtf and avi_panel1.vmt and rename them appropriately. Don't forget to update the .vmt to point at the new .vtf.
Disclaimer
This code is far from bug-free or production ready. It definitely works, though, and it certainly won't kill your computer or anything. If you find bugs, please contact me. I will be using this code in the future, and will certainly find bugs of my own, and I'll update the code as necessary. I wanted to get this code out and available to people before I lost the drive to write it up and everything, so here it is, warts and all.
Acknowledgments
As mentioned above, this code is really just an aggregation of different peoples' work. Zerodegrez did the work for the original procedural materials tutorial, and all of this procedural code is taken straight from that. Tom Leonard at Valve was nice enough to give me the AVI opening/reading code, and Mike Durand at Valve was very friendly and helpful about getting procedural materials working again in the SDK (turns out they were fixed in the Aug, 2006 update, who knew?) Thanks again. Please let me know if you have any questions, problems or suggestions. Good luck!