Skip to content

Commit

Permalink
fix filter_glob & add file handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
marekzajac97 committed Aug 19, 2024
1 parent c146b4e commit 23f704e
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 66 deletions.
50 changes: 25 additions & 25 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,7 @@ There are two ways of importing BF2 meshes. One is to use `Import -> BF2` menu t
- Each Lod may contain multiple child objects that will be exported as separate ObjectTemplates using different geometry parts, each Lod must contain the same hierarchy of them. StaticMeshes or SkinnedMeshes usually don't have any parts, so Lod will be just a single object, but for BundledMeshes you might want to have multiple geometry parts (e.g. "hull" as root and a "turret" and "motor" as its child objects). Those objects cannot be empty, each one must contain mesh data to export properly! However, the mesh data itself may have no geometry (verts & faces deleted), which is useful for exporting things as invisible but separate logical objects (e.g. the `Engine` ObjectTemplate of the vehicle).
- Each object in the hierarchy should have its corresponding BF2 ObjectTemplate type set (e.g. `Bundle`, `PlayerControlObject` etc). You will find this property in the `Object Properties` tab, it defaults to `SimpleObject`. It can be left empty when an object is intended to be exported as a separate geometry part but not as a separate ObjectTemplate (e.g. an animatable weapon part of the handheld `GenericFirearm`).

## Materials and UVs
- Each material that is assigned to any visible mesh must be set up for export. To setup BF2 material go to `Material Properties`, you should see `BF2 Material` panel there. Enable `Is BF2 Material` and choose appropriate settings: `Alpha Mode`, `Shader` and `Technique` (BundledMesh/SkinnedMesh only) as well as desired texture maps to load.
- For StaticMesh: There will be 6 texture slots for Base, Detail, Dirt, Crack, Detail Normal, and Crack Normal. Only Base texture is mandatory, if others are not meant to be used, leave them empty.
- For BundledMesh: There should be 3 texture slots for Diffuse, Normal, and Wreck. Only Diffuse texture is mandatory, if others are not meant to be used, leave them empty.
- For SkinnedMesh: There should be 2 texture slots for Diffuse and Normal. Only Diffuse texture is mandatory, if Normal is not meant to be used, leave it empty.
- Clicking on `Apply Material` changes some material settings, loads textures and builds a tree of Shader Nodes that try to mimic BF2 rendering.
- Each LOD's mesh must have a minimum of 1 and a maximum of 5 UV layers assigned and each UV layer must be called `UV<index>`, where each one corresponds to the following texture maps:
- For StaticMesh UV0 = Base, UV1 = Detail, UV2 = Dirt, UV3 (or UV2 if Dirt layer is not present) = Crack and the last one (always UV4) is the Lightmap UV, which can also be auto-generated when toggled in the export options.
- For BundledMesh and SkinnedMesh there's only UV0 for all texture maps.
- Export requires one UV map to be chosen for tangent space calculation, this must be the same UV that was used to bake the normal map, for static meshes (which reuse textures) it should likely be UV1 (Detail Normal).

## Collision meshes
- Each object may contain collision mesh data. To add it, you need to create an empty child object that is prefixed with `NONVIS__`. This new object should have a maximum of 4 child objects (suffixed with `_COL<index>`) containing collision mesh data, each corresponding to a specific collision type: Projectile = COL0, Vehicle = COL1, Soldier = COL2, AI (navmesh) = COL3. Collision meshes should only be added under object's Lod0 hierchies.
- Each COL can have an arbitrary number of materials assigned, no special material settings are required, object's material index-to-name mapping will be saved inside the `.con` file.

## Tank tracks skinning (BundledMesh)
BF2 BundledMeshes support a basic method of skinning allowing one "bone" per vertex, where the "bone" is another object (geometry part). In other words, it allows "moving" some of the vertices from one object (geometry part) to another so that individual vertices that make up a face get split among different parts. These parts can be affected by in-game physics differently which may cause some faces to stretch and deform. This technique is most commonly used for setting up tank tracks by splitting them up and "linking" track pieces to wheel objects. To achieve this in Blender create a new [Vertex Group](https://docs.blender.org/manual/en/latest/modeling/meshes/properties/vertex_groups/index.html) named **exactly** the same as the child object that the vertices are supposed to be transferred to and add them to the group. Make sure that a single vertex is assigned to **exactly one** vertex group, or you will get an export error.

## Animated UVs (BundledMesh)
To set up animated UVs go to `Edit Mode`, select specific parts (vertices/faces) of your mesh that should use UV animation and assign them to proper sets using `Mesh -> BF2` menu, choosing Left/Right Tracks/Wheels Translation/Rotation. You can also select vertices/faces currently assigned to those sets using `Select -> BF2` menu. Vertices assinged to "wheel rotation" set will additionaly require setting up the center point of UV rotation for each wheel individually. Select all vertices, and position the 2D cursor to the wheel center in the UV Editing view, then select `Mesh -> BF2 -> Set Animated UV Rotation Center`. Repeat the process for all wheels.

## Rigging (SkinnedMesh)
In order to rig your model, you must import the BF2 skeleton into your scene. When rigging soldiers you will need two skeletons `1p_setup.ske` for 1P (Geom 0) and `3p_setup.ske` 3P (Geom 1). The first step is to switch to `Pose Mode` and pose the armature(s) to best match your mesh(es). When you are done, make sure you apply this pose as the rest pose [Pose -> Apply -> Apply Pose As Rest Pose](https://docs.blender.org/manual/en/latest/animation/armatures/posing/editing/apply.html), then switch to `Object Mode` and for each Lod object go to `Modifiers` tab and [Add Modifier -> Deform -> Armature](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/armature.html), in modifier settings select 'Object' to point to the name of the imported skeleton. Now the hard part, you have to set vertex weights for each Lod, meaning how much each bone affects each vertex of the mesh. You could use automatic weights (which should be a good starting point) as follows: In `Object Mode` select the Armature, go to `Pose Mode`, click `Select -> All`, go back to `Object Mode`, select the mesh while holding the `Shift` key, go to `Wieght Paint` mode use [Weights -> Assign Automatic From Bones](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#assign-automatic-from-bone). Bare in mind that to export properly, each vertex must have at most two weights (be assigned to a maximum of two vertex groups), and all those weights have to be normalized (add-up to 1). You can limit the number of vertex weights in `Weight Paint` mode using [Weights -> Limit Total](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#limit-total) option (make sure it is set to 2). You can normalize weights using [Weights -> Normalize All](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#normalize-all) option. Also, make sure that `Auto Normalize` is enabled in [Weight Paint Tools Settings](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/tool_settings/options.html) when rigging in `Weight Paint` mode.

## Example object hierarchies
### Example object hierarchies

<details>
<summary>StaticMesh</summary>
Expand Down Expand Up @@ -165,6 +141,30 @@ SkinnedMesh_soldier

`[m]` tag indicates that the object contains mesh data.

## Materials and UVs
- Each material that is assigned to any visible mesh must be set up for export. To setup BF2 material go to `Material Properties`, you should see `BF2 Material` panel there. Enable `Is BF2 Material` and choose appropriate settings: `Alpha Mode`, `Shader` and `Technique` (BundledMesh/SkinnedMesh only) as well as desired texture maps to load.
- For StaticMesh: There will be 6 texture slots for Base, Detail, Dirt, Crack, Detail Normal, and Crack Normal. Only Base texture is mandatory, if others are not meant to be used, leave them empty.
- For BundledMesh: There should be 3 texture slots for Diffuse, Normal, and Wreck. Only Diffuse texture is mandatory, if others are not meant to be used, leave them empty.
- For SkinnedMesh: There should be 2 texture slots for Diffuse and Normal. Only Diffuse texture is mandatory, if Normal is not meant to be used, leave it empty.
- Clicking on `Apply Material` changes some material settings, loads textures and builds a tree of Shader Nodes that try to mimic BF2 rendering.
- Each LOD's mesh must have a minimum of 1 and a maximum of 5 UV layers assigned and each UV layer must be called `UV<index>`, where each one corresponds to the following texture maps:
- For StaticMesh UV0 = Base, UV1 = Detail, UV2 = Dirt, UV3 (or UV2 if Dirt layer is not present) = Crack and the last one (always UV4) is the Lightmap UV, which can also be auto-generated when toggled in the export options.
- For BundledMesh and SkinnedMesh there's only UV0 for all texture maps.
- Export requires one UV map to be chosen for tangent space calculation, this must be the same UV that was used to bake the normal map, for static meshes (which reuse textures) it should likely be UV1 (Detail Normal).

## Collision meshes
- Each object may contain collision mesh data. To add it, you need to create an empty child object that is prefixed with `NONVIS__`. This new object should have a maximum of 4 child objects (suffixed with `_COL<index>`) containing collision mesh data, each corresponding to a specific collision type: Projectile = COL0, Vehicle = COL1, Soldier = COL2, AI (navmesh) = COL3. Collision meshes should only be added under object's Lod0 hierchies.
- Each COL can have an arbitrary number of materials assigned, no special material settings are required, object's material index-to-name mapping will be saved inside the `.con` file.

## Tank tracks skinning (BundledMesh)
BF2 BundledMeshes support a basic method of skinning allowing one "bone" per vertex, where the "bone" is another object (geometry part). In other words, it allows "moving" some of the vertices from one object (geometry part) to another so that individual vertices that make up a face get split among different parts. These parts can be affected by in-game physics differently which may cause some faces to stretch and deform. This technique is most commonly used for setting up tank tracks by splitting them up and "linking" track pieces to wheel objects. To achieve this in Blender create a new [Vertex Group](https://docs.blender.org/manual/en/latest/modeling/meshes/properties/vertex_groups/index.html) named **exactly** the same as the child object that the vertices are supposed to be transferred to and add them to the group. Make sure that a single vertex is assigned to **exactly one** vertex group, or you will get an export error.

## Animated UVs (BundledMesh)
To set up animated UVs go to `Edit Mode`, select specific parts (vertices/faces) of your mesh that should use UV animation and assign them to proper sets using `Mesh -> BF2` menu, choosing Left/Right Tracks/Wheels Translation/Rotation. You can also select vertices/faces currently assigned to those sets using `Select -> BF2` menu. Vertices assinged to "wheel rotation" set will additionaly require setting up the center point of UV rotation for each wheel individually. Select all vertices, and position the 2D cursor to the wheel center in the UV Editing view, then select `Mesh -> BF2 -> Set Animated UV Rotation Center`. Repeat the process for all wheels.

## Rigging (SkinnedMesh)
In order to rig your model, you must import the BF2 skeleton into your scene. When rigging soldiers you will need two skeletons `1p_setup.ske` for 1P (Geom 0) and `3p_setup.ske` 3P (Geom 1). The first step is to switch to `Pose Mode` and pose the armature(s) to best match your mesh(es). When you are done, make sure you apply this pose as the rest pose [Pose -> Apply -> Apply Pose As Rest Pose](https://docs.blender.org/manual/en/latest/animation/armatures/posing/editing/apply.html), then switch to `Object Mode` and for each Lod object go to `Modifiers` tab and [Add Modifier -> Deform -> Armature](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/armature.html), in modifier settings select 'Object' to point to the name of the imported skeleton. Now the hard part, you have to set vertex weights for each Lod, meaning how much each bone affects each vertex of the mesh. You could use automatic weights (which should be a good starting point) as follows: In `Object Mode` select the Armature, go to `Pose Mode`, click `Select -> All`, go back to `Object Mode`, select the mesh while holding the `Shift` key, go to `Wieght Paint` mode use [Weights -> Assign Automatic From Bones](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#assign-automatic-from-bone). Bare in mind that to export properly, each vertex must have at most two weights (be assigned to a maximum of two vertex groups), and all those weights have to be normalized (add-up to 1). You can limit the number of vertex weights in `Weight Paint` mode using [Weights -> Limit Total](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#limit-total) option (make sure it is set to 2). You can normalize weights using [Weights -> Normalize All](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/editing.html#normalize-all) option. Also, make sure that `Auto Normalize` is enabled in [Weight Paint Tools Settings](https://docs.blender.org/manual/en/latest/sculpt_paint/weight_paint/tool_settings/options.html) when rigging in `Weight Paint` mode.

# Tutorials
- [StaticMesh - hierarchy, materials and export (by Ason)](https://www.youtube.com/watch?v=H97o0d3zkoY)
- [BundledMesh - simple weapon export (by Krayt)](https://www.youtube.com/watch?v=crQRXm-4lxQ)
2 changes: 1 addition & 1 deletion io_scene_bf2/blender_manifest.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema_version = "1.0.0"
id = "io_scene_bf2"
version = "0.8.0"
version = "0.8.1"
name = "Battlefield 2"
tagline = "Import and export asset files for DICE's Refractor 2 engine"
maintainer = "Marek Zajac <[email protected]>"
Expand Down
21 changes: 19 additions & 2 deletions io_scene_bf2/operators/import_export/ops_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import traceback
from bpy.props import StringProperty, BoolProperty, CollectionProperty # type: ignore
from bpy_extras.io_utils import ExportHelper, ImportHelper # type: ignore
from bpy_extras.io_utils import ExportHelper, ImportHelper, poll_file_object_drop # type: ignore

from ...core.animation import import_animation, export_animation, get_bones_for_export
from ...core.skeleton import find_active_skeleton
Expand All @@ -13,7 +13,9 @@ class IMPORT_OT_bf2_animation(bpy.types.Operator, ImportHelper):
bl_idname= "bf2_animation.import"
bl_description = 'Battlefield 2 animation file'
bl_label = "Import animation"
filter_glob = StringProperty(default="*.baf", options={'HIDDEN'})

filename_ext = ".baf"
filter_glob: StringProperty(default="*.baf", options={'HIDDEN'}) # type: ignore

setup_ctrls: BoolProperty(
name="Setup Controllers",
Expand Down Expand Up @@ -101,6 +103,19 @@ def execute(self, context):
self.report({"INFO"}, 'Export complete')
return {'FINISHED'}


class IMPORT_EXPORT_FH_baf(bpy.types.FileHandler):
bl_idname = "IMPORT_EXPORT_FH_baf"
bl_label = "BF2 Animation"
bl_import_operator = IMPORT_OT_bf2_animation.bl_idname
bl_export_operator = EXPORT_OT_bf2_animation.bl_idname
bl_file_extensions = ".baf"

@classmethod
def poll_drop(cls, context):
return poll_file_object_drop(context)


FILE_DESC = "Animation (.baf)"

def draw_import(layout):
Expand All @@ -114,8 +129,10 @@ def register():
bpy.utils.register_class(BoneExportCollection)
bpy.utils.register_class(IMPORT_OT_bf2_animation)
bpy.utils.register_class(EXPORT_OT_bf2_animation)
bpy.utils.register_class(IMPORT_EXPORT_FH_baf)

def unregister():
bpy.utils.unregister_class(IMPORT_EXPORT_FH_baf)
bpy.utils.unregister_class(IMPORT_OT_bf2_animation)
bpy.utils.unregister_class(BoneExportCollection)
bpy.utils.unregister_class(EXPORT_OT_bf2_animation)
Expand Down
21 changes: 18 additions & 3 deletions io_scene_bf2/operators/import_export/ops_collisionmesh.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import bpy # type: ignore
import traceback
from bpy.props import StringProperty # type: ignore
from bpy_extras.io_utils import ExportHelper, ImportHelper # type: ignore
from bpy_extras.io_utils import ExportHelper, ImportHelper, poll_file_object_drop # type: ignore

from ...core.collision_mesh import import_collisionmesh, export_collisionmesh

class IMPORT_OT_bf2_collisionmesh(bpy.types.Operator, ImportHelper):
bl_idname= "bf2_collisionmesh.import"
bl_description = 'Battlefield 2 collision mesh file'
bl_label = "Import Collision Mesh"
filter_glob = StringProperty(default="*.collisionmesh", options={'HIDDEN'})
filter_glob: StringProperty(default="*.collisionmesh", options={'HIDDEN'}) # type: ignore

def execute(self, context):
try:
Expand All @@ -23,7 +23,7 @@ class EXPORT_OT_bf2_collisionmesh(bpy.types.Operator, ExportHelper):
bl_label = "Export Collision Mesh"

filename_ext = ".collisionmesh"
filter_glob = StringProperty(default="*.collisionmesh", options={'HIDDEN'})
filter_glob: StringProperty(default="*.collisionmesh", options={'HIDDEN'}) # type: ignore

@classmethod
def poll(cls, context):
Expand All @@ -42,6 +42,19 @@ def invoke(self, context, _event):
self.filepath = context.view_layer.objects.active.name + self.filename_ext
return super().invoke(context, _event)


class IMPORT_EXPORT_FH_collisionmesh(bpy.types.FileHandler):
bl_idname = "IMPORT_EXPORT_FH_collisionmesh"
bl_label = "BF2 CollisionMesh"
bl_import_operator = IMPORT_OT_bf2_collisionmesh.bl_idname
bl_export_operator = EXPORT_OT_bf2_collisionmesh.bl_idname
bl_file_extensions = ".collisionmesh"

@classmethod
def poll_drop(cls, context):
return poll_file_object_drop(context)


FILE_DESC = "CollisionMesh (.collisionmesh)"

def draw_import(layout):
Expand All @@ -53,7 +66,9 @@ def draw_export(layout):
def register():
bpy.utils.register_class(IMPORT_OT_bf2_collisionmesh)
bpy.utils.register_class(EXPORT_OT_bf2_collisionmesh)
bpy.utils.register_class(IMPORT_EXPORT_FH_collisionmesh)

def unregister():
bpy.utils.unregister_class(IMPORT_EXPORT_FH_collisionmesh)
bpy.utils.unregister_class(EXPORT_OT_bf2_collisionmesh)
bpy.utils.unregister_class(IMPORT_OT_bf2_collisionmesh)
Loading

0 comments on commit 23f704e

Please sign in to comment.