Compare commits

...
5 Commits
Author SHA1 Message Date
EIREXE 47662fe4ba Fix absolute gltf asset paths too.
CI / build-and-test (pull_request) Successful in 11m29s
2026-09-14 15:03:24 +02:00
EIREXE 445f249153 Add support for split .gltf and .bin files
CI / build-and-test (pull_request) Successful in 11m32s
2026-09-14 14:48:10 +02:00
EIREXE 30477254e5 Add build and test CI (#1)
CI / build-and-test (push) Successful in 11m30s
Reviewed-on: #1
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
2026-09-14 12:02:24 +00:00
EIREXE 4861374aef Add test fighter for CI 2026-09-14 11:38:42 +02:00
EIREXE d3a688e768 Fix build and add some assets 2026-09-14 11:25:27 +02:00
10 changed files with 81764 additions and 96 deletions
+45
View File
@@ -0,0 +1,45 @@
name: CI
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Install Rust toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustup" toolchain install nightly --profile minimal
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
clang \
libasound2-dev \
libudev-dev \
libusb-1.0-0-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
libxkbcommon-x11-0 \
mesa-vulkan-drivers \
mold \
pkg-config
- name: Build
run: cargo build --locked -j 4
- name: Run unit tests
run: cargo test --locked --lib -j 4
+1 -1
View File
@@ -1,6 +1,6 @@
(
id: TestFighter,
model_path: "fighters/test/test-fighter-2.glb",
model_path: "fighters/test/test_fighter.gltf",
model_scale: "10.0",
baked_animation_path: "fighters/test/test.fighterbake",
icon: (
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
-84
View File
@@ -1,84 +0,0 @@
(
id: TestStage,
model_path: "stages/battlefield.glb#Scene0",
camera: (
left: -75.0,
right: 75.0,
bottom: -30.0,
top: 70.0,
origin: (0.0, 15.0),
vertical_fov_degrees: 30.0,
min_depth: 150.0,
max_depth: 400.0,
subject_scale: 1.0,
tracking_smoothness: 3.0,
vertical_tilt_degrees: 0.0,
horizontal_pan_coefficient: 0.0,
vertical_pan_coefficient: 0.0,
max_horizontal_pan_degrees: 17.5,
max_upward_pan_degrees: 5.0,
max_downward_pan_degrees: 7.0,
max_downward_expansion: 40.0,
),
polygons: [
(
poly_type: Platform,
lines: [
(
(x: "-59.5", y: "23.45"),
Floor
),
(
(x: "-28.0", y: "23.45"),
Floor
)
]
),
(
poly_type: Platform,
lines: [
(
(x: "28.0", y: "23.45"),
Floor
),
(
(x: "59.5", y: "23.45"),
Floor
)
]
),
(
poly_type: Closed,
lines: [
(
(x: "-56", y: "-200.0"),
Wall
),
(
(x: "-56.0", y: "-3.5"),
Floor,
),
(
(x: "-39.2", y: "0.0"),
Floor,
),
(
(x: "0.0", y: "0.0"),
Floor
),
(
(x: "39.2", y: "0.0"),
Floor
),
(
(x: "56.0", y: "-3.5"),
Wall
),
(
(x: "56", y: "-200.0"),
Wall
),
]
)
]
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

+65 -6
View File
@@ -3,6 +3,7 @@
use std::{
collections::{HashMap, HashSet},
io,
path::Path,
};
use bevy::{
@@ -517,6 +518,7 @@ struct FighterBakeDeclaration {
#[derive(Asset, TypePath)]
struct FighterBakeSource {
model_bytes: Vec<u8>,
buffers: Vec<Vec<u8>>,
animations: HashMap<AnimKind, String>,
}
@@ -550,8 +552,31 @@ impl AssetLoader for FighterBakeSourceLoader {
.read_asset_bytes(&manifest.model_path)
.await
.map_err(|error| io::Error::other(error.to_string()))?;
let gltf = gltf::Gltf::from_slice(&model_bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let mut buffers = Vec::with_capacity(gltf.buffers().len());
for buffer in gltf.buffers() {
let bytes = match buffer.source() {
gltf::buffer::Source::Uri(uri) => load_context
.read_asset_bytes(AssetPath::from_path_buf(
Path::new(&manifest.model_path)
.parent()
.unwrap_or_else(|| Path::new(""))
.join(uri),
))
.await
.map_err(|error| io::Error::other(error.to_string()))?,
gltf::buffer::Source::Bin => gltf.blob.clone().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "missing glTF binary blob")
})?,
};
buffers.push(bytes);
}
Ok(FighterBakeSource {
model_bytes,
buffers,
animations: manifest.animations,
})
}
@@ -600,7 +625,7 @@ impl AssetTransformer for FighterBakeTransformer {
source: TransformedAsset<Self::AssetInput>,
_: &'a Self::Settings,
) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
let baked = bake_gltf(&source.model_bytes, &source.animations)?;
let baked = bake_gltf(&source.model_bytes, &source.buffers, &source.animations)?;
Ok(source.replace_asset(baked))
}
}
@@ -656,10 +681,43 @@ impl Plugin for BakedAnimationPlugin {
fn bake_gltf(
bytes: &[u8],
buffer_bytes: &[Vec<u8>],
animations: &HashMap<AnimKind, String>,
) -> io::Result<BakedFighterAnimations> {
let (document, buffers, _) = gltf::import_slice(bytes)
let gltf = gltf::Gltf::from_slice(bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let buffers = gltf
.buffers()
.zip(buffer_bytes)
.map(|(buffer, bytes)| {
if bytes.len() < buffer.length() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"glTF buffer {} is too short: expected {}, got {}",
buffer.index(),
buffer.length(),
bytes.len()
),
));
}
Ok(gltf::buffer::Data(bytes.clone()))
})
.collect::<io::Result<Vec<_>>>()?;
if buffers.len() != gltf.buffers().len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"glTF buffer data is incomplete",
));
}
bake_gltf_document(gltf.document, buffers, animations)
}
fn bake_gltf_document(
document: gltf::Document,
buffers: Vec<gltf::buffer::Data>,
animations: &HashMap<AnimKind, String>,
) -> io::Result<BakedFighterAnimations> {
let nodes: Vec<_> = document.nodes().collect();
if nodes.is_empty() {
return Err(io::Error::new(
@@ -933,11 +991,12 @@ mod tests {
fn configured_fighter_clips_bake_to_fixed_point_frames() {
let manifest: FighterManifest =
ron::from_str(include_str!("../../assets/fighters/test/test.fighter.ron")).unwrap();
let baked = bake_gltf(
include_bytes!("../../assets/fighters/test/test-fighter-2.glb"),
&manifest.animations,
)
let (document, buffers, _) = gltf::import(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/fighters/test/test_fighter.gltf"
))
.unwrap();
let baked = bake_gltf_document(document, buffers, &manifest.animations).unwrap();
assert_eq!(baked.frame_rate, BAKED_ANIMATION_FPS);
assert_eq!(baked.frame_count(AnimKind::Wait), Some(121));
+1 -5
View File
@@ -1,7 +1,3 @@
//! Standalone binary for game-test: `cargo run` plays the game
//! without the editor. The editor's Play button runs the same
//! [`game_test::GamePlugin`] through its own game runner instead.
use bevy::asset::AssetMode::Processed;
use bevy::camera_controller::free_camera::FreeCameraPlugin;
use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor};
@@ -30,7 +26,7 @@ fn main() -> AppExit {
App::new()
.add_plugins(default_plugins)
.add_plugins(avian3d::prelude::PhysicsPlugins::default())
.add_plugins(game_test::GamePlugin)
.add_plugins(shinespark::GamePlugin)
.add_plugins(FreeCameraPlugin)
.add_plugins(EguiPlugin::default())
.add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s