Merge branch 'aweinstock/modular-weapon-rigging' into 'master'

Support modular weapon voxel meshes being made by assembling their components.

See merge request veloren/veloren!1806
This commit is contained in:
Imbris
2021-02-23 00:45:26 +00:00
8 changed files with 139 additions and 31 deletions

View File

@ -1845,4 +1845,12 @@
"voxel.sprite.gem.diamondgem", "voxel.sprite.gem.diamondgem",
(0.0, 0.0, 0.0), (-55.0, 30.0, 20.0), 0.6, (0.0, 0.0, 0.0), (-55.0, 30.0, 20.0), 0.6,
), ),
ModularComponent("common.items.crafting_ing.modular.damage.sword.tier5"): VoxTrans(
"voxel.weapon_components.sword.cultist.cultist_purp_2h-0_blade",
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.5,
),
ModularComponent("common.items.crafting_ing.modular.held.sword.tier5"): VoxTrans(
"voxel.weapon_components.sword.cultist.cultist_purp_2h-0_handle",
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.5,
),
}) })

View File

@ -0,0 +1,8 @@
({
"common.items.crafting_ing.modular.damage.sword.tier5": Damage((
"weapon_components.sword.cultist.cultist_purp_2h-0_blade", (1, 2, 11),
)),
"common.items.crafting_ing.modular.held.sword.tier5": Held((
"weapon_components.sword.cultist.cultist_purp_2h-0_handle", (-2.0, -4.5, -7.5), (0, 0, 0),
)),
})

Binary file not shown.

Binary file not shown.

View File

@ -249,6 +249,14 @@ impl ItemDef {
) )
} }
pub fn is_component(&self, kind: ModularComponentKind) -> bool {
if let ItemKind::ModularComponent(ModularComponent { modkind, .. }) = self.kind {
kind == modkind
} else {
false
}
}
#[cfg(test)] #[cfg(test)]
pub fn new_test( pub fn new_test(
item_definition_id: String, item_definition_id: String,

View File

@ -22,6 +22,16 @@ impl From<&DotVoxData> for Segment {
} }
impl Segment { impl Segment {
/// Take a list of voxel data, offsets, and x-mirror flags, and assembled
/// them into a combined segment
pub fn from_voxes(data: &[(&DotVoxData, Vec3<i32>, bool)]) -> (Self, Vec3<i32>) {
let mut union = DynaUnionizer::new();
for (datum, offset, xmirror) in data.iter() {
union = union.add(Segment::from_vox(datum, *xmirror), *offset);
}
union.unify()
}
pub fn from_vox(dot_vox_data: &DotVoxData, flipped: bool) -> Self { pub fn from_vox(dot_vox_data: &DotVoxData, flipped: bool) -> Self {
if let Some(model) = dot_vox_data.models.get(0) { if let Some(model) = dot_vox_data.models.get(0) {
let palette = dot_vox_data let palette = dot_vox_data

View File

@ -16,7 +16,7 @@ use common::{
}, },
item::{ item::{
armor::{Armor, ArmorKind}, armor::{Armor, ArmorKind},
ItemKind, Item, ItemKind,
}, },
CharacterState, CharacterState,
}, },
@ -69,12 +69,18 @@ pub struct FigureKey<Body> {
pub(super) extra: Option<Arc<CharacterCacheKey>>, pub(super) extra: Option<Arc<CharacterCacheKey>>,
} }
#[derive(Eq, Hash, PartialEq)]
pub(super) struct ToolKey {
pub name: String,
pub components: Vec<String>,
}
/// Character data that should be visible when tools are visible (i.e. in third /// Character data that should be visible when tools are visible (i.e. in third
/// person or when the character is in a tool-using state). /// person or when the character is in a tool-using state).
#[derive(Eq, Hash, PartialEq)] #[derive(Eq, Hash, PartialEq)]
pub(super) struct CharacterToolKey { pub(super) struct CharacterToolKey {
pub active: Option<String>, pub active: Option<ToolKey>,
pub second: Option<String>, pub second: Option<ToolKey>,
} }
/// Character data that exists in third person only. /// Character data that exists in third person only.
@ -193,13 +199,21 @@ impl CharacterCacheKey {
}) })
}, },
tool: if are_tools_visible { tool: if are_tools_visible {
let tool_key_from_item = |item: &Item| ToolKey {
name: item.item_definition_id().to_owned(),
components: item
.components()
.iter()
.map(|comp| comp.item_definition_id().to_owned())
.collect(),
};
Some(CharacterToolKey { Some(CharacterToolKey {
active: inventory active: inventory
.equipped(EquipSlot::Mainhand) .equipped(EquipSlot::Mainhand)
.map(|i| i.item_definition_id().to_owned()), .map(tool_key_from_item),
second: inventory second: inventory
.equipped(EquipSlot::Offhand) .equipped(EquipSlot::Offhand)
.map(|i| i.item_definition_id().to_owned()), .map(tool_key_from_item),
}) })
} else { } else {
None None

View File

@ -1,4 +1,4 @@
use super::cache::FigureKey; use super::cache::{FigureKey, ToolKey};
use common::{ use common::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron}, assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
comp::{ comp::{
@ -10,6 +10,7 @@ use common::{
fish_small::{self, BodyType as FSBodyType, Species as FSSpecies}, fish_small::{self, BodyType as FSBodyType, Species as FSSpecies},
golem::{self, BodyType as GBodyType, Species as GSpecies}, golem::{self, BodyType as GBodyType, Species as GSpecies},
humanoid::{self, Body, BodyType, EyeColor, Skin, Species}, humanoid::{self, Body, BodyType, EyeColor, Skin, Species},
item::{ItemDef, ModularComponentKind},
object, object,
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies}, quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies}, quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
@ -20,6 +21,7 @@ use common::{
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc;
use tracing::{error, warn}; use tracing::{error, warn};
use vek::*; use vek::*;
@ -42,8 +44,8 @@ fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
fn graceful_load_segment(mesh_name: &str) -> Segment { fn graceful_load_segment(mesh_name: &str) -> Segment {
Segment::from(&graceful_load_vox(mesh_name).read().0) Segment::from(&graceful_load_vox(mesh_name).read().0)
} }
fn graceful_load_segment_flipped(mesh_name: &str) -> Segment { fn graceful_load_segment_flipped(mesh_name: &str, flipped: bool) -> Segment {
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, true) Segment::from_vox(&graceful_load_vox(mesh_name).read().0, flipped)
} }
fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment { fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment {
MatSegment::from(&graceful_load_vox(mesh_name).read().0) MatSegment::from(&graceful_load_vox(mesh_name).read().0)
@ -141,6 +143,14 @@ struct ArmorVoxSpec {
color: Option<[u8; 3]>, color: Option<[u8; 3]>,
} }
#[derive(Deserialize)]
enum ModularComponentSpec {
/// item id, offset from origin to mount point
Damage((String, [i32; 3])),
/// item id, offset from origin to hand, offset from origin to mount point
Held((String, [f32; 3], [i32; 3])),
}
// For use by armor with a left and right component // For use by armor with a left and right component
#[derive(Deserialize)] #[derive(Deserialize)]
struct SidedArmorVoxSpec { struct SidedArmorVoxSpec {
@ -323,6 +333,8 @@ struct HumArmorFootSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
#[derive(Deserialize)] #[derive(Deserialize)]
struct HumMainWeaponSpec(HashMap<String, ArmorVoxSpec>); struct HumMainWeaponSpec(HashMap<String, ArmorVoxSpec>);
#[derive(Deserialize)] #[derive(Deserialize)]
struct HumModularComponentSpec(HashMap<String, ModularComponentSpec>);
#[derive(Deserialize)]
struct HumArmorLanternSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>); struct HumArmorLanternSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
#[derive(Deserialize)] #[derive(Deserialize)]
struct HumArmorGliderSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>); struct HumArmorGliderSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
@ -344,6 +356,7 @@ make_vox_spec!(
armor_pants: HumArmorPantsSpec = "voxygen.voxel.humanoid_armor_pants_manifest", armor_pants: HumArmorPantsSpec = "voxygen.voxel.humanoid_armor_pants_manifest",
armor_foot: HumArmorFootSpec = "voxygen.voxel.humanoid_armor_foot_manifest", armor_foot: HumArmorFootSpec = "voxygen.voxel.humanoid_armor_foot_manifest",
main_weapon: HumMainWeaponSpec = "voxygen.voxel.humanoid_main_weapon_manifest", main_weapon: HumMainWeaponSpec = "voxygen.voxel.humanoid_main_weapon_manifest",
modular_components: HumModularComponentSpec = "voxygen.voxel.humanoid_modular_component_manifest",
armor_lantern: HumArmorLanternSpec = "voxygen.voxel.humanoid_lantern_manifest", armor_lantern: HumArmorLanternSpec = "voxygen.voxel.humanoid_lantern_manifest",
armor_glider: HumArmorGliderSpec = "voxygen.voxel.humanoid_glider_manifest", armor_glider: HumArmorGliderSpec = "voxygen.voxel.humanoid_glider_manifest",
// TODO: Add these. // TODO: Add these.
@ -447,12 +460,14 @@ make_vox_spec!(
)), )),
tool.and_then(|tool| tool.active.as_ref()).map(|tool| { tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
spec.main_weapon.read().0.mesh_main_weapon( spec.main_weapon.read().0.mesh_main_weapon(
&spec.modular_components.read().0,
tool, tool,
false, false,
) )
}), }),
tool.and_then(|tool| tool.second.as_ref()).map(|tool| { tool.and_then(|tool| tool.second.as_ref()).map(|tool| {
spec.main_weapon.read().0.mesh_main_weapon( spec.main_weapon.read().0.mesh_main_weapon(
&spec.modular_components.read().0,
tool, tool,
true, true,
) )
@ -822,32 +837,71 @@ impl HumArmorFootSpec {
} }
impl HumMainWeaponSpec { impl HumMainWeaponSpec {
fn mesh_main_weapon(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes { fn mesh_main_weapon(
let spec = match self.0.get(item_definition_id) { &self,
Some(spec) => spec, modular_components: &HumModularComponentSpec,
None => { tool: &ToolKey,
error!(?item_definition_id, "No tool/weapon specification exists"); flipped: bool,
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0)); ) -> BoneMeshes {
}, // TODO: resolve ItemDef info into the ToolKey earlier
let itemdef = Arc::<ItemDef>::load_expect_cloned(&tool.name);
let not_found = |name: &str| {
error!(?name, "No tool/weapon specification exists");
load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0))
}; };
let (tool_kind_segment, mut offset) = if itemdef.is_modular() {
let tool_kind_segment = if flipped { let (mut damage, mut held) = (None, None);
graceful_load_segment_flipped(&spec.vox_spec.0) for comp in tool.components.iter() {
} else { let compdef = Arc::<ItemDef>::load_expect_cloned(comp);
graceful_load_segment(&spec.vox_spec.0) if compdef.is_component(ModularComponentKind::Held) {
held = Some(comp.clone());
}
if compdef.is_component(ModularComponentKind::Damage) {
damage = Some(comp.clone());
}
// TODO: when enhancements are added, line them up to make a
// pretty row of gems on the hilt or something
}
if let (Some(damage), Some(held)) = (damage.as_ref(), held.as_ref()) {
use ModularComponentSpec::*;
let damagespec = match modular_components.0.get(&*damage) {
Some(Damage(spec)) => spec,
_ => return not_found(&damage),
}; };
let heldspec = match modular_components.0.get(&*held) {
Some(Held(spec)) => spec,
_ => return not_found(&held),
};
let damage_asset = graceful_load_vox(&damagespec.0).read();
let held_asset = graceful_load_vox(&heldspec.0).read();
let offset = Vec3::new( let (segment, segment_origin) = Segment::from_voxes(&[
if flipped { (&damage_asset.0, Vec3::from(damagespec.1), flipped),
//log::warn!("tool kind segment {:?}", ); (&held_asset.0, Vec3::from(heldspec.2), flipped),
//tool_kind_segment.; ]);
0.0 - spec.vox_spec.1[0] - (tool_kind_segment.sz.x as f32) (segment, segment_origin.map(|x: i32| x as f32) + heldspec.1)
} else { } else {
spec.vox_spec.1[0] error!(
}, "A modular weapon is missing some components (damage: {:?}, held: {:?})",
spec.vox_spec.1[1], damage, held
spec.vox_spec.1[2],
); );
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
}
} else {
let spec = match self.0.get(&tool.name) {
Some(spec) => spec,
None => return not_found(&tool.name),
};
(
graceful_load_segment_flipped(&spec.vox_spec.0, flipped),
Vec3::from(spec.vox_spec.1),
)
};
if flipped {
offset.x = 0.0 - offset.x - (tool_kind_segment.sz.x as f32);
}
(tool_kind_segment, offset) (tool_kind_segment, offset)
} }