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
commit f33d3873b4
8 changed files with 139 additions and 31 deletions

View File

@ -1845,4 +1845,12 @@
"voxel.sprite.gem.diamondgem",
(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)]
pub fn new_test(
item_definition_id: String,

View File

@ -22,6 +22,16 @@ impl From<&DotVoxData> for 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 {
if let Some(model) = dot_vox_data.models.get(0) {
let palette = dot_vox_data

View File

@ -16,7 +16,7 @@ use common::{
},
item::{
armor::{Armor, ArmorKind},
ItemKind,
Item, ItemKind,
},
CharacterState,
},
@ -69,12 +69,18 @@ pub struct FigureKey<Body> {
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
/// person or when the character is in a tool-using state).
#[derive(Eq, Hash, PartialEq)]
pub(super) struct CharacterToolKey {
pub active: Option<String>,
pub second: Option<String>,
pub active: Option<ToolKey>,
pub second: Option<ToolKey>,
}
/// Character data that exists in third person only.
@ -193,13 +199,21 @@ impl CharacterCacheKey {
})
},
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 {
active: inventory
.equipped(EquipSlot::Mainhand)
.map(|i| i.item_definition_id().to_owned()),
.map(tool_key_from_item),
second: inventory
.equipped(EquipSlot::Offhand)
.map(|i| i.item_definition_id().to_owned()),
.map(tool_key_from_item),
})
} else {
None

View File

@ -1,4 +1,4 @@
use super::cache::FigureKey;
use super::cache::{FigureKey, ToolKey};
use common::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
comp::{
@ -10,6 +10,7 @@ use common::{
fish_small::{self, BodyType as FSBodyType, Species as FSSpecies},
golem::{self, BodyType as GBodyType, Species as GSpecies},
humanoid::{self, Body, BodyType, EyeColor, Skin, Species},
item::{ItemDef, ModularComponentKind},
object,
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
@ -20,6 +21,7 @@ use common::{
};
use hashbrown::HashMap;
use serde::Deserialize;
use std::sync::Arc;
use tracing::{error, warn};
use vek::*;
@ -42,8 +44,8 @@ fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
fn graceful_load_segment(mesh_name: &str) -> Segment {
Segment::from(&graceful_load_vox(mesh_name).read().0)
}
fn graceful_load_segment_flipped(mesh_name: &str) -> Segment {
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, true)
fn graceful_load_segment_flipped(mesh_name: &str, flipped: bool) -> Segment {
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, flipped)
}
fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment {
MatSegment::from(&graceful_load_vox(mesh_name).read().0)
@ -141,6 +143,14 @@ struct ArmorVoxSpec {
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
#[derive(Deserialize)]
struct SidedArmorVoxSpec {
@ -323,6 +333,8 @@ struct HumArmorFootSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
#[derive(Deserialize)]
struct HumMainWeaponSpec(HashMap<String, ArmorVoxSpec>);
#[derive(Deserialize)]
struct HumModularComponentSpec(HashMap<String, ModularComponentSpec>);
#[derive(Deserialize)]
struct HumArmorLanternSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
#[derive(Deserialize)]
struct HumArmorGliderSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
@ -344,6 +356,7 @@ make_vox_spec!(
armor_pants: HumArmorPantsSpec = "voxygen.voxel.humanoid_armor_pants_manifest",
armor_foot: HumArmorFootSpec = "voxygen.voxel.humanoid_armor_foot_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_glider: HumArmorGliderSpec = "voxygen.voxel.humanoid_glider_manifest",
// TODO: Add these.
@ -447,12 +460,14 @@ make_vox_spec!(
)),
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
spec.main_weapon.read().0.mesh_main_weapon(
&spec.modular_components.read().0,
tool,
false,
)
}),
tool.and_then(|tool| tool.second.as_ref()).map(|tool| {
spec.main_weapon.read().0.mesh_main_weapon(
&spec.modular_components.read().0,
tool,
true,
)
@ -822,32 +837,71 @@ impl HumArmorFootSpec {
}
impl HumMainWeaponSpec {
fn mesh_main_weapon(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
let spec = match self.0.get(item_definition_id) {
Some(spec) => spec,
None => {
error!(?item_definition_id, "No tool/weapon specification exists");
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
},
fn mesh_main_weapon(
&self,
modular_components: &HumModularComponentSpec,
tool: &ToolKey,
flipped: bool,
) -> 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 (mut damage, mut held) = (None, None);
for comp in tool.components.iter() {
let compdef = Arc::<ItemDef>::load_expect_cloned(comp);
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 tool_kind_segment = if flipped {
graceful_load_segment_flipped(&spec.vox_spec.0)
} else {
graceful_load_segment(&spec.vox_spec.0)
};
let offset = Vec3::new(
if flipped {
//log::warn!("tool kind segment {:?}", );
//tool_kind_segment.;
0.0 - spec.vox_spec.1[0] - (tool_kind_segment.sz.x as f32)
let (segment, segment_origin) = Segment::from_voxes(&[
(&damage_asset.0, Vec3::from(damagespec.1), flipped),
(&held_asset.0, Vec3::from(heldspec.2), flipped),
]);
(segment, segment_origin.map(|x: i32| x as f32) + heldspec.1)
} else {
spec.vox_spec.1[0]
},
spec.vox_spec.1[1],
spec.vox_spec.1[2],
);
error!(
"A modular weapon is missing some components (damage: {:?}, held: {:?})",
damage, held
);
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)
}