Skip to content

Commit

Permalink
FEAT: Full support for alpha properties
Browse files Browse the repository at this point in the history
  • Loading branch information
magicaldave committed Oct 19, 2024
1 parent 17fac4a commit 93f8e4b
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 34 deletions.
82 changes: 70 additions & 12 deletions resources/Morrowind.fgd
Original file line number Diff line number Diff line change
@@ -1,12 +1,70 @@
@BaseClass = paintable
@BaseClass = material
[
emissive_color(color) : "Color emitted by the brush" : "1.0 0 1.0"
ambient_color(color) : "Ambient color of the brush" : "1.0 0 0"
diffuse_color(color) : "Diffuse color of the brush" : "0 1.0 0"
material_alpha(float) : "Material Transparency" : "1.0"
Material_Emissive_color(color) : "Color emitted by the brush" : "1.0 0 0"
Material_Ambient_color(color) : "Ambient color of the brush" : "0 1.0 0"
Material_Diffuse_color(color) : "Diffuse color of the brush" : "0 0 1.0"

Material_Alpha(float) : "Material Transparency" : "1.0"
Material_Alpha_UseBlend(choices) : "Use alpha blending for this brush" : 0 =
[
0 : "False"
1 : "True"
]
Material_Alpha_BlendSourceMode(choices) : "Mode to use for alpha blending on the light source": 0 =
[
0 : "One"
2 : "Zero"
4 : "Source Color"
6 : "One Minus Source Color"
8 : "Destination Color"
10 : "One Minus Destination Color"
12 : "Source Alpha"
14 : "One Minus Source Alpha"
16 : "Destination Alpha"
18 : "One Minus Destination Alpha"
20 : "Source Alpha Saturate"
]
Material_Alpha_BlendDestinationMode(choices) : "Mode to use for alpha blending on the light destination": 0 =
[
0 : "One"
32 : "Zero"
64 : "Source Color"
96 : "One Minus Source Color"
128 : "Destination Color"
160 : "One Minus Destination Color"
192 : "Source Alpha"
224 : "One Minus Source Alpha"
256 : "Destination Alpha"
288 : "One Minus Destination Alpha"
320 : "Source Alpha Saturate"
]

Material_Alpha_TestEnable(choices) : "Use alpha testing for this brush" : 0 =
[
0 : "False"
512 : "True"
]
Material_Alpha_TestFunction(choices) : "Use alpha testing for this brush" : 0 =
[
0 : "Always"
1024 : "Less"
2048 : "Equal"
3072 : "Less Than Or Equal"
4096 : "Greater Than"
5120 : "Not Equal"
6144 : "Greater Than Or Equal"
7168 : "Never"
]
Material_Alpha_TestThreshold(float) : "Threshold to use against the background when alpha testing": "1.0"

Material_Alpha_NoSort(choices) : "Disable triangle sorting for this object" : 0 =
[
0 : "False"
8192 : "True"
]
]

@SolidClass base(paintable) = worldspawn : "World entity"
@SolidClass base(material) = worldspawn : "World entity"
[
FakeExterior(choices) : "Use sky for this cell" : 0 =
[
Expand Down Expand Up @@ -41,7 +99,7 @@

// Begin common classes

@BaseClass base(paintable) = baseObject
@BaseClass base(material) = baseObject
[
RefId(string) : "Ref Id for the object" : "agronian guy"
Name(string) : "Ingame readable name for the book" : "Tarhiel"
Expand Down Expand Up @@ -2082,15 +2140,15 @@
]
]

@PointClass size(-32 -32 -32, 32 32 32) color (0 64 128) base(PointLightData) = Light_Point64: "64 Unit Light" []
@PointClass size(-32 -32 -32, 32 32 32) iconsprite("Textures/light64.webp") base(PointLightData) = Light_Point64: "64 Unit Light" []

@PointClass size(-32 -32 -32, 32 32 32) color (0 64 128) base(PointLightData) = Light_Point128: "128 Unit Light" [ Radius(int): "Light Radius" : 128 ]
@PointClass size(-64 -64 -64, 64 64 64) iconsprite("Textures/light128.webp") base(PointLightData) = Light_Point128: "128 Unit Light" [ Radius(int): "Light Radius" : 128 ]

@PointClass size(-32 -32 -32, 32 32 32) color (0 64 128) base(PointLightData) = Light_Point256: "256 Unit Light" [ Radius(int): "Light Radius" : 256 ]
@PointClass size(-128 -128 -128, 128 128 128) iconsprite("Textures/light256.webp") base(PointLightData) = Light_Point256: "256 Unit Light" [ Radius(int): "Light Radius" : 256 ]

@PointClass size(-32 -32 -32, 32 32 32) color (0 64 128) base(PointLightData) = Light_Point512: "512 Unit Light" [ Radius(int): "Light Radius" : 512 ]
@PointClass size(-256 -256 -256, 256 256 256) iconsprite("Textures/light512.webp") base(PointLightData) = Light_Point512: "512 Unit Light" [ Radius(int): "Light Radius" : 512 ]

@PointClass size(-32 -32 -32, 32 32 32) color (0 64 128) base(PointLightData) = Light_Point1024: "1024 Unit Light" [ Radius(int): "Light Radius" : 1024 ]
@PointClass size(-512 -512 -512, 512 512 512) iconsprite("Textures/light1024.webp") base(PointLightData) = Light_Point1024: "1024 Unit Light" [ Radius(int): "Light Radius" : 1024 ]

@SolidClass base(baseObject) = world_Container : "Container type" [
Script(string) : "Id of the script used by the object. Optional. Be warned that all instances of this refId will share the script." : "fallingScript"
Expand Down
218 changes: 203 additions & 15 deletions src/brush_ni_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,155 @@ use tes3::nif::{NiTriShape, NiTriShapeData};

use crate::{map_data::MapData, surfaces, Mesh};

macro_rules! define_enum_with_fromstr {
(
$(#[$meta:meta])*
$vis:vis enum $name:ident {
$(
$variant:ident = $value:expr
),* $(,)?
}
default = $default:ident
) => {
#[derive(Clone, Copy, Debug, PartialEq)]
$vis enum $name {
$(
$variant = $value,
)*
}

impl std::str::FromStr for $name {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse::<i32>() {
$(
Ok($value) => Ok($name::$variant),
)*
Ok(v) => {
println!("WARNING: Falling through to default value {:?} for {} (received {})",
$name::$default, stringify!($name), v);
Ok($name::$default)
},
Err(_) => Err(format!("Cannot parse '{}' as {}", s, stringify!($name))),
}
}
}

impl Default for $name {
fn default() -> $name {
$name::$default
}
}
}
}

define_enum_with_fromstr! {
pub enum BrushSourceBlendMode {
One = 0,
Zero = 2,
SourceColor = 4,
OneMinusSourceColor = 6,
DestinationColor = 8,
OneMinusDestinationColor = 10,
SourceAlpha = 12,
OneMinusSourceAlpha = 14,
DestinationAlpha = 16,
OneMinusDestinationALpha = 18,
SourceAlphaSaturate = 20,
}
default = SourceAlpha
}

define_enum_with_fromstr! {
pub enum BrushDestinationBlendMode {
One = 0,
Zero = 32,
SourceColor = 64,
OneMinusSourceColor = 96,
DestinationColor = 128,
OneMinusDestinationColor = 160,
SourceAlpha = 192,
OneMinusSourceAlpha = 224,
DestinationAlpha = 256,
OneMinusDestinationALpha = 288,
SourceAlphaSaturate = 320,
}
default = OneMinusSourceAlpha
}

define_enum_with_fromstr! {
pub enum BrushAlphaTestFunction {
Always = 0,
Less = 1024,
Equal = 2048,
LessThanOrEqual = 3072,
GreaterThan = 4096,
NotEqual = 5120,
GreaterThanOrEqual = 6144,
Never = 7168,
}
default = GreaterThan
}

define_enum_with_fromstr! {
pub enum BrushUseAlpha {
OFF = 0,
BlendEnable = 1,
TestEnable = 512,
}
default = TestEnable
}

define_enum_with_fromstr! {
pub enum BrushNoSort {
OFF = 0,
ON = 8196,
}
default = OFF
}

#[derive(Debug, Default, PartialEq)]
pub struct BrushNiAlphaProps {
pub opacity: Option<f32>,
pub use_blend: Option<BrushUseAlpha>,
pub blend_source_mode: Option<BrushSourceBlendMode>,
pub blend_destination_mode: Option<BrushDestinationBlendMode>,
pub use_test: Option<BrushUseAlpha>,
pub test_function: Option<BrushAlphaTestFunction>,
pub test_threshold: Option<u8>,
pub no_sort: Option<BrushNoSort>,
}

impl BrushNiAlphaProps {
pub fn to_flags(&self) -> u16 {
self.use_blend.unwrap_or_default() as u16
| self.blend_source_mode.unwrap_or_default() as u16
| self.blend_destination_mode.unwrap_or_default() as u16
| self.use_test.unwrap_or_default() as u16
| self.test_function.unwrap_or_default() as u16
| self.no_sort.unwrap_or_default() as u16
}
}

#[derive(Default, PartialEq)]
pub struct BrushNiColorProps {
pub emissive: Option<[f32; 3]>,
pub ambient: Option<[f32; 3]>,
pub diffuse: Option<[f32; 3]>,
}

#[derive(PartialEq)]
pub struct BrushNiMatProps {
pub emissive_color: Option<[f32; 3]>,
pub ambient_color: Option<[f32; 3]>,
pub diffuse_color: Option<[f32; 3]>,
pub alpha: Option<f32>,
pub color: BrushNiColorProps,
pub alpha: BrushNiAlphaProps,
}

impl BrushNiMatProps {
pub fn default() -> BrushNiMatProps {
BrushNiMatProps {
emissive_color: None,
diffuse_color: None,
ambient_color: None,
alpha: None,
color: BrushNiColorProps::default(),
alpha: BrushNiAlphaProps::default(),
}
}
}
Expand Down Expand Up @@ -99,22 +233,76 @@ impl BrushNiNode {

let entity_props = map_data.get_entity_properties(entity_id);

["ambient", "diffuse", "emissive"]
["Ambient", "Diffuse", "Emissive"]
.iter()
.for_each(|color_type| {
if let Some(color) = entity_props.get(&format!("{}_color", color_type)) {
if let Some(color) = entity_props.get(&format!("Material_Color_{}", color_type)) {
let color_value = Some(Self::get_color(color));
match *color_type {
"ambient" => node.mat_props.ambient_color = color_value,
"diffuse" => node.mat_props.diffuse_color = color_value,
"emissive" => node.mat_props.emissive_color = color_value,
"ambient" => node.mat_props.color.ambient = color_value,
"diffuse" => node.mat_props.color.diffuse = color_value,
"emissive" => node.mat_props.color.emissive = color_value,
_ => unreachable!(),
}
}
});

if let Some(value) = entity_props.get(&"material_alpha".to_string()) {
node.mat_props.alpha = Some(
[
"UseBlend",
"BlendSourceMode",
"BlendDestinationMode",
"TestEnable",
"TestFunction",
"TestThreshold",
"NoSort",
]
.iter()
.for_each(|alpha_prop| {
if let Some(prop) = entity_props.get(&format!("Material_Alpha_{}", alpha_prop)) {
println!("Parsing alpha prop {prop}");
match *alpha_prop {
"UseBlend" => {
if let Ok(value) = prop.parse::<BrushUseAlpha>() {
node.mat_props.alpha.use_blend = Some(value);
}
}
"BlendSourceMode" => {
if let Ok(value) = prop.parse::<BrushSourceBlendMode>() {
node.mat_props.alpha.blend_source_mode = Some(value);
}
}
"BlendDestinationMode" => {
if let Ok(value) = prop.parse::<BrushDestinationBlendMode>() {
node.mat_props.alpha.blend_destination_mode = Some(value);
}
}
"TestEnable" => {
if let Ok(value) = prop.parse::<BrushUseAlpha>() {
node.mat_props.alpha.use_test = Some(value);
}
}
"TestFunction" => {
if let Ok(value) = prop.parse::<BrushAlphaTestFunction>() {
node.mat_props.alpha.test_function = Some(value);
}
}
"TestThreshold" => {
if let Ok(value) = prop.parse::<u8>() {
node.mat_props.alpha.test_threshold = Some(value);
}
}
"NoSort" => {
if let Ok(value) = prop.parse::<BrushNoSort>() {
node.mat_props.alpha.no_sort = Some(value);
}
}
_ => unreachable!(),
}
}
});

if let Some(value) = entity_props.get(&"Material_Alpha".to_string()) {
node.mat_props.alpha.opacity = Some(
value
.parse()
.expect("Failed to parse float value from material properties!"),
Expand Down
Loading

0 comments on commit 93f8e4b

Please sign in to comment.