Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
"# assets"
# NEOTOKYO Rebuild assets

Runtime content and editable sources live under `neo/`. neo's CMake setup
copies this tree into its local game directory.

## Weapon animation authoring

Start with the [Blender animation workflow](neo/modelsrc/weapons/README.md).
The Jinrai and NSF Tachi projects include editable rigs, animations, preview
textures, SMD exports, and QC compile definitions. Edit and save in Blender,
export with Blender Source Tools, then compile the exported files for Source.
Binary file added neo/models/weapons/v_jinrai_tachi.dx80.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_jinrai_tachi.dx90.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_jinrai_tachi.mdl
Binary file not shown.
Binary file added neo/models/weapons/v_jinrai_tachi.sw.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_jinrai_tachi.vvd
Binary file not shown.
Binary file added neo/models/weapons/v_nsf_tachi.dx80.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_nsf_tachi.dx90.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_nsf_tachi.mdl
Binary file not shown.
Binary file added neo/models/weapons/v_nsf_tachi.sw.vtx
Binary file not shown.
Binary file added neo/models/weapons/v_nsf_tachi.vvd
Binary file not shown.
134 changes: 134 additions & 0 deletions neo/modelsrc/weapons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Editing weapon animations in Blender

The Tachi projects are editable sources, not just animation exports:

- [Jinrai Tachi](jinrai_tachi/README.md): `jinrai_tachi/v_jinrai_tachi.blend`
- [NSF Tachi](nsf_tachi/README.md): `nsf_tachi/v_nsf_tachi.blend`

Each project contains the skinned hands and pistol, all 16 animation clips,
and a preview camera. `reloadempty` is selected when the project opens.
Save animation changes in the `.blend` file first, then export SMDs and
compile the QC. Do not edit generated SMDs as your animation source.

## Requirements

- Blender 5.2 or newer; these projects were saved with 5.2.3 LTS.
- [Blender Source Tools](https://developer.valvesoftware.com/wiki/Blender_Source_Tools)
3.4.3, enabled in Edit > Preferences > Add-ons.
- Source SDK Base 2013 Multiplayer's `bin/studiomdl.exe` for compilation.
- Python 3 for the optional compile helper; no extra Python packages needed.
- NEOTOKYO installed for the in-game materials. Blender preview images are
included separately, so opening the project does not require finding Steam.

## Open and edit

1. Open either `.blend` above. Keep the surrounding folders together.
2. Play or scrub frames **0–63**, at **30 fps**, to inspect the empty reload.
Timeline markers identify the slide release at frames **54–62**.
3. Select the `*_tachi_rig` armature and enter Pose Mode. Select the `slide`
bone to adjust its movement, or select hand/finger bones to change the pose.
4. The Action Editor's slot selector contains the clips. Choose `reload` to
compare the partial reload or `idle01empty` to inspect the locked-back pose.
The clips are slots of one Action, not separate Action datablocks.
5. Save the `.blend` before exporting. Preserve bone names, hierarchy, and
material names. A preview camera is provided for inspection, not a promise
of matching the game's viewmodel FOV.

Blender preview PNGs are in `tachi/textures/`, referenced relative to each
project. They are decoded copies of the original NEOTOKYO base-color VTFs;
they are previews, not replacements for the game's VMT/VTF materials.
The preview does not reproduce the Source shader's normal/specular effects.

## Export SMDs

In Scene Properties > Source Engine Export, use **Export Scene**. The saved
configuration exports every animation slot into `anims/`.
The export root is `//` (beside the `.blend`), format SMD, with 54 bones and
no added implicit root. The skinned mesh is included for preview, but its
export is disabled: retain the original `reference.smd` unchanged for these
animation-only projects. Blender Source Tools 3.4.3 with Blender 5.2 changes
split normals on a reference-mesh round trip; exporting that mesh would
change shading unrelated to the animation.

For a repeatable export of both projects, from this directory:

```sh
blender --background --python-exit-code 1 --python export_tachi.py
```

Use the full path to `blender.exe` if Blender is not on PATH. Export does not
save edits for you. The helper opens the saved projects, checks preview-image
paths, and exports them with Blender Source Tools. It does not import SMDs or
recreate the Blender projects.

## Compile and preview in the game

Choose the **neo game directory containing `gameinfo.txt`**, not the checkout
root or the SDK's `bin` directory. Close the game/model viewer before
replacing its models. Back up any local Tachi model overrides you want to keep.

```sh
python compile_tachi.py --compiler "C:/Program Files (x86)/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/bin/studiomdl.exe" --game "C:/path/to/neo/game/neo"
```

The example compiler location is the default Steam library; change it to your
installation. The helper builds both QCs in a temporary staging game, then
copies the resulting model files into the chosen `models/weapons/` directory.
This avoids SDK tools' inability to resolve neo's `|appid_244630|` search path;
it does **not** edit your `gameinfo.txt` or Blender preferences. Compilation
requires no material files. Runtime materials remain the installed game's.

The QC remains the authority for activities, playback FPS, attachments,
transition nodes, and frame-0 muzzle-flash events. Exporting an animation does
not rewrite the QC. If adding a clip, add a matching `$sequence` there.

To retain compiled deliverables in neoAssets, copy all generated
`v_jinrai_tachi.*` and `v_nsf_tachi.*` files from the destination's
`models/weapons/` to this repository's `neo/models/weapons/`.

With companion game code that selects `ACT_VM_RELOAD_EMPTY`, fire the Tachi
dry and reload: the slide stays back until the magazine is seated. A partial
reload should still play `reload`. Check both teams. No game-code build is
needed merely to export or compile an animation.

## CMake and installed copies

neo's CMake configuration copies **everything below neoAssets `neo/`** into
`neo/game/neo/`, including `modelsrc/`, Blender projects, exports, and preview
textures. Separate resource packaging requires `NEO_INSTALL_RESOURCES=ON`
(default OFF); its resources directory also includes that subtree. Asset
copying happens during CMake configuration, not on each animation export:
copy updated sources/models explicitly or configure again to synchronize.
Open the installed
project at `neo/game/neo/modelsrc/weapons/<team>_tachi/v_<team>_tachi.blend`;
its image/export/QC paths still resolve relative to the project.

CMake downloads **upstream master** by default, not this PR. When working from
a branch, set `NEO_EXTRA_ASSETS_ALT_PATH` to the **neoAssets checkout root**
in your existing CMake configuration/preset. Otherwise a later configure can
overwrite local asset changes with the downloaded version. The setting is
not the checkout's `neo/` subfolder.

For an asset-only local preview, copying this branch's `neo/modelsrc/` and
Tachi `neo/models/weapons/v_*_tachi.*` into the game's matching directories
has the same layout; do not delete unrelated game files.

Treat installed projects as review copies. To contribute, edit in the
neoAssets checkout, export, compile, and include the `.blend`, SMD exports,
QC changes if any, and rebuilt models together. Copy any edits made in an
installed project back to the checkout **before** reconfiguring or reinstalling.

## Source history and review checks

These projects were migrated from the original NEOTOKYO Tachi viewmodels,
with one added `reloadempty` clip. The mesh and other animations retain their
original names; no separate decompiler is needed for normal editing.
`segments.csv` records the original empty-reload composition for provenance;
it is not the current authoring/build pipeline.

After an animation change, check clip length/FPS, hand deformation, slide
motion, and both team models. Ordinary reloads, draws, idle poses, firing,
attachments, and muzzle flashes should remain unchanged unless deliberately
edited. Small floating-point and compiler-compression differences can occur
on a Blender/SMD/model round trip; do not mistake a successful compile for a
complete visual review.
54 changes: 54 additions & 0 deletions neo/modelsrc/weapons/compile_tachi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Compile exported Tachi SMDs using Valve's Source SDK 2013 studiomdl.

Run with Python 3: compile_tachi.py --compiler PATH --game PATH
The destination must be a game folder containing gameinfo.txt. Compilation
uses a temporary game configuration because SDK tools cannot resolve the
client's |appid_244630| material mount. Only the resulting Tachi model files
are copied to the chosen destination; its gameinfo.txt is never changed.
"""
import argparse
from pathlib import Path
import shutil
import subprocess
import tempfile


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--compiler', type=Path, required=True,
help='Source SDK Base 2013 Multiplayer/bin/studiomdl.exe')
parser.add_argument('--game', type=Path, required=True,
help='Destination neo game directory (contains gameinfo.txt)')
args = parser.parse_args()
compiler = args.compiler.resolve()
game = args.game.resolve()
if not compiler.is_file():
parser.error('Compiler does not exist: ' + str(compiler))
if not (game / 'gameinfo.txt').is_file():
parser.error('Game directory must contain gameinfo.txt: ' + str(game))
root = Path(__file__).resolve().parent
with tempfile.TemporaryDirectory(prefix='neo-tachi-') as temporary:
staging = Path(temporary)
(staging / 'gameinfo.txt').write_text(
'"GameInfo" { "game" "Tachi model compile" "FileSystem" { '
'"SteamAppId" "243750" "SearchPaths" { '
'"Game" "|gameinfo_path|." } } }\n', encoding='ascii')
outputs = []
for team in ('jinrai', 'nsf'):
name = 'v_' + team + '_tachi'
qc = root / (team + '_tachi') / (name + '.qc')
subprocess.run([str(compiler), '-nop4', '-game', str(staging), str(qc)], check=True)
for extension in ('.mdl', '.vvd', '.dx90.vtx'):
path = staging / 'models/weapons' / (name + extension)
if not path.is_file():
raise RuntimeError('Compiler did not create ' + str(path))
outputs.extend((staging / 'models/weapons').glob(name + '.*'))
destination = game / 'models/weapons'
destination.mkdir(parents=True, exist_ok=True)
for path in outputs:
shutil.copy2(path, destination / path.name)
print('Installed', destination / path.name)


if __name__ == '__main__':
main()
43 changes: 43 additions & 0 deletions neo/modelsrc/weapons/export_tachi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Export the saved Tachi Blender sources with Blender Source Tools.

Run: blender --background --python-exit-code 1 --python export_tachi.py
Runtime: approximately 5 seconds for both models on an i7-12700H.
Requires Blender 5.2 or newer and Blender Source Tools 3.4.3.
This reads .blend sources; it never reconstructs them from SMD exports.
"""
from pathlib import Path

import bpy


def export_project(project):
bpy.ops.wm.open_mainfile(filepath=str(project))
bpy.ops.preferences.addon_enable(module='io_scene_valvesource')
missing = [image.filepath for image in bpy.data.images
if image.source == 'FILE' and not image.packed_file
and not Path(bpy.path.abspath(image.filepath)).is_file()]
if missing:
raise RuntimeError('Missing preview images: ' + ', '.join(missing))
folder = project.parent
reference = folder / 'reference.smd'
reference_bytes = reference.read_bytes()
armature = next(obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE')
expected = [folder / 'anims' / (slot.name_display + '.smd')
for slot in armature.animation_data.action.slots]
previous = {path: path.stat().st_mtime_ns if path.exists() else None
for path in expected}
result = bpy.ops.export_scene.smd(export_scene=True)
if result != {'FINISHED'}:
raise RuntimeError('Source Tools export failed: ' + str(result))
if any(not path.is_file() or path.stat().st_mtime_ns == previous[path]
for path in expected):
raise RuntimeError('Source Tools did not freshly export every expected SMD')
if reference.read_bytes() != reference_bytes:
raise RuntimeError('The preview mesh must not overwrite reference.smd')
print('EXPORTED', project.name, len(expected), 'animation SMD files; reference retained')


if __name__ == '__main__':
root = Path(__file__).resolve().parent
for team in ('jinrai', 'nsf'):
export_project(root / (team + '_tachi') / ('v_' + team + '_tachi.blend'))
29 changes: 29 additions & 0 deletions neo/modelsrc/weapons/jinrai_tachi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Jinrai Tachi animation source

Open **`v_jinrai_tachi.blend`** in Blender 5.2 or newer with Blender Source
Tools enabled. The skinned hands and pistol are included, with `reloadempty`
selected in the Action Editor. Preview textures resolve from
`../tachi/textures/`; keep that folder alongside the team projects.

See [the animation workflow](../README.md) for editing, exporting, compiling,
CMake asset installation, and contributing changes.

- Authoritative editable source: `v_jinrai_tachi.blend`.
- Preserved original reference mesh: `reference.smd` (not re-exported).
- Generated clips: `anims/*.smd` (all 16 clips).
- Compile definition: `v_jinrai_tachi.qc`.
- Runtime output: `models/weapons/v_jinrai_tachi.*` in the chosen game folder.

The empty reload spans frames **0–63 at 30 fps** (2.1 seconds between the
first and last frame). The `slide` bone stays in its `idle01empty` pose, then
returns forward over frames **54–62**, after the new magazine is seated.
Compare the `reload` slot for a partial reload and `idle01empty` for the
locked-back pose. Every clip uses the original 54-bone hierarchy.

Use Scene Properties > Source Engine Export > **Export Scene** to rebuild
all SMDs beside this project. Save the Blender file first. The QC preserves
activities, attachments, transition nodes, and firing muzzle-flash events;
review it when adding or renaming animation slots.

`segments.csv` records the initial empty-reload composition. It is historical
provenance, not an input required to edit or rebuild this Blender project.
Loading