mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
@ -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,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
@ -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),
|
||||||
|
)),
|
||||||
|
})
|
BIN
assets/voxygen/voxel/weapon_components/sword/cultist/cultist_purp_2h-0_blade.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/weapon_components/sword/cultist/cultist_purp_2h-0_blade.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/weapon_components/sword/cultist/cultist_purp_2h-0_handle.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/weapon_components/sword/cultist/cultist_purp_2h-0_handle.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 (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 {
|
let (segment, segment_origin) = Segment::from_voxes(&[
|
||||||
graceful_load_segment_flipped(&spec.vox_spec.0)
|
(&damage_asset.0, Vec3::from(damagespec.1), flipped),
|
||||||
} else {
|
(&held_asset.0, Vec3::from(heldspec.2), flipped),
|
||||||
graceful_load_segment(&spec.vox_spec.0)
|
]);
|
||||||
};
|
(segment, segment_origin.map(|x: i32| x as f32) + heldspec.1)
|
||||||
|
|
||||||
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)
|
|
||||||
} 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)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user