Формат моделей и 3D-композиция
FOnline использует описания моделей .fo3d, чтобы собрать на клиенте запечённые 3D-меши, преобразованные исходные анимации, выбираемую слоями экипировку, дочерние модели, частицы, переопределения материалов и объёмы отсечения.
Это руководство описывает авторинг и runtime-модель. Точный контракт текущей ревизии приведён в сгенерированных справочниках токенов, ресурсов и лимитов, правил валидации и в канонической JSON-модели.
Область и авторитетные источники
Контрактом владеют:
Source/Tools/ModelMeshBaker.cppиSource/Common/ModelMeshData.*для импорта и проверки мешей.fbx/.obj, а также mesh-only payloadLFMODMSH;Source/Tools/ModelSourceLoader.*,Source/Tools/ModelAnimationConverter.*иSource/Common/ModelAnimationData.*для извлечения исходного скелета и клипов, анализа совместимости, преобразования Ozz и нативного rig payload;Source/Tools/ModelInfoBaker.cppдля разбора.fo3d, раскрытия include, проверки зависимостей и исходников, сериализации model info, создания runtime rig и метаданных анимации;Source/Client/ModelManager.*,ModelHierarchy.*,ModelInformation.*,ModelInstance.*иModelAnimation.*для строгой runtime-загрузки, общих неизменяемых данных, композиции и позы экземпляра, контроллеров анимации и рисования;Source/Frontend/Rendering.hи сгенерированный CMake-интерфейс проекта для compile-time лимитов моделей;- тесты baker, mesh data, source loader, animation data/converter/runtime, совместимости скелета, Ozz и client engine как исполняемые примеры грамматики, форматов, преобразования, загрузки и ошибок.
Это переиспользуемая документация движка. Встраивающий проект владеет конкретными именами моделей, значениями слоёв, enum анимаций, художественными правилами, экипировкой, игровыми таймингами и сценами визуальной проверки.
Полная машинная модель генерируется из BuildTools/ModelFormatInterface.json. Генератор напрямую сравнивает документированный набор токенов с ModelDescriptionParser::ParseToken; расхождение парсера и документации останавливает проверку.
Обзор конвейера
Конвейер моделей состоит из двух упорядоченных baker и общих модулей загрузки и преобразования:
ModelMeshBakerс порядком4импортирует.fbxи.objи записывает версионированный mesh-only ресурсLFMODMSHпо тому же пути и с тем же расширением. Клипы и изменяемая поза в этот payload не входят.ModelInfoBakerс порядком6разбирает конкретные.fo3d, проверяет ссылки и свежесть исходников, загружает выбранные скелеты и клипы черезModelSourceAssetCache, преобразует канонический runtime rig и записываетLFMODINFс обязательным payloadLFOZZRIGпо пути.fo3d. Он также создаётModelAnimationInfo.foinfoдля общих длительностей и границ.
Клиент не разбирает авторский текст, FBX или OBJ. ModelManager загружает общие иерархии мешей, ModelInformation строго читает неизменяемое описание и rig, а каждый ModelInstance владеет изменяемыми контроллерами, матрицами позы, дочерними объектами, частицами и render-композицией. Старые payload без заголовка и частичные fallback для rig отклоняются.
Файлы с basename TEMPLATE_ предназначены только для include. Они влияют на конкретные описания и timestamps запекания, но не создаются как самостоятельные .fo3d-ресурсы или секции ModelAnimationInfo.foinfo.
Контракт исходного меша
Поддерживаемые входы
Текущий ModelMeshBaker сканирует только:
.fbxдля скелетных и статических мешей, skinning, имён diffuse-текстур материалов, исходных скелетов и анимационных клипов;.objдля статических моделей, присоединяемых объектов и объёмов отсечения.
Устаревшие .x и .3ds не являются текущими входами. ModelMeshBaker не выберет файл с таким расширением.
Поведение импорта
Mesh baker и source loader используют закреплённую версию ufbx, но имеют разных владельцев. Первый выдаёт иерархию, bind, vertices, indices, skin и materials; второй извлекает проверенные данные skeleton/TRS/clip для преобразования анимации. Встроенные файлы игнорируются, skinning вычисляется, веса очищаются, а отсутствующие normals обрабатываются детерминированно.
Требования к исходникам:
- faces должны быть заранее триангулированы;
- конкретная модель должна содержать хотя бы один drawable mesh;
- имена nodes становятся именами bones, а nodes с геометрией — именами drawable meshes;
- используйте один material на drawable node, если важно детерминированное владение текстурой;
- файловая текстура
DiffuseColorпервого material становится texture slot0; - имена текстур сохраняются без исходной директории и затем разрешаются относительно запечённого меша;
- используется только первый skin deformer;
- число skin clusters должно помещаться в
FO_MODEL_MAX_BONES; - у вершины сохраняются только
FO_MODEL_BONES_PER_VERTEXвлияний, после чего веса нормализуются; - меш без skin получает детерминированную привязку к одной кости;
- имена animation stacks являются clip names для
Anim, но клипы загружаются из исходника и входят в rig model info, а не вLFMODMSH; - прямые
.fbx-attachments должны содержать только rest pose; если у присоединённого исходника есть клипы, используйте дочерний.fo3dс явнымиAnim; - внешние animation sources должны содержать только hierarchy и animation. Drawable geometry запрещена, кроме точного файла с временным исключением
AllowAnimationGeometry.
Source loader отклоняет не-конечные transforms, дубликаты clip names без учёта регистра, неверные durations и key times, превышение лимитов и глубины, а также некорректные отношения skeleton. Animation sources могут добавлять совместимые канонические joints без физического ModelBone; физические meshes и cuts остаются в базовой иерархии.
Текущие лимиты по умолчанию:
| Опция проекта | Runtime-константа | По умолчанию |
|---|---|---|
FO_MODEL_LAYERS_COUNT |
MODEL_LAYERS_COUNT |
30 |
FO_MODEL_MAX_TEXTURES |
MODEL_MAX_TEXTURES |
8 |
FO_MODEL_MAX_BONES |
MODEL_MAX_BONES |
54 |
FO_MODEL_BONES_PER_VERTEX |
MODEL_BONES_PER_VERTEX |
4 |
Это контракты бинарной формы и shader layout. При переопределении проект обязан использовать одинаковые значения в client binaries, запечённых моделях, Critter.ModelLayers, effects и packages.
Лексический синтаксис
.fo3d представляет собой последовательность токенов, разделённых whitespace:
#и;начинают комментарий;- quoted strings и escape syntax отсутствуют;
- paths и names не могут содержать whitespace;
- в одной строке может быть несколько directives;
- directive забирает обязательные arguments, затем разбор продолжается со следующего token;
- неизвестный token или отсутствующий argument является ошибкой запекания;
- integer arguments принимают числа, явные booleans или enum names из metadata resolver;
- float arguments должны быть конечными числами.
Компактная запись допустима:
Layer 1 Value 2 Attach Hat.fbx Link Head RotY 180 Texture 0 Hat.tga
Для сопровождения размещайте structural selectors (Layer, Value, Root, Attach) перед относящимися к ним modifiers.
Минимальные описания
Минимальная статическая модель:
Model Props/Crate.obj
Скелетная модель с анимациями и attachment, выбранным слоем:
Model Characters/Human.fbx
RotationBone Spine
Anim CritterStateAnim.Unarmed CritterActionAnim.Idle ModelFile Idle
Anim CritterStateAnim.Unarmed CritterActionAnim.Walk ModelFile Walk
Layer 1
Value 1
Attach Items/Hat.fbx Link Head
Enum names зависят от metadata встраивающего проекта. Числа в тестах движка доказывают поведение parser, но не являются рекомендуемым словарём проекта.
Include и шаблоны
Include разбирает другой файл inline:
Include TEMPLATE_Humanoid.fo3d mesh Human.fbx scale 0.9
После path идут пары name/value. До tokenization каждое буквальное %name% в подключаемом тексте заменяется значением:
# TEMPLATE_Humanoid.fo3d
Model %mesh%
Scale* %scale%
Правила include:
- path задаётся относительно файла, содержащего
Include; - пути
Model,AttachиCutв подключённом тексте задаются относительно include-файла; - внешний файл
Animпозднее разрешается относительно итогового конкретного.fo3d; - include arguments занимают остаток строки, поэтому следующую directive на той же строке размещать нельзя;
- replacements являются простой заменой текста, а не token-aware операцией;
- включённый текст разделяет parser state с вызывающим файлом;
- рекурсивные include отклоняются;
- самый новый timestamp во всём include graph управляет incremental rebake.
Предпочитайте самодостаточные шаблоны, явно устанавливающие Root, Layer, Value и Mesh. Скрытая зависимость от состояния вызывающего файла затрудняет работу людей, ИИ и валидаторов.
Состояние парсера
Parser отслеживает выбранные Layer, Value, текущий link для modifiers и текущий selector Mesh для Texture и Effect.
В начале файла текущим link является default root, поэтому top-level transforms и materials применяются к базовой модели даже без Root.
Layer или Value изменяет selector, очищает Mesh и переводит текущий link на dummy object. После выбора пары layer/value запишите Root, Attach или AttachParticles до transform, material, disable или cut. Modifiers при активном dummy link разбираются, но отбрасываются.
Directive для возврата Layer к начальному -1 нет. Все default-root declarations должны идти до первого Layer либо в более раннем include.
Root также очищает Mesh. Attach и AttachParticles создают link и очищают Mesh. Размещайте Mesh после selector link, к которому он относится.
Link сохраняется только для не-default и не-dummy link. На default root он игнорируется. Link у layer Root сериализуется, но дочернего объекта для присоединения нет; не задавайте его.
Слои и значения
Layer выбирает индекс фиксированного массива:
0 <= layer < FO_MODEL_LAYERS_COUNT
Value выбирает точное целое, определённое проектом. Ноль означает inactive и не может создать layer entry Root, Attach или AttachParticles.
В runtime ModelInstance::PlayAnim копирует или повторно использует массив слоёв, применяет точные overrides AnimLayerValue, сбрасывает модель к default root, находит совпавшие пары Layer/Value, применяет root modifiers и materials, создаёт или сохраняет children и particles, удаляет больше не выбранные элементы и перестраивает combined meshes при изменении композиции.
Layer value — это состояние render-композиции, а не только косметические metadata. Оно может менять transforms, animation speed, geometry, materials, effects, cuts, child models, particles и batching. Семантика каждого layer/value принадлежит документации и тестам проекта.
Модификаторы корня
Без выбранного layer Root выбирает default link базовой модели:
Root
Scale 0.9
RotX 90
При выбранной ненулевой паре layer/value он создаёт условный root modifier:
Layer 3
Value 2
Root
DisableMesh Torso
Texture 0 Armor.tga
Условные root links могут добавлять transforms и speed multipliers, переопределять textures/effects, отключать layers и meshes, добавлять cuts. Дочернюю модель они не создают.
Присоединение моделей
Attach требует выбранного layer и ненулевого value:
Layer 1
Value 4
Attach Weapons/Rifle.fo3d Link RightHand
Путь дочернего объекта задаётся относительно declaring file.
Присоединение к одной кости
При Link <bone> весь child становится дочерним объектом одной проверенной кости:
Attach Hat.fbx Link Head
Rotation, translation, scale, speed, materials, disables и cuts этого link применяются внутри child instance.
Общий скелет
Без Link runtime сопоставляет одноимённые кости child и parent:
Attach ArmorTorso.fbx
Используйте это только для одежды и частей тела, созданных под один skeleton. Если общих bones нет, runtime creation завершается ошибкой.
Дочернее описание или прямой меш
Используйте Attach child.fo3d, когда child нужны собственные base mesh, layers/attachments, default material/effect policy, cuts, animations или rendering flags. Для простой запечённой иерархии достаточно прямого .fbx / .obj. Дочерний .fo3d запекается самостоятельно; parent проверяет существование descriptor и bone из Link.
У direct attachment нет description-level коррекции scale. Maximum-axis extent его static bounds должен оставаться в диапазоне Baking.ModelAttachmentMinExtent .. Baking.ModelAttachmentMaxExtent; иначе bake завершается ошибкой с измеренным extent и limit. Используйте child .fo3d, если composition требует явного scale. Mesh nodes с отрицательным transform determinant раньше отклоняет ModelMeshBaker: reset/freeze mirrored geometry к положительному transform до export, не рассчитывая на переворот normals и winding в baker-е.
Присоединение частиц
AttachParticles выбирается слоем:
Layer 8
Value 1
AttachParticles Particles/Jet.spk Link Backpack
MoveY 0.15
RotY 90
Particle path является глобальным путём запечённого ресурса, а не путём относительно .fo3d. Ссылайтесь на созданный .spk или .efk, а не на авторский .spark или .efkproj. Всегда задавайте корректную bone в Link: runtime-создание частиц требует её.
MoveX/Y/Z и RotY определяют placement частицы. Instance живёт, пока активна точная пара layer/value, и удаляется при изменении композиции. XML, SPARK objects, renderer fields, effects/textures, runtime cache и визуальная проверка принадлежат формату частиц. Частицы на bones используют прямой путь 3D-композиции, а не selector atlas/direct-scene у ParticleSprite.
Преобразования и скорость
Поля link: RotX/Y/Z в градусах, MoveX/Y/Z в координатах модели, ScaleX/Y/Z и playback multiplier Speed. Scale задаёт все три оси.
У каждого поля есть assignment, additive и multiplicative формы:
Scale 0.9
Scale+ 0.1
Scale* 1.5
RotY 90
RotY+ 15
RotY* 0.5
Для форм + и * действует особая инициализация: если поле равно нулю, operand становится его значением; иначе выполняется обычное сложение или умножение. Поэтому template может использовать Scale* 0.9 или Speed* 1.2 без предшествующего assignment, а порядок declarations наблюдаем.
В runtime ноль означает identity/no contribution. Ненулевые transforms перемножаются с текущим model transform. Отрицательный итоговый Speed запрещён при запекании; ноль означает отсутствие вклада в скорость.
Меши, текстуры и эффекты
Mesh выбирает drawable node:
Mesh Torso
Texture 0 Armor.tga
Effect Effects/Armor.fofx
Mesh All очищает selector, и следующие material directives применяются ко всем drawable meshes текущей модели link. Subset устарел: parser забирает argument и пишет warning, но ничего не выбирает.
Текстуры
Texture <slot> <name>
Slot должен лежать в [0, FO_MODEL_MAX_TEXTURES). Для обычного имени выбранный Mesh должен существовать и быть drawable, path разрешается относительно target mesh, а ресурс должен быть запечён. Imported diffuse texture является default для slot 0; остальные slots изначально пусты.
В attached child Parent копирует первую совпавшую текущую texture родителя в том же slot, а Parent_<mesh> — texture конкретного parent mesh:
Texture 0 Parent_Torso
Не используйте Parent в root description; при нескольких parent meshes задавайте suffix явно.
Эффекты
Effect Effects/SkinnedArmor.fofx
Effect paths являются глобальными baked-resource paths. Parent и Parent_<mesh> копируют текущий effect родителя по тем же правилам. Meshes объединяются в draw batch только при совместимых effect, texture set и bone capacity, поэтому material overrides могут менять batching и требуют измерения на типичных композициях.
Отключение слоёв и мешей
DisableLayer принимает layer indices через дефис:
DisableLayer 5-6-7
При активном link эти slots пропускаются внутри model instance.
DisableMesh принимает drawable node names через дефис, а DisableMesh All сохраняет wildcard и отключает все meshes:
DisableMesh Hair-HelmetBase
Используйте отключения для взаимоисключающей композиции, но явно документируйте project ownership слоёв. Циклическая и зависящая от порядка политика плохо тестируется.
Объёмы отсечения
Cut удаляет geometry из выбранных слоёв combined mesh:
Cut CutVolumes/Helmet.obj All HeadVolume - - -
Шесть arguments:
- path
.fbx/.objcut volume относительно declaring file; - target layers через дефис либо
All; - drawable shape names из cut file через дефис либо
All; - первая unskin bone либо
-; - вторая unskin bone либо
-; - unskin shape,
~shapeдля reversed behavior либо-.
All для layers раскрывается во все compile-time layers, кроме текущего выбранного. В default-root scope включаются все layers. All для shapes выбирает все drawable shapes, кроме отдельно указанного unskin shape.
Runtime классифицирует cut shape по числу запечённых vertices: ровно 36 означает axis-aligned box bounds, любое другое число — sphere radius по X extent. Это правило формата движка, а не общий mesh heuristic. Создавайте простые специализированные cut assets и проверяйте результат визуально.
Обе unskin bones задаются вместе; unskin shape требует обеих. Все bones и drawable shapes проверяются при запекании. Любой cut отключает обычный culling composed model, поэтому это correctness feature с render cost.
Управление рендерингом
Автоматический layout model sprite
DrawSize и ViewSize удалены. ModelInfoBaker пишет aggregate bounds, idle-priority view/name bounds и per-animation bounds в ModelAnimationInfo.foinfo версии 2. Клиент проецирует их для каждого направления, расширяет с учётом children и layers и вычисляет offscreen frame, visual anchor, lighting envelope и interaction/view rectangle.
Каждый non-particle child link сериализует проверенный root-space AABB; default
link и particle links не содержат geometry payload. При расчёте envelope baker
учитывает disabled meshes, nested descriptions, link transforms и sampled
animations родителя. Runtime framing объединяет bounds активных animations с
envelopes выбранных links и проецирует только их corners без per-frame
weighted-vertex sweep. Live particles всё ещё могут вызвать ограниченный
expansion/rerender при выходе за baked geometry envelope. Авторы настраивают
source transforms, animation reach, attachments и Render.ModelProjFactor, а
не фиксированные пиксельные прямоугольники в .fo3d.
Проверяйте все направления и типичные animations в видимом клиенте. Для диагностики clipping, пустого пространства, polygon edges и crop placement используйте Game.DumpAtlases() или Dump atlases в Mapper. Бинарный и runtime-контракты описаны в Baking Pipeline и Frontend и рендеринг.
Прочие флаги
DisableShadowотключает тень модели.DisableAnimationInterpolationвыбирает nearest-key sampling.DisableBackwardAnimвыбирает forward walk/run вместоWalkBack/RunBackи совмещает look direction с движением.RotationBone <bone>включает movement overlay controller и directional rotation torso/head.FastTransitionBone <bone>сбрасывает transition state нового child на указанной link bone.
Граница анимации
Animation directives .fo3d:
Anim <state> <action> <ModelFile|animation-source> <clip|~clip|Base>
AnimSpeed <state> <action> <positive-factor>
AllowAnimationGeometry <external-animation-file>
AnimLayerValue <state> <action> <layer> <value>
StateAnimEqual <from> <to>
ActionAnimEqual <from> <to>
FastTransitionBone <bone>
RotationBone <bone>
DisableAnimationInterpolation
DisableBackwardAnim
Model Animation описывает first-entry-wins tuples, ModelFile/Base/~clip, совместимость skeleton и rig conversion, one-step aliases, effective duration, common и loaded-client lookup, substitutions и validation.
AnimLayerValue применяется к точной запрошенной паре до model composition. Alias resolution относится к animation lookup; не считайте, что alias также переписывает key для layer override.
AllowAnimationGeometry — узкий migration aid. Он называет один точный внешний файл из Anim, разрешается от конечного .fo3d, используется только baker validation и не сериализуется. Duplicate paths/targets, невыбранные файлы и оставшиеся после удаления geometry исключения являются ошибками. Исправьте export, сохранив необходимую helper/bone hierarchy, и удалите исключение в той же миграции.
3D skeletal animation отделена от 2D-контракта NextX / NextY в Sprite Root Motion.
Runtime-загрузка и кеширование
ModelManager::CreateModel(name) принимает запечённый .fo3d с полной composition semantics либо baked mesh path с простой rest-pose model без declarations и animation controller.
Descriptions и mesh hierarchies кешируются по resource name. Неизменяемые clips, remaps, bindings и canonical skeleton принадлежат ModelInformation; mutable timelines, poses, matrices, children и procedural transforms — каждому ModelInstance. При смене слоёв активные children повторно используются по stable baked link id, а переставшие совпадать children и particles удаляются.
Combined mesh generation объединяет совместимые видимые meshes, пока effect, textures или bone capacity не требуют нового batch. Cuts применяются после объединения parent и child meshes.
Не изменяйте и не разбирайте binary payload запечённых .fo3d, .fbx или .obj из project scripts. Их layout является приватным контрактом baker/runtime.
Поведение при ошибках
ModelInfoBaker отклоняет или сообщает: отсутствующий Model; недоступные, устаревшие или malformed baked meshes и sources; primary mesh без drawable geometry; отсутствующие textures/effects/particles/children/cuts; неверные layer/texture indices; нулевые layer values для Root/Attach; отсутствующие bones или meshes; malformed includes; invalid/non-finite numbers; отрицательный Speed; неположительный AnimSpeed; неизвестные enums, clips или несовместимые skeleton; attached FBX с clips; внешнюю animation geometry без точного временного исключения; mirrored mesh nodes; direct FBX/OBJ attachments вне настроенного диапазона Engine world units; неверные cut combinations; неизвестные tokens.
Runtime повторяет критические binary и range checks. Runtime exception означает повреждённые или устаревшие baked data либо пробел в validation; не ловите её для тихой подстановки несвязанной модели.
Предупреждение об устаревшем контенте
Не выводите текущую поддержку из старых FOnline-проектов:
.xи.3dsне выбираются текущим mesh baker;AnimEqualзаменён наStateAnimEqualиActionAnimEqual;CalculateTangentSpace,RenderFrameиRenderFramesне являются текущими tokens;Subsetоставлен только как warning path и ничего не выбирает.
Сначала переведите legacy assets на текущие source formats и grammar, затем проверяйте против текущей ревизии Engine. Старый проектный контент — свидетельство исторического использования, а не нормативная спецификация.
Практики авторинга
- Размещайте default-root declarations до первого
Layer. - Оставляйте у concrete description ровно один намеренный итоговый
Model. - Начинайте include-only files с
TEMPLATE_. - Пусть template явно задаёт selector context.
- Используйте enum names и project layer constants из metadata.
- Документируйте каждый layer index, allowed value, owner и конфликтующий layer.
- Делайте отдельный drawable node для независимо заменяемого material/effect.
- Используйте direct mesh для простого prop, а child
.fo3dдля переиспользуемой композиции. - Всегда задавайте
Linkу particle attachment. - Соблюдайте регистр paths, bones, meshes, animation stacks и effects.
- Делайте cut volumes простыми и специализированными.
- Проверяйте матрицу сочетаний layers, а не только каждый attachment отдельно.
- Экспортируйте external animations без drawable geometry, сохраняя нужную hierarchy.
- Считайте каждый
AllowAnimationGeometryвременным долгом с владельцем ремонта. - После изменений loader, converter, mesh wire или animation sources выполняйте force bake и затем incremental bake.
Для ИИ-авторинга фиксируйте parser state перед каждым modifier:
current layer = 3
current value = 2
current link = Attach Armor.fo3d
current mesh = Torso
next directive = Texture 0 Parent_Torso
Если состояние нельзя назвать однозначно, разбейте компактную строку и явно задайте selectors.
Процесс проверки
-
Перегенерируйте и проверьте справочник:
python BuildTools\docs_model_format.py --write python BuildTools\docs_model_format.py --check python -m unittest BuildTools.tests.test_docs_model_format -
Запустите focused Engine tests:
.\Binaries\Tests-Windows-win64\LF_UnitTests.exe "ModelBaker*" -
Перезапеките встраивающий проект:
cmake --build Build\Auto --config RelWithDebInfo --target BakeResources - В видимом клиенте проверьте framing/anchors/crop bounds, все layer/value, оба вида attachment, particles, inheritance textures/effects, disables, cuts, idle/movement/turn/backward/action animations, shadows и interpolation.
- Зафиксируйте project-specific layer semantics, ожидаемые screenshots и regression routes в документации проекта.
Чистый bake доказывает grammar, asset closure, enums/ranges и serialization. Он не доказывает качество позы, scale, clipping, материалы, blending, cut geometry, interaction bounds или performance.
Маршрутизация изменений
- При изменении
.fo3dtokens, parser state, include, path rules или validation обновите руководство,BuildTools/ModelFormatInterface.json, generated outputs и focused tests. - При изменении импорта
.fbx/.obj, skinning, materials, animation conversion или model limits обновите asset/limit contract и native mesh-baker tests. - При изменении layers, attachments, particles, transforms, materials, cuts, batching или flags обновите composition/runtime sections и проверьте видимый client scene.
- Animation tuples, aliases, speed, duration и script lookup принадлежат также Model Animation.
- 2D frame offsets и sprite phase принадлежат Sprite Root Motion.
- Каталоги моделей и семантика слоёв принадлежат только документации встраивающего проекта, которая ссылается на этот контракт.