mirror of
https://github.com/Theaninova/mhlib.git
synced 2025-12-11 03:56:18 +00:00
mhk3
This commit is contained in:
124
rust/Cargo.lock
generated
124
rust/Cargo.lock
generated
@@ -242,12 +242,82 @@ dependencies = [
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "godot"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"godot-core",
|
||||
"godot-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"godot4-prebuilt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-codegen"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"heck",
|
||||
"nanoserde",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"godot-codegen",
|
||||
"godot-ffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-ffi"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"godot-codegen",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#c0935ac122dd94983f0f790cd07d6c7629a7429e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"venial",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot4-prebuilt"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/godot-rust/godot4-prebuilt?branch=4.0.1#f9e8cfec0ec565201801b02160ef836800746617"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
@@ -257,6 +327,12 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
@@ -325,8 +401,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "lightwave"
|
||||
name = "lightwave-3d"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9e57b794415e79e3446e057c2c2cc9922c7a83261b2ac6be00b15191e9ed07e"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
]
|
||||
@@ -364,7 +442,18 @@ name = "mhex"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"lightwave",
|
||||
"lightwave-3d",
|
||||
"starforcelib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mhgd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"godot",
|
||||
"itertools",
|
||||
"lightwave-3d",
|
||||
"springylib",
|
||||
"starforcelib",
|
||||
]
|
||||
|
||||
@@ -396,6 +485,21 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755e7965536bc54d7c9fba2df5ada5bf835b0443fd613f0a53fa199a301839d3"
|
||||
dependencies = [
|
||||
"nanoserde-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde-derive"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed7a94da6c6181c35d043fc61c43ac96d3a5d739e7b8027f77650ba41504d6ab"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
@@ -448,6 +552,12 @@ version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.12"
|
||||
@@ -671,6 +781,16 @@ version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "venial"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61584a325b16f97b5b25fcc852eb9550843a251057a5e3e5992d2376f3df4bb2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
|
||||
members = ["renderwarelib", "springylib", "starforcelib", "mhex", "lightwave"]
|
||||
members = ["renderwarelib", "springylib", "starforcelib", "mhex", "mhgd"]
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[package]
|
||||
name = "lightwave"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
binrw = "0.11.1"
|
||||
@@ -1,175 +0,0 @@
|
||||
# LightWave 3D Rust Parser
|
||||
|
||||
Complete LWO2 parser for Rust.
|
||||
|
||||
Basic Usage
|
||||
|
||||
```rust
|
||||
use lightwave::LightWaveObject;
|
||||
|
||||
fn main() {
|
||||
LightWaveObject::read_file("path/to/file.lwo");
|
||||
// or
|
||||
LightWaveObject::read(Cursor::new(vec![0x00, 0x01, ...]))
|
||||
}
|
||||
```
|
||||
|
||||
## LightWave Object (LWO2)
|
||||
|
||||
Fully feature complete following the [LWO2 Spec](http://static.lightwave3d.com/sdk/2015/html/filefmts/lwo2.html).
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|--------------------------------------------|--------|--------|
|
||||
| Layer | `LAYR` | ✅ |
|
||||
| Point List | `PNTS` | ✅ |
|
||||
| Vertex Mapping | `VMAP` | ✅ |
|
||||
| Polygon List | `POLS` | ✅ |
|
||||
| Tag Strings | `TAGS` | ✅ |
|
||||
| Polygon Tag Mapping | `PTAG` | ✅ |
|
||||
| Discontinuous Vertex Mapping | `VMAD` | ✅ |
|
||||
| Vertex Map Parameter | `VMPA` | ✅ |
|
||||
| [Envelope Definition](#envelope-subchunks) | `ENVL` | ✅ |
|
||||
| [Image (-Sequence)](#clip-subchunks) | `CLIP` | ✅ |
|
||||
| [Surface Definition](#surface-subchunks) | `SURF` | ✅ |
|
||||
| Bounding Box | `BBOX` | ✅ |
|
||||
| Description Line | `DESC` | ✅ |
|
||||
| Commentary Text | `TEXT` | ✅ |
|
||||
| Thumbnail Icon Image | `ICON` | ✅ |
|
||||
|
||||
|
||||
### Envelope Subchunks
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|--------------------------|--------|--------|
|
||||
| Envelope Type | `TYPE` | ✅ |
|
||||
| Pre-Behavior | `PRE` | ✅ |
|
||||
| Post-Behavior | `POST` | ✅ |
|
||||
| Keyframe Time and Value | `KEY` | ✅ |
|
||||
| Interval Interpolation | `SPAN` | ✅ |
|
||||
| Plugin Channel Modifiers | `CHAN` | ✅ |
|
||||
| Channel Name | `NAME` | ✅ |
|
||||
|
||||
|
||||
### Clip Subchunks
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|----------------------|--------|--------|
|
||||
| Still Image | `STIL` | ✅ |
|
||||
| Image Sequence | `ISEQ` | ✅ |
|
||||
| Plugin Animation | `ANIM` | ✅ |
|
||||
| Reference (Clone) | `XREF` | ✅ |
|
||||
| Flag (Undocumented) | `FLAG` | ⚠️ |
|
||||
| Color-cycling Still | `STCC` | ✅ |
|
||||
| Time | `TIME` | ✅ |
|
||||
| Color Space RGB | `CLRS` | ✅ |
|
||||
| Color Space Alpha | `CLRA` | ✅ |
|
||||
| Image Filtering | `FILT` | ✅ |
|
||||
| Image Dithering | `DITH` | ✅ |
|
||||
| Contrast | `CONT` | ✅ |
|
||||
| Brightness | `BRIT` | ✅ |
|
||||
| Saturation | `SATR` | ✅ |
|
||||
| Hue | `HUE` | ✅ |
|
||||
| Gamma Correction | `GAMM` | ✅ |
|
||||
| Negative | `NEGA` | ✅ |
|
||||
| Plugin Image Filters | `IFLT` | ✅ |
|
||||
| Plugin Pixel Filters | `PFLT` | ✅ |
|
||||
|
||||
|
||||
### Surface Subchunks
|
||||
|
||||
### Basic Surface Parameters
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|-----------------------------------|----------------------------------------------------------|--------|
|
||||
| Base Color | `COLR` | ✅ |
|
||||
| Base Shading Values | `DIFF`<br>`LUMI`<br>`SPEC`<br>`REFL`<br>`TRAN`<br>`TRNL` | ✅ |
|
||||
| Specular Glossiness | `GLOS` | ✅ |
|
||||
| Diffuse Sharpness | `SHRP` | ✅ |
|
||||
| Bump Intensity | `BUMP` | ✅ |
|
||||
| Polygon Sidedness | `SIDE` | ✅ |
|
||||
| Max Smoothing Angle | `SMAN` | ✅ |
|
||||
| Reflection Options | `RFOP` | ✅ |
|
||||
| Reflection Map Image | `RIMG` | ✅ |
|
||||
| Reflection Map Image Seam Angle | `RSAN` | ✅ |
|
||||
| Reflection Blurring | `RBLR` | ✅ |
|
||||
| Refractive Index | `RIND` | ✅ |
|
||||
| Transparency Options | `TROP` | ✅ |
|
||||
| Refraction Map Image | `TIMG` | ✅ |
|
||||
| Refraction Blurring | `TBLR` | ✅ |
|
||||
| Color Highlights | `CLRH` | ✅ |
|
||||
| Color Filter | `CLRF` | ✅ |
|
||||
| Additive Transparency | `ADRT` | ✅ |
|
||||
| Glow Effect | `GLOW` | ✅ |
|
||||
| Render Outlines | `LINE` | ✅ |
|
||||
| Alpha Mode | `ALPH` | ✅ |
|
||||
| Vertex Color Map | `VCOL` | ✅ |
|
||||
| [Surface Blocks](#surface-blocks) | `BLOK` | 🚧 |
|
||||
|
||||
### Surface Blocks
|
||||
|
||||
Ordinal Strings:
|
||||
* ✅ [Image Texture Map](#image-texture-map) `IMAP`
|
||||
* ✅ [Procedural Texture](#procedural-texture) `PROC`
|
||||
* ✅ [Gradient Texture](#gradient-texture) `GRAD`
|
||||
* ✅ [Shader Plugin](#shaders) `SHDR`
|
||||
|
||||
#### Shared
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|-------------------------|--------|--------|
|
||||
| Texture Channel | `CHAN` | ✅ |
|
||||
| Enable State | `ENAB` | ✅ |
|
||||
| Opacity | `OPAC` | ✅ |
|
||||
| Displacement Axis | `AXIS` | ✅ |
|
||||
| Negative (Undocumented) | `NEGA` | ⚠️ |
|
||||
|
||||
#### Texture Mapping
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|---------------------|----------------------------|--------|
|
||||
| Positioning | `CNTR`<br>`SIZE`<br>`ROTA` | ✅ |
|
||||
| Reference Object | `OREF` | ✅ |
|
||||
| Falloff | `FALL` | ✅ |
|
||||
| Coordinate System | `CSYS` | ✅ |
|
||||
|
||||
#### Image Texture Map
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|-------------------------------------|------------------|--------|
|
||||
| [Texture Mapping](#texture-mapping) | `TMAP` | ✅ |
|
||||
| Projection Mode | `PROJ` | ✅ |
|
||||
| Major Axis | `AXIS` | ✅ |
|
||||
| Image Map | `IMAG` | ✅ |
|
||||
| Image Wrap Options | `WRAP` | ✅ |
|
||||
| Image Wrap Amount | `WRPW`<br>`WRPH` | ✅ |
|
||||
| UV Vertex Map | `VMAP` | ✅ |
|
||||
| Antialiasing Strength | `AAST` | ✅ |
|
||||
| Pixel Blending | `PIXB` | ✅ |
|
||||
| Sticky Projection | `STCK` | ✅ |
|
||||
| Texture Ampliture | `TAMP` | ✅ |
|
||||
|
||||
#### Procedural Texture
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|--------------------------|--------|--------|
|
||||
| Axis | `AXIS` | ✅ |
|
||||
| Basic Value | `VALU` | ✅ |
|
||||
| Algorithm and Parameters | `FUNC` | ✅ |
|
||||
|
||||
#### Gradient Texture
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|----------------|-------------------|--------|
|
||||
| Parameter Name | `PNAM` | ✅ |
|
||||
| Item Name | `INAM` | ✅ |
|
||||
| Gradient Range | `GRST`<br>`GREN` | ✅ |
|
||||
| Repeat Mode | `GRPT` | ✅ |
|
||||
| Key Values | `FKEY` | ✅ |
|
||||
| Key Parameters | `IKEY` | ✅ |
|
||||
|
||||
#### Shaders
|
||||
|
||||
|
||||
| Chunk | Tag | Status |
|
||||
|------------------|--------|--------|
|
||||
| Shader Algorithm | `FUNC` | ✅ |
|
||||
@@ -1,69 +0,0 @@
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, BinRead, BinResult, Endian};
|
||||
use std::io::{Read, Seek};
|
||||
use std::iter::from_fn;
|
||||
|
||||
#[binread]
|
||||
#[br(assert(false, "Not implemented yet"))]
|
||||
#[derive(Debug)]
|
||||
pub struct BinReadTodo();
|
||||
|
||||
pub fn until_size_limit<R, Arg, T, Ret>(
|
||||
limit: u64,
|
||||
) -> impl Fn(&mut R, Endian, Arg) -> BinResult<Ret>
|
||||
where
|
||||
T: for<'a> BinRead<Args<'a> = Arg>,
|
||||
R: Read + Seek,
|
||||
Arg: Clone,
|
||||
Ret: FromIterator<T>,
|
||||
{
|
||||
until_size_limit_with(limit, default_reader)
|
||||
}
|
||||
|
||||
/// Reads data until total size reaches a limit
|
||||
pub fn until_size_limit_with<R, Arg, T, ReadFn, Ret>(
|
||||
limit: u64,
|
||||
reader_fn: ReadFn,
|
||||
) -> impl Fn(&mut R, Endian, Arg) -> BinResult<Ret>
|
||||
where
|
||||
T: for<'a> BinRead<Args<'a> = Arg>,
|
||||
R: Read + Seek,
|
||||
Arg: Clone,
|
||||
ReadFn: Fn(&mut R, Endian, Arg) -> BinResult<T>,
|
||||
Ret: FromIterator<T>,
|
||||
{
|
||||
move |reader, endian, args| {
|
||||
let pos = reader.stream_position()?;
|
||||
from_fn(|| match reader.stream_position() {
|
||||
Ok(now) if now - pos < limit => Some(reader_fn(reader, endian, args.clone())),
|
||||
Ok(_) => None,
|
||||
Err(err) => Some(Err(binrw::Error::Io(err))),
|
||||
})
|
||||
.fuse()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count_with_vx<R>(n: usize) -> impl Fn(&mut R, Endian, ()) -> BinResult<Vec<u32>>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
move |reader, endian, _args: ()| {
|
||||
core::iter::repeat_with(|| vx(reader, endian, ()))
|
||||
.take(n)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn default_reader<'a, T: BinRead, R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
endian: Endian,
|
||||
args: T::Args<'a>,
|
||||
) -> BinResult<T>
|
||||
where
|
||||
T::Args<'a>: Clone,
|
||||
{
|
||||
let mut value = T::read_options(reader, endian, args.clone())?;
|
||||
value.after_parse(reader, endian, args)?;
|
||||
Ok(value)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
use binrw::{binread, BinRead};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub struct Chunk<D>
|
||||
where
|
||||
for<'a> D: BinRead<Args<'a> = (u32,)>,
|
||||
{
|
||||
pub length: u32,
|
||||
#[br(pad_size_to = length, align_after = 2, args(length))]
|
||||
pub data: D,
|
||||
}
|
||||
|
||||
impl<D> Deref for Chunk<D>
|
||||
where
|
||||
for<'a> D: BinRead<Args<'a> = (u32,)>,
|
||||
{
|
||||
type Target = D;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub struct SubChunk<D>
|
||||
where
|
||||
for<'a> D: BinRead<Args<'a> = (u32,)>,
|
||||
{
|
||||
pub length: u16,
|
||||
#[br(pad_size_to = length, align_after = 2, args(length as u32))]
|
||||
pub data: D,
|
||||
}
|
||||
|
||||
impl<D> Deref for SubChunk<D>
|
||||
where
|
||||
for<'a> D: BinRead<Args<'a> = (u32,)>,
|
||||
{
|
||||
type Target = D;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use crate::lwo2::tags::Tag;
|
||||
use binrw::{binread, until_eof, BinRead, BinResult};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek};
|
||||
use std::path::Path;
|
||||
|
||||
mod binrw_helpers;
|
||||
pub mod iff;
|
||||
pub mod lwo2;
|
||||
|
||||
/// The data in LightWave 3D® object files comprise the points, polygons and surfaces that describe
|
||||
/// the geometry and appearance of an object. "Polygons" here means any of several geometric
|
||||
/// elements (faces, curves or patches, for example) defined by an ordered list of points, and
|
||||
/// "surfaces" refers to the collection of attributes, sometimes called materials, that define
|
||||
/// the visual surface properties of polygons.
|
||||
///
|
||||
/// Object files can contain multiple layers, or parts, and each part can be a single connected mesh
|
||||
/// or several disjoint meshes. They may also contain one or more surface definitions with no points
|
||||
/// or polygons at all. Surface definitions can include references to other files (images, for
|
||||
/// example), plug-ins, and envelopes containing parameter values that vary over time.
|
||||
///
|
||||
/// This document outlines the object file format and provides a detailed reference for each of the
|
||||
/// components. The component descriptions include both a regular expression defining the syntax and
|
||||
/// a discussion of the contents. See also the Examples supplement, a more conversational
|
||||
/// introduction to the format that includes annotated listings of file contents as well as
|
||||
/// several sample files.
|
||||
/// Informally, object files start with the four bytes "FORM" followed by a four-byte integer giving
|
||||
/// the length of the file (minus 8) and the four byte ID "LWO2". The remainder of the data is a
|
||||
/// collection of chunks, some of which will contain subchunks.
|
||||
///
|
||||
/// To be read, IFF files must be parsed. The order in which chunks can occur in a file isn't fixed.
|
||||
/// Some chunks, however, contain data that depends on the contents of other chunks, and this fixes
|
||||
/// a relative order for the chunks involved. Chunks and subchunks also depend on context for their
|
||||
/// meaning. The CHAN subchunk in an envelope chunk isn't the same thing as the CHAN subchunk in a
|
||||
/// surface block. And you may encounter chunks that aren't defined here, which you should be
|
||||
/// prepared to skip gracefully if you don't understand them. You can do this by using the chunk
|
||||
/// size to seek to the next chunk.
|
||||
#[binread]
|
||||
#[br(big, magic(b"FORM"))]
|
||||
#[derive(Debug)]
|
||||
pub struct LightWaveObject {
|
||||
pub file_size: u32,
|
||||
#[br(magic(b"LWO2"), parse_with = until_eof)]
|
||||
pub data: Vec<Tag>,
|
||||
}
|
||||
|
||||
impl LightWaveObject {
|
||||
pub fn read_file<P: AsRef<Path>>(path: P) -> std::io::Result<LightWaveObject> {
|
||||
let mut reader = File::open(path)?;
|
||||
Self::read(&mut reader)
|
||||
.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
|
||||
}
|
||||
|
||||
pub fn read<R>(reader: &mut R) -> BinResult<LightWaveObject>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
BinRead::read(reader)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
use binrw::{BinReaderExt, BinResult, Endian};
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub mod sub_tags;
|
||||
pub mod tags;
|
||||
|
||||
/// This is an index into an array of items (points or polygons), or a collection of items
|
||||
/// each uniquely identified by an integer (clips or envelopes). A VX is written as a variable
|
||||
/// length 2- or 4-byte element. If the index value is less than 65,280 (0xFF00), then the
|
||||
/// index is written as an unsigned two-byte integer. Otherwise the index is written as an
|
||||
/// unsigned four byte integer with bits 24-31 set. When reading an index, if the first byte
|
||||
/// encountered is 255 (0xFF), then the four-byte form is being used and the first byte should
|
||||
/// be discarded or masked out.
|
||||
pub fn vx<R>(reader: &mut R, endian: Endian, _args: ()) -> BinResult<u32>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let kind: u16 = reader.read_type(endian)?;
|
||||
Ok(if kind < 0xff00 {
|
||||
kind as u32
|
||||
} else {
|
||||
(((kind as u32) & 0xff) << 16) | (reader.read_type::<u16>(endian)? as u32)
|
||||
})
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::Name;
|
||||
use binrw::binread;
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum GradientTextureSubChunk {
|
||||
#[br(magic(b"PNAM"))]
|
||||
ParameterName(SubChunk<Name>),
|
||||
#[br(magic(b"INAM"))]
|
||||
ItemName(SubChunk<Name>),
|
||||
#[br(magic(b"GRST"))]
|
||||
GradientRangeStart(SubChunk<GradientRange>),
|
||||
#[br(magic(b"GREN"))]
|
||||
GradientRangeEnd(SubChunk<GradientRange>),
|
||||
#[br(magic(b"GRPT"))]
|
||||
RepeatMode(SubChunk<RepeatMode>),
|
||||
#[br(magic(b"FKEY"))]
|
||||
KeyValues(SubChunk<KeyValues>),
|
||||
#[br(magic(b"IKEY"))]
|
||||
KeyParameters(SubChunk<KeyParameters>),
|
||||
}
|
||||
|
||||
/// The repeat mode. This is currently undefined.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct KeyParameters {
|
||||
#[br(count = length / 2)]
|
||||
pub repeat_mode: Vec<u16>,
|
||||
}
|
||||
|
||||
/// The transfer function is defined by an array of keys, each with an input value and an RGBA
|
||||
/// output vector. Given an input value, the gradient can be evaluated by selecting the keys whose
|
||||
/// positions bracket the value and interpolating between their outputs. If the input value is lower
|
||||
/// than the first key or higher than the last key, the gradient value is the value of the closest
|
||||
/// key.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct KeyValues {
|
||||
#[br(count = length / 18)]
|
||||
pub key_values: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub struct KeyValue {
|
||||
pub input: f32,
|
||||
pub output: [f32; 4],
|
||||
}
|
||||
|
||||
/// The start and end of the input range. These values only affect the display of the gradient
|
||||
/// in the user interface. They don't affect rendering.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct GradientRange {
|
||||
pub name: f32,
|
||||
}
|
||||
|
||||
/// The repeat mode. This is currently undefined.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct RepeatMode {
|
||||
pub repeat_mode: u16,
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::texture_mapping::TextureMapping;
|
||||
use crate::lwo2::sub_tags::{ValueEnvelope, VxReference};
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum SurfaceBlockImageTextureSubChunk {
|
||||
#[br(magic(b"TMAP"))]
|
||||
TextureMapping(SubChunk<TextureMapping>),
|
||||
#[br(magic(b"PROJ"))]
|
||||
ProjectionMode(SubChunk<ProjectionMode>),
|
||||
#[br(magic(b"AXIS"))]
|
||||
MajorAxis(SubChunk<MajorAxis>),
|
||||
#[br(magic(b"IMAG"))]
|
||||
ImageMap(SubChunk<VxReference>),
|
||||
#[br(magic(b"WRAP"))]
|
||||
ImageWrapOptions(SubChunk<ImageWrapOptions>),
|
||||
#[br(magic(b"WRPW"))]
|
||||
ImageWrapAmountWidth(SubChunk<ImageWrapAmount>),
|
||||
#[br(magic(b"WRPH"))]
|
||||
ImageWrapAmountHeight(SubChunk<ImageWrapAmount>),
|
||||
#[br(magic(b"VMAP"))]
|
||||
UvVertexMap(SubChunk<UvMap>),
|
||||
#[br(magic(b"AAST"))]
|
||||
AntialiasingStrength(SubChunk<AntialiasingStrength>),
|
||||
#[br(magic(b"PIXB"))]
|
||||
PixelBlending(SubChunk<PixelBlending>),
|
||||
#[br(magic(b"STICK"))]
|
||||
StickyProjection(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"TAMP"))]
|
||||
TextureAmplitude(SubChunk<ValueEnvelope>),
|
||||
}
|
||||
|
||||
/// The major axis used for planar, cylindrical and spherical projections. The value is 0, 1 or 2
|
||||
/// for the X, Y or Z axis.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct MajorAxis {
|
||||
pub texture_axis: u16,
|
||||
}
|
||||
|
||||
/// Pixel blending enlarges the sample filter when it would otherwise be smaller than a single
|
||||
/// image map pixel. If the low-order flag bit is set, then pixel blending is enabled.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PixelBlending {
|
||||
pub flags: u16,
|
||||
}
|
||||
|
||||
/// The low bit of the flags word is an enable flag for texture antialiasing. The antialiasing
|
||||
/// strength is proportional to the width of the sample filter, so larger values sample a larger
|
||||
/// area of the image.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct AntialiasingStrength {
|
||||
pub flags: u16,
|
||||
pub strength: f32,
|
||||
}
|
||||
|
||||
/// For UV projection, which depends on texture coordinates at each vertex, this selects the name of
|
||||
/// the TXUV vertex map that contains those coordinates.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct UvMap {
|
||||
#[br(align_after = 2)]
|
||||
pub txuv_map_name: NullString,
|
||||
}
|
||||
|
||||
/// For cylindrical and spherical projections, these parameters control how many times the image
|
||||
/// repeats over each full interval.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ImageWrapAmount {
|
||||
pub cycles: f32,
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
}
|
||||
|
||||
/// Specifies how the color of the texture is derived for areas outside the image.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ImageWrapOptions {
|
||||
pub width_wrap: ImageWrapType,
|
||||
pub height_wrap: ImageWrapType,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum ImageWrapType {
|
||||
/// Areas outside the image are assumed to be black. The ultimate effect of this depends on
|
||||
/// the opacity settings. For an additive texture layer on the color channel, the final color
|
||||
/// will come from the preceding layers or from the base color of the surface.
|
||||
Reset = 0,
|
||||
/// The image is repeated or tiled.
|
||||
Repeat = 1,
|
||||
/// Like repeat, but alternate tiles are mirror-reversed.
|
||||
Mirror = 2,
|
||||
/// The color is taken from the image's nearest edge pixel.
|
||||
Edge = 3,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16, import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub enum ProjectionMode {
|
||||
Planar = 0,
|
||||
Cylindrical = 1,
|
||||
Spherical = 2,
|
||||
Cubic = 3,
|
||||
FrontProjection = 4,
|
||||
UV = 5,
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::gradient_texture::GradientTextureSubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::image_texture::SurfaceBlockImageTextureSubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::procedural_texture::ProceduralTextureSubChunk;
|
||||
use crate::lwo2::sub_tags::EnableState;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
pub mod gradient_texture;
|
||||
pub mod image_texture;
|
||||
pub mod procedural_texture;
|
||||
pub mod texture_mapping;
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub enum SurfaceBlocks {
|
||||
#[br(magic(b"IMAP"))]
|
||||
ImageMapTexture {
|
||||
header: SubChunk<SurfaceBlockHeader>,
|
||||
#[br(parse_with = until_size_limit(length as u64 - (header.length as u64 + 2 + 4)))]
|
||||
attributes: Vec<SurfaceBlockImageTextureSubChunk>,
|
||||
},
|
||||
#[br(magic(b"PROC"))]
|
||||
ProceduralTexture {
|
||||
header: SubChunk<SurfaceBlockHeader>,
|
||||
#[br(parse_with = until_size_limit(length as u64 - (header.length as u64 + 2 + 4)))]
|
||||
attributes: Vec<ProceduralTextureSubChunk>,
|
||||
},
|
||||
#[br(magic(b"GRAD"))]
|
||||
GradientTexture {
|
||||
header: SubChunk<SurfaceBlockHeader>,
|
||||
#[br(parse_with = until_size_limit(length as u64 - (header.length as u64 + 2 + 4)))]
|
||||
attributes: Vec<GradientTextureSubChunk>,
|
||||
},
|
||||
#[br(magic(b"SHDR"))]
|
||||
ShaderPlugin {
|
||||
header: SubChunk<SurfaceBlockHeader>,
|
||||
#[br(magic(b"FUNC"))]
|
||||
algorithm: SubChunk<Algorithm>,
|
||||
},
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Algorithm {
|
||||
#[br(align_after = 2)]
|
||||
pub algorithm_name: NullString,
|
||||
#[br(count = length - (algorithm_name.len() as u32 + 1))]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct SurfaceBlockHeader {
|
||||
#[br(pad_before = 2)]
|
||||
#[br(parse_with = until_size_limit(length as u64 - 4))]
|
||||
pub block_attributes: Vec<SurfaceBlockHeaderSubChunk>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum SurfaceBlockHeaderSubChunk {
|
||||
#[br(magic(b"CHAN"))]
|
||||
Channel(SubChunk<Channel>),
|
||||
#[br(magic(b"ENAB"))]
|
||||
EnabledState(SubChunk<EnableState>),
|
||||
#[br(magic(b"OPAC"))]
|
||||
Opacity(SubChunk<Opacity>),
|
||||
#[br(magic(b"AXIS"))]
|
||||
DisplacementAxis(SubChunk<DisplacementAxis>),
|
||||
#[br(magic(b"NEGA"))]
|
||||
Negative(SubChunk<EnableState>),
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct DisplacementAxis {
|
||||
pub displacement_axis: u16,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Opacity {
|
||||
pub kind: OpacityType,
|
||||
pub opacity: f32,
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum OpacityType {
|
||||
Normal = 0,
|
||||
Subtractive = 1,
|
||||
Difference = 2,
|
||||
Multiply = 3,
|
||||
Divide = 4,
|
||||
Alpha = 5,
|
||||
TextureDisplacement = 6,
|
||||
Additive = 7,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Channel {
|
||||
pub texture_channel: TextureChannel,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum TextureChannel {
|
||||
#[br(magic(b"COLR"))]
|
||||
Color,
|
||||
#[br(magic(b"DIFF"))]
|
||||
Diffuse,
|
||||
#[br(magic(b"LUMI"))]
|
||||
Luminosity,
|
||||
#[br(magic(b"SPEC"))]
|
||||
Specular,
|
||||
#[br(magic(b"GLOS"))]
|
||||
Glossy,
|
||||
#[br(magic(b"REFL"))]
|
||||
Reflectivity,
|
||||
#[br(magic(b"TRAN"))]
|
||||
Transparency,
|
||||
#[br(magic(b"RIND"))]
|
||||
RefractiveIndex,
|
||||
#[br(magic(b"TRNL"))]
|
||||
Translucency,
|
||||
#[br(magic(b"BUMP"))]
|
||||
Bump,
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::Algorithm;
|
||||
use binrw::binread;
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum ProceduralTextureSubChunk {
|
||||
#[br(magic(b"AXIS"))]
|
||||
Axis(SubChunk<Axis>),
|
||||
#[br(magic(b"VALU"))]
|
||||
BasicValue(SubChunk<BasicValue>),
|
||||
#[br(magic(b"FUNC"))]
|
||||
AlgorithmAndParameters(SubChunk<Algorithm>),
|
||||
}
|
||||
|
||||
/// Procedurals are often modulations between the current channel value and another value, given
|
||||
/// here. This may be a scalar or a vector.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct BasicValue {
|
||||
#[br(count = length / 4)]
|
||||
pub value: Vec<f32>,
|
||||
}
|
||||
|
||||
/// If the procedural has an axis, it may be defined with this chunk using a value of 0, 1 or 2.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Axis {
|
||||
pub axis: u16,
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::VectorEnvelope;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct TextureMapping {
|
||||
#[br(parse_with = until_size_limit(length as u64))]
|
||||
pub attributes: Vec<TextureMappingSubChunk>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum TextureMappingSubChunk {
|
||||
#[br(magic(b"CNTR"))]
|
||||
Center(SubChunk<VectorEnvelope>),
|
||||
#[br(magic(b"SIZE"))]
|
||||
Size(SubChunk<VectorEnvelope>),
|
||||
#[br(magic(b"ROTA"))]
|
||||
Rotation(SubChunk<VectorEnvelope>),
|
||||
#[br(magic(b"OREF"))]
|
||||
ReferenceObject(SubChunk<ReferenceObject>),
|
||||
#[br(magic(b"FALL"))]
|
||||
Falloff(SubChunk<Falloff>),
|
||||
#[br(magic(b"CSYS"))]
|
||||
CoordinateSystem(SubChunk<CoordinateSystem>),
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16, import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub enum CoordinateSystem {
|
||||
ObjectCoordinates = 0,
|
||||
WorldCoordinates = 1,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ReferenceObject {
|
||||
#[br(align_after = 2)]
|
||||
pub object_name: NullString,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Falloff {
|
||||
pub kind: FalloffType,
|
||||
pub vector: [f32; 3],
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum FalloffType {
|
||||
Cubic = 0,
|
||||
Spherical = 1,
|
||||
LinearX = 2,
|
||||
LinearY = 3,
|
||||
LinearZ = 4,
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
pub mod blocks;
|
||||
pub mod plugin;
|
||||
pub mod surface_parameters;
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct VectorEnvelope {
|
||||
pub base_color: [f32; 3],
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Name {
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ValueEnvelope {
|
||||
pub value: f32,
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct VxReference {
|
||||
#[br(parse_with = vx)]
|
||||
pub texture_image: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct EnableState {
|
||||
pub enable: u16,
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PluginServerNameAndData {
|
||||
#[br(temp)]
|
||||
start_pos: PosValue<()>,
|
||||
#[br(align_after = 2)]
|
||||
pub server_name: NullString,
|
||||
pub flags: u16,
|
||||
#[br(temp)]
|
||||
end_pos: PosValue<()>,
|
||||
#[br(count = length as u64 - (end_pos.pos - start_pos.pos))]
|
||||
pub parameters: Vec<u8>,
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::blocks::SurfaceBlocks;
|
||||
use crate::lwo2::sub_tags::{ValueEnvelope, VectorEnvelope, VxReference};
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum SurfaceParameterSubChunk {
|
||||
#[br(magic(b"COLR"))]
|
||||
BaseColor(SubChunk<VectorEnvelope>),
|
||||
#[br(magic(b"DIFF"))]
|
||||
BaseShadingValueDiffuse(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"LUMI"))]
|
||||
BaseShadingValueLuminosity(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"SPEC"))]
|
||||
BaseShadingValueSpecular(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"REFL"))]
|
||||
BaseShadingValueReflectivity(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"TRAN"))]
|
||||
BaseShadingValueTransparency(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"TRNL"))]
|
||||
BaseShadingValueTranslucency(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"GLOS"))]
|
||||
SpecularGlossiness(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"SHRP"))]
|
||||
DiffuseSharpness(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"BUMP"))]
|
||||
BumpIntensity(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"SIDE"))]
|
||||
PolygonSidedness(SubChunk<PolygonSidedness>),
|
||||
#[br(magic(b"SMAN"))]
|
||||
MaxSmoothingAngle(SubChunk<MaxSmoothingAngle>),
|
||||
#[br(magic(b"RFOP"))]
|
||||
ReflectionOptions(SubChunk<ReflectionOptions>),
|
||||
#[br(magic(b"RIMG"))]
|
||||
ReflectionMapImage(SubChunk<VxReference>),
|
||||
#[br(magic(b"RSAN"))]
|
||||
ReflectionMapSeamAngle(SubChunk<VectorEnvelope>),
|
||||
#[br(magic(b"RBLR"))]
|
||||
ReflectionBlurring(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"RIND"))]
|
||||
RefractiveIndex(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"TROP"))]
|
||||
TransparencyOptions(SubChunk<ReflectionOptions>),
|
||||
#[br(magic(b"TIMG"))]
|
||||
RefractionMapImage(SubChunk<VxReference>),
|
||||
#[br(magic(b"TBLR"))]
|
||||
RefractionBlurring(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"CLRH"))]
|
||||
ColorHighlights(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"CLRF"))]
|
||||
ColorFilter(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"ADTR"))]
|
||||
AdditiveTransparency(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"GLOW"))]
|
||||
GlowEffect(SubChunk<GlowEffect>),
|
||||
#[br(magic(b"LINE"))]
|
||||
RenderOutlines(SubChunk<RenderOutlines>),
|
||||
#[br(magic(b"ALPH"))]
|
||||
AlphaMode(SubChunk<AlphaMode>),
|
||||
#[br(magic(b"VCOL"))]
|
||||
VertexColorMap(SubChunk<VertexColorMap>),
|
||||
#[br(magic(b"BLOK"))]
|
||||
Blocks(SubChunk<SurfaceBlocks>),
|
||||
}
|
||||
|
||||
/// The vertex color map subchunk identifies an RGB or RGBA VMAP that will be used to color the surface.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct VertexColorMap {
|
||||
pub intensity: f32,
|
||||
#[br(parse_with = vx)]
|
||||
pub envelope: u32,
|
||||
pub vmap_type: [u8; 4],
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
}
|
||||
|
||||
/// The alpha mode defines the alpha channel output options for the surface.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct AlphaMode {
|
||||
pub mode: AlphaModeMode,
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum AlphaModeMode {
|
||||
/// The surface has no effect on the alpha channel when rendered.
|
||||
UnaffectedBySurface = 0,
|
||||
/// The alpha channel will be written with the constant value following the mode in the subchunk.
|
||||
ConstantValue = 1,
|
||||
/// The alpha value is derived from surface opacity, which is the default if the ALPH chunk is missing.
|
||||
SurfaceOpacity = 2,
|
||||
/// The alpha value comes from the shadow density.
|
||||
ShadowDensity = 3,
|
||||
}
|
||||
|
||||
/// The line effect draws the surface as a wireframe of the polygon edges. Currently the only flag
|
||||
/// defined is an enable switch in the low bit. The size is the thickness of the lines in pixels,
|
||||
/// and the color, if not given, is the base color of the surface. Note that you may encounter
|
||||
/// LINE subchunks with no color information (these will have a subchunk length of 8 bytes) and
|
||||
/// possibly without size information (subchunk length 2).
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct RenderOutlines {
|
||||
pub flags: u16,
|
||||
#[br(if(length > 2))]
|
||||
pub size: f32,
|
||||
#[br(if(length > 2))]
|
||||
#[br(parse_with = vx)]
|
||||
pub size_envelope: u32,
|
||||
#[br(if(length > 8))]
|
||||
pub color: [f32; 3],
|
||||
#[br(if(length > 8))]
|
||||
#[br(parse_with = vx)]
|
||||
pub color_envelope: u32,
|
||||
}
|
||||
|
||||
/// The glow effect causes a surface to spread and affect neighboring areas of the image. The type
|
||||
/// can be 0 for Hastings glow, and 1 for image convolution. The size and intensity define how large
|
||||
/// and how strong the effect is.
|
||||
///
|
||||
/// You may also encounter glow information written in a GVAL subchunk containing only the intensity
|
||||
/// and its envelope (the subchunk length is 6).
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct GlowEffect {
|
||||
pub kind: GlowType,
|
||||
pub intensity: f32,
|
||||
#[br(parse_with = vx)]
|
||||
pub intensity_envelope: u32,
|
||||
#[br(if(length > 6))]
|
||||
pub size: f32,
|
||||
#[br(if(length > 6))]
|
||||
#[br(parse_with = vx)]
|
||||
pub size_envelope: u32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum GlowType {
|
||||
HastingsGlow = 0,
|
||||
ImageConvolution = 1,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16, import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub enum ReflectionOptions {
|
||||
BackdropOnly = 0,
|
||||
RaytracingAndBackdrop = 1,
|
||||
SphericalMap = 2,
|
||||
RaytracingAndSphericalMap = 3,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PolygonSidedness {
|
||||
pub sidedness: u16,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct MaxSmoothingAngle {
|
||||
pub max_smoothing_angle: f32,
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use binrw::binread;
|
||||
|
||||
///Store the bounding box for the vertex data in a layer. Optional. The min and max vectors are
|
||||
/// the lower and upper corners of the bounding box.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct BoundingBox {
|
||||
pub min: [f32; 3],
|
||||
pub max: [f32; 3],
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
/// (Introduced with LightWave® 6.5.) Associates a set of floating-point vectors with the vertices
|
||||
/// of specific polygons. VMADs are similar to VMAPs, but they assign vectors to polygon vertices
|
||||
/// rather than points. For a given mapping, a VMAP always assigns only one vector to a point, while
|
||||
/// a VMAD can assign as many vectors to a point as there are polygons sharing the point.
|
||||
///
|
||||
/// The motivation for VMADs is the problem of seams in UV texture mapping. If a UV map is
|
||||
/// topologically equivalent to a cylinder or a sphere, a seam is formed where the opposite edges of
|
||||
/// the map meet. Interpolation of UV coordinates across this discontinuity is aesthetically and
|
||||
/// mathematically incorrect. The VMAD substitutes an equivalent mapping that interpolates
|
||||
/// correctly. It only needs to do this for polygons in which the seam lies.
|
||||
///
|
||||
/// VMAD chunks are paired with VMAPs of the same name, if they exist. The vector values in the VMAD
|
||||
/// will then replace those in the corresponding VMAP, but only for calculations involving the
|
||||
/// specified polygons. When the same points are used for calculations on polygons not specified in
|
||||
/// the VMAD, the VMAP values are used.
|
||||
///
|
||||
/// VMADs need not be associated with a VMAP. They can also be used simply to define a
|
||||
/// (discontinuous) per-polygon mapping. But not all mapping types are valid for VMADs, since for
|
||||
/// some types it makes no sense for points to have more than one map value. TXUV, RGB, RGBA and
|
||||
/// WGHT types are supported for VMADs, for example, while MORF and SPOT are not. VMADs of
|
||||
/// unsupported types are preserved but never evaluated.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct DiscontinuousVertexMappings {
|
||||
#[br(temp)]
|
||||
pub start_pos: PosValue<()>,
|
||||
pub kind: [u8; 4],
|
||||
#[br(temp)]
|
||||
pub dimension: u16,
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
#[br(temp)]
|
||||
pub end_pos: PosValue<()>,
|
||||
#[br(parse_with = |reader, endian, _: ()| until_size_limit(length as u64 - (end_pos.pos - start_pos.pos))(reader, endian, (dimension, )))]
|
||||
pub mappings: Vec<DiscontinuousVertexMapping>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(dimension: u16))]
|
||||
#[derive(Debug)]
|
||||
pub struct DiscontinuousVertexMapping {
|
||||
#[br(parse_with = vx)]
|
||||
pub vert: u32,
|
||||
#[br(parse_with = vx)]
|
||||
pub poly: u32,
|
||||
#[br(count = dimension)]
|
||||
pub values: Vec<f32>,
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
use crate::lwo2::sub_tags::plugin::PluginServerNameAndData;
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct EnvelopeDefinition {
|
||||
#[br(temp)]
|
||||
pos_start: PosValue<()>,
|
||||
#[br(parse_with = vx)]
|
||||
pub index: u32,
|
||||
#[br(temp)]
|
||||
pos_end: PosValue<()>,
|
||||
#[br(parse_with = until_size_limit(length as u64 - (pos_end.pos - pos_start.pos)))]
|
||||
pub attributes: Vec<EnvelopeSubChunk>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum EnvelopeSubChunk {
|
||||
#[br(magic(b"TYPE"))]
|
||||
EnvelopeType(SubChunk<EnvelopeType>),
|
||||
#[br(magic(b"PRE"))]
|
||||
PreBehavior(SubChunk<Behavior>),
|
||||
#[br(magic(b"POST"))]
|
||||
PostBehavior(SubChunk<Behavior>),
|
||||
#[br(magic(b"KEY"))]
|
||||
KeyframeTimeAndValue(SubChunk<KeyframeTimeAndValue>),
|
||||
#[br(magic(b"SPAN"))]
|
||||
IntervalInterpolation(SubChunk<IntervalInterpolation>),
|
||||
#[br(magic(b"CHAN"))]
|
||||
PluginChannelModifiers(SubChunk<PluginServerNameAndData>),
|
||||
#[br(magic(b"NAME"))]
|
||||
ChannelName(SubChunk<PluginChannelName>),
|
||||
}
|
||||
|
||||
/// An optional name for the envelope. LightWave® itself ignores the names of surface envelopes,
|
||||
/// but plug-ins can browse the envelope database by name.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PluginChannelName {
|
||||
#[br(align_after = 2)]
|
||||
pub channel_name: NullString,
|
||||
}
|
||||
|
||||
/// Defines the interpolation between the most recent KEY chunk and the KEY immediately before it in
|
||||
/// time. The type identifies the interpolation algorithm and can be STEP, LINE, TCB
|
||||
/// (Kochanek-Bartels), HERM (Hermite), BEZI (1D Bezier) or BEZ2 (2D Bezier).
|
||||
/// Different parameters are stored for each of these.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct IntervalInterpolation {
|
||||
pub kind: IntervalInterpolationType,
|
||||
#[br(count = (length - 4) / 4)]
|
||||
pub parameters: Vec<f32>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum IntervalInterpolationType {
|
||||
#[br(magic(b"STEP"))]
|
||||
Step,
|
||||
#[br(magic(b"LINE"))]
|
||||
Line,
|
||||
#[br(magic(b"TCB\0"))]
|
||||
KochanekBartels,
|
||||
#[br(magic(b"HERM"))]
|
||||
Hermite,
|
||||
#[br(magic(b"BEZI"))]
|
||||
Bezier1D,
|
||||
#[br(magic(b"BEZ2"))]
|
||||
Bezier2D,
|
||||
}
|
||||
|
||||
/// The value of the envelope at the specified time in seconds. The signal value between keyframes
|
||||
/// is interpolated. The time of a keyframe isn't restricted to integer frames.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct KeyframeTimeAndValue {
|
||||
pub time: f32,
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16, import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub enum Behavior {
|
||||
/// Sets the value to 0.0.
|
||||
Reset = 0,
|
||||
/// Sets the value to the value at the nearest key.
|
||||
Constant = 1,
|
||||
/// Repeats the interval between the first and last keys (the primary interval).
|
||||
Repeat = 2,
|
||||
/// Like Repeat, but alternating copies of the primary interval are time-reversed.
|
||||
Oscillate = 3,
|
||||
/// Like Repeat, but offset by the difference between the values of the first and last keys.
|
||||
OffsetRepeat = 4,
|
||||
/// Linearly extrapolates the value based on the tangent at the nearest key.
|
||||
Linear = 5,
|
||||
}
|
||||
|
||||
/// The type subchunk records the format in which the envelope is displayed to the user and a type
|
||||
/// code that identifies the components of certain predefined envelope triples. The user format has
|
||||
/// no effect on the actual values, only the way they're presented in LightWave®'s interface.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct EnvelopeType {
|
||||
pub user_format: UserFormat,
|
||||
pub kind: EnvelopeKind,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u8)]
|
||||
#[derive(Debug)]
|
||||
pub enum UserFormat {
|
||||
Float = 2,
|
||||
Distance = 3,
|
||||
Percent = 4,
|
||||
Angle = 5,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u8)]
|
||||
#[derive(Debug)]
|
||||
pub enum EnvelopeKind {
|
||||
PositionX = 0x1,
|
||||
PositionY = 0x2,
|
||||
PositionZ = 0x3,
|
||||
RotHeading = 0x4,
|
||||
RotPitch = 0x5,
|
||||
RotBank = 0x6,
|
||||
ScaleX = 0x7,
|
||||
ScaleY = 0x8,
|
||||
ScaleZ = 0x9,
|
||||
ColorR = 0xa,
|
||||
ColorG = 0xb,
|
||||
ColorB = 0xc,
|
||||
FalloffX = 0xd,
|
||||
FalloffY = 0xe,
|
||||
FalloffZ = 0xf,
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::iff::SubChunk;
|
||||
use crate::lwo2::sub_tags::plugin::PluginServerNameAndData;
|
||||
use crate::lwo2::sub_tags::{EnableState, ValueEnvelope};
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
/// Describes an image or a sequence of images. Surface definitions specify images by referring to
|
||||
/// CLIP chunks. The term "clip" is used to describe these because they can be numbered sequences
|
||||
/// or animations as well as stills. The index identifies this clip uniquely and may be any non-zero
|
||||
/// value less than 0x1000000. The filename and any image processing modifiers follow as a variable
|
||||
/// list of subchunks, which are documented below in the Clip Subchunks section.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ImageClip {
|
||||
pub index: u32,
|
||||
#[br(parse_with = until_size_limit(length as u64 - 4))]
|
||||
pub attributes: Vec<ImageClipSubChunk>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum ImageClipSubChunk {
|
||||
#[br(magic(b"STIL"))]
|
||||
StillImage(SubChunk<StillImage>),
|
||||
#[br(magic(b"ISEQ"))]
|
||||
ImageSequence(SubChunk<ImageSequence>),
|
||||
#[br(magic(b"ANIM"))]
|
||||
PluginAnimation(SubChunk<PluginAnimation>),
|
||||
#[br(magic(b"XREF"))]
|
||||
Reference(SubChunk<Reference>),
|
||||
#[br(magic(b"FLAG"))]
|
||||
Flag(SubChunk<Flags>),
|
||||
#[br(magic(b"STCC"))]
|
||||
ColorCyclingStill(SubChunk<ColorCyclingStill>),
|
||||
#[br(magic(b"TIME"))]
|
||||
Time(SubChunk<Time>),
|
||||
#[br(magic(b"CLRS"))]
|
||||
ColorSpaceRgb(SubChunk<ColorSpace>),
|
||||
#[br(magic(b"CLRA"))]
|
||||
ColorSpaceAlpha(SubChunk<ColorSpace>),
|
||||
#[br(magic(b"FILT"))]
|
||||
ImageFiltering(SubChunk<Flags>),
|
||||
#[br(magic(b"DITH"))]
|
||||
ImageDithering(SubChunk<Flags>),
|
||||
#[br(magic(b"CONT"))]
|
||||
Contrast(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"BRIT"))]
|
||||
Brightness(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"SATR"))]
|
||||
Saturation(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"HUE\0"))]
|
||||
Hue(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"GAMM"))]
|
||||
GammaCorrection(SubChunk<ValueEnvelope>),
|
||||
#[br(magic(b"NEGA"))]
|
||||
Negative(SubChunk<EnableState>),
|
||||
#[br(magic(b"IFLT"))]
|
||||
PluginImageFilters(SubChunk<PluginServerNameAndData>),
|
||||
#[br(magic(b"PFLT"))]
|
||||
PluginPixelFilters(SubChunk<PluginServerNameAndData>),
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PluginAnimation {
|
||||
#[br(temp)]
|
||||
start_pos: PosValue<()>,
|
||||
#[br(align_after = 2)]
|
||||
pub file_name: NullString,
|
||||
#[br(align_after = 2)]
|
||||
pub server_name: NullString,
|
||||
pub flags: u16,
|
||||
#[br(temp)]
|
||||
end_pos: PosValue<()>,
|
||||
#[br(count = end_pos.pos - start_pos.pos)]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Contains the color space of the texture. If the flag is 0, then the color space is contained
|
||||
/// in the following 2 bytes. That color space is defined by the LWCOLORSPACE enum. If the flag
|
||||
/// is set to 1, then the file name of the color space is save as a local string.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ColorSpace {
|
||||
pub flags: u16,
|
||||
pub color_space: u16,
|
||||
#[br(align_after = 2)]
|
||||
pub file_name: NullString,
|
||||
}
|
||||
|
||||
/// A still image with color-cycling is a source defined by a neutral-format name and cycling
|
||||
/// parameters. lo and hi are indexes into the image's color table. Within this range, the color
|
||||
/// table entries are shifted over time to cycle the colors in the image. If lo is less than hi,
|
||||
/// the colors cycle forward, and if hi is less than lo, they go backwards.
|
||||
///
|
||||
/// Except for the TIME subchunk, the subchunks after the source subchunk modify the source image
|
||||
/// and are applied as filters layered on top of the source image.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ColorCyclingStill {
|
||||
pub lo: i16,
|
||||
pub hi: i16,
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
}
|
||||
|
||||
/// Defines source times for an animated clip.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Time {
|
||||
pub start_time: f32,
|
||||
pub duration: f32,
|
||||
pub frame_rate: f32,
|
||||
}
|
||||
|
||||
/// TODO: What's this?
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Flags {
|
||||
pub flag: u32,
|
||||
}
|
||||
|
||||
/// The source is a single still image referenced by a filename in neutral path format.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct StillImage {
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
}
|
||||
|
||||
/// The source is a numbered sequence of still image files. Each filename contains a fixed number
|
||||
/// of decimal digits that specify a frame number, along with a prefix (the part before the frame
|
||||
/// number, which includes the path) and a suffix (the part after the number, typically a PC-style
|
||||
/// extension that identifies the file format). The prefix and suffix are the same for all files
|
||||
/// in the sequence.
|
||||
///
|
||||
/// The flags include bits for looping and interlace. The offset is added to the current frame
|
||||
/// number to obtain the digits of the filename for the current frame. The start and end values
|
||||
/// define the range of frames in the sequence.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ImageSequence {
|
||||
pub num_digits: u8,
|
||||
pub flags: u8,
|
||||
pub offset: i16,
|
||||
pub reserved: u16,
|
||||
pub start: i16,
|
||||
pub end: i16,
|
||||
#[br(align_after = 2)]
|
||||
pub prefix: NullString,
|
||||
#[br(align_after = 2)]
|
||||
pub suffix: NullString,
|
||||
}
|
||||
|
||||
/// The source is a copy, or instance, of another clip, given by the index. The string is a unique
|
||||
/// name for this instance of the clip.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Reference {
|
||||
pub index: u32,
|
||||
#[br(align_after = 2)]
|
||||
pub string: NullString,
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
/// Signals the start of a new layer. All the data chunks which follow will be included in this
|
||||
/// layer until another layer chunk is encountered. If data is encountered before a layer chunk,
|
||||
/// it goes into an arbitrary layer. If the least significant bit of flags is set, the layer is
|
||||
/// hidden. The parent index indicates the default parent for this layer and can be -1 or missing
|
||||
/// to indicate no parent.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct Layer {
|
||||
pub number: u16,
|
||||
pub flags: u16,
|
||||
pub pivot: [f32; 3],
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
pub status: PosValue<()>,
|
||||
#[br(if(status.pos < length as u64))]
|
||||
pub parent: Option<u16>,
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
/// Store an object description. Optional. This should be a simple line of upper and lowercase
|
||||
/// characters, punctuation and spaces which describes the contents of the object file. There
|
||||
/// should be no control characters in this text string and it should generally be kept short.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptionLine {
|
||||
#[br(align_after = 2)]
|
||||
pub description_line: NullString,
|
||||
}
|
||||
|
||||
/// An iconic or thumbnail image for the object which can be used when viewing the file in a
|
||||
/// browser. Currently the only suported encoding is 0, meaning uncompressed RGB byte triples.
|
||||
/// The width is the number of pixels in each row of the image, and the height (number of rows)
|
||||
/// is (chunkSize - 4)/width. This chunk is optional.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct ThumbnailIconImage {
|
||||
pub encoding: ThumbnailImageEncoding,
|
||||
pub width: u16,
|
||||
#[br(calc = (length as u16 - 4) / width)]
|
||||
pub height: u16,
|
||||
#[br(count = length - 4)]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = u16)]
|
||||
#[derive(Debug)]
|
||||
pub enum ThumbnailImageEncoding {
|
||||
UncompressedRgb = 0,
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
use crate::iff::Chunk;
|
||||
use crate::lwo2::tags::bounding_box::BoundingBox;
|
||||
use crate::lwo2::tags::discontinuous_vertex_mapping::DiscontinuousVertexMappings;
|
||||
use crate::lwo2::tags::image_clip::ImageClip;
|
||||
use crate::lwo2::tags::layer::Layer;
|
||||
use crate::lwo2::tags::meta::{DescriptionLine, ThumbnailIconImage};
|
||||
use crate::lwo2::tags::point_list::PointList;
|
||||
use crate::lwo2::tags::polygon_list::PolygonLists;
|
||||
use crate::lwo2::tags::polygon_tag_mapping::PolygonTagMappings;
|
||||
use crate::lwo2::tags::surface_definition::SurfaceDefinition;
|
||||
use crate::lwo2::tags::tag_strings::TagStrings;
|
||||
use crate::lwo2::tags::vertex_map_parameter::VertexMapParameter;
|
||||
use crate::lwo2::tags::vertex_mapping::VertexMappings;
|
||||
use binrw::binread;
|
||||
|
||||
pub mod bounding_box;
|
||||
pub mod discontinuous_vertex_mapping;
|
||||
pub mod envelope;
|
||||
pub mod image_clip;
|
||||
pub mod layer;
|
||||
pub mod meta;
|
||||
pub mod point_list;
|
||||
pub mod polygon_list;
|
||||
pub mod polygon_tag_mapping;
|
||||
pub mod surface_definition;
|
||||
pub mod tag_strings;
|
||||
pub mod vertex_map_parameter;
|
||||
pub mod vertex_mapping;
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum Tag {
|
||||
#[br(magic(b"LAYR"))]
|
||||
Layer(Chunk<Layer>),
|
||||
#[br(magic(b"PNTS"))]
|
||||
PointList(Chunk<PointList>),
|
||||
#[br(magic(b"VMAP"))]
|
||||
VertexMapping(Chunk<VertexMappings>),
|
||||
#[br(magic(b"TAGS"))]
|
||||
TagStrings(Chunk<TagStrings>),
|
||||
#[br(magic(b"PTAG"))]
|
||||
PolygonTagMapping(Chunk<PolygonTagMappings>),
|
||||
#[br(magic(b"VMAD"))]
|
||||
DiscontinuousVertexMapping(Chunk<DiscontinuousVertexMappings>),
|
||||
#[br(magic(b"VMPA"))]
|
||||
VertexMapParameter(Chunk<VertexMapParameter>),
|
||||
#[br(magic(b"BBOX"))]
|
||||
BoundingBox(Chunk<BoundingBox>),
|
||||
#[br(magic(b"DESC"))]
|
||||
DescriptionLine(Chunk<DescriptionLine>),
|
||||
#[br(magic(b"TEXT"))]
|
||||
CommentaryText(Chunk<DescriptionLine>),
|
||||
#[br(magic(b"ICON"))]
|
||||
ThumbnailIconImage(Chunk<ThumbnailIconImage>),
|
||||
#[br(magic(b"POLS"))]
|
||||
PolygonList(Chunk<PolygonLists>),
|
||||
#[br(magic(b"SURF"))]
|
||||
SurfaceDefinition(Chunk<SurfaceDefinition>),
|
||||
#[br(magic(b"CLIP"))]
|
||||
ImageClip(Chunk<ImageClip>),
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use binrw::binread;
|
||||
|
||||
///Lists (x, y, z) coordinate triples for a set of points. The number of points in the chunk is
|
||||
/// just the chunk size divided by 12. The PNTS chunk must precede the POLS, VMAP and VMAD chunks
|
||||
/// that refer to it. These chunks list points using a 0-based index into PNTS.
|
||||
///
|
||||
/// The LightWave® coordinate system is left-handed, with +X to the right or east, +Y upward,
|
||||
/// and +Z forward or north. Object files don't contain explicit units, but by convention the
|
||||
/// unit is meters. Coordinates in PNTS are relative to the pivot point of the layer.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PointList {
|
||||
#[br(count = length / 12, assert(length % 12 == 0))]
|
||||
pub point_location: Vec<[f32; 3]>,
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use crate::binrw_helpers::{count_with_vx, until_size_limit};
|
||||
use binrw::binread;
|
||||
|
||||
/// A list of polygons for the current layer. Possible polygon types include:
|
||||
///
|
||||
/// * **FACE**
|
||||
/// <ul>"Regular" polygons, the most common.</ul>
|
||||
/// * **CURV**
|
||||
/// <ul>Catmull-Rom splines. These are used during modeling and are currently ignored by the
|
||||
/// renderer.</ul>
|
||||
/// * **PTCH**
|
||||
/// <ul>Subdivision patches. The POLS chunk contains the definition of the control cage
|
||||
/// polygons, and the patch is created by subdividing these polygons. The renderable geometry
|
||||
/// that results from subdivision is determined interactively by the user through settings
|
||||
/// within LightWave®. The subdivision method is undocumented.</ul>
|
||||
/// * **MBAL**
|
||||
/// <ul>Metaballs. These are single-point polygons. The points are associated with a VMAP of
|
||||
/// type MBAL that contains the radius of influence of each metaball. The renderable polygonal
|
||||
/// surface constructed from a set of metaballs is inferred as an isosurface on a scalar field
|
||||
/// derived from the sum of the influences of all of the metaball points.</ul>
|
||||
/// * **BONE**
|
||||
/// <ul>Line segments representing the object's skeleton. These are converted to bones for
|
||||
/// deformation during rendering.</ul>
|
||||
///
|
||||
/// Each polygon is defined by a vertex count followed by a list of indexes into the most recent
|
||||
/// PNTS chunk. The maximum number of vertices is 1023. The 6 high-order bits of the vertex count
|
||||
/// are flag bits with different meanings for each polygon type. When reading POLS, remember to mask
|
||||
/// out the flags to obtain numverts. (For CURV polygon: The two low order flags are for continuity
|
||||
/// control point toggles. The four remaining high order flag bits are additional vertex count bits;
|
||||
/// this brings the maximum number of vertices for CURV polygons to 2^14 = 16383.)
|
||||
///
|
||||
/// When writing POLS, the vertex list for each polygon should begin at a convex vertex and proceed
|
||||
/// clockwise as seen from the visible side of the polygon. LightWave® polygons are single-sided
|
||||
/// (although double-sidedness is a possible surface property), and the normal is defined as the
|
||||
/// cross product of the first and last edges.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PolygonLists {
|
||||
pub kind: [u8; 4],
|
||||
#[br(parse_with = until_size_limit(length as u64 - 4))]
|
||||
pub polygons: Vec<PolygonList>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub struct PolygonList {
|
||||
#[br(temp)]
|
||||
pub numvert_and_flags: u16,
|
||||
#[br(calc = (numvert_and_flags >> 10) as u8)]
|
||||
pub flags: u8,
|
||||
#[br(parse_with = count_with_vx((numvert_and_flags & 0x3ff) as usize))]
|
||||
pub vert: Vec<u32>,
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::binread;
|
||||
|
||||
/// Associates tags of a given type with polygons in the most recent POLS chunk. The most common
|
||||
/// polygon tag types are
|
||||
///
|
||||
/// * **SURF**:
|
||||
/// <ul>The surface assigned to the polygon. The actual surface attributes are found by matching
|
||||
/// the name in the TAGS chunk with the name in a SURF chunk.</ul>
|
||||
/// * **PART**:
|
||||
/// <ul>The part the polygon belongs to. Parts are named groups of polygons analogous to point
|
||||
/// selection sets (but a polygon can belong to only one part).</ul>
|
||||
/// * **SMGP**
|
||||
/// <ul>The smoothing group the polygon belongs to. Shading is only interpolated within a
|
||||
/// smoothing group, not across groups.</ul>
|
||||
///
|
||||
/// The polygon is identified by an index into the previous POLS chunk, and the tag is given by an
|
||||
/// index into the previous TAGS chunk. Not all polygons will have a value for every tag type. The
|
||||
/// behavior for polygons lacking a given tag depends on the type.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct PolygonTagMappings {
|
||||
pub kind: [u8; 4],
|
||||
#[br(parse_with = until_size_limit(length as u64 - 4))]
|
||||
pub mappings: Vec<PolygonTagMapping>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub struct PolygonTagMapping {
|
||||
#[br(parse_with = vx)]
|
||||
pub poly: u32,
|
||||
pub tag: u16,
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::lwo2::sub_tags::surface_parameters::SurfaceParameterSubChunk;
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct SurfaceDefinition {
|
||||
#[br(temp)]
|
||||
pub start_pos: PosValue<()>,
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
#[br(align_after = 2)]
|
||||
pub source: NullString,
|
||||
#[br(temp)]
|
||||
pub end_pos: PosValue<()>,
|
||||
#[br(parse_with = until_size_limit(length as u64 - (end_pos.pos - start_pos.pos)))]
|
||||
pub attributes: Vec<SurfaceParameterSubChunk>,
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use binrw::{binread, NullString};
|
||||
|
||||
/// Lists the tag strings that can be associated with polygons by the PTAG chunk.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct TagStrings {
|
||||
#[br(parse_with = until_size_limit(length as u64))]
|
||||
pub tag_strings: Vec<NullString>,
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use binrw::binread;
|
||||
|
||||
/// Describes special properties of VMAPs.
|
||||
#[binread]
|
||||
#[br(import(_length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct VertexMapParameter {
|
||||
pub uv_subdivision_type: UvSubdivisionType,
|
||||
pub sketch_color: i32,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(repr = i32)]
|
||||
#[derive(Debug)]
|
||||
pub enum UvSubdivisionType {
|
||||
Linear = 0,
|
||||
Subpatch = 1,
|
||||
SubpatchLinearCorners = 2,
|
||||
SubpatchLinearEdges = 3,
|
||||
SubpatchDiscoEdges = 4,
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
use crate::binrw_helpers::until_size_limit;
|
||||
use crate::lwo2::vx;
|
||||
use binrw::{binread, NullString, PosValue};
|
||||
|
||||
/// Associates a set of floating-point vectors with a set of points. VMAPs begin with a type,
|
||||
/// a dimension (vector length) and a name. These are followed by a list of vertex/vector pairs.
|
||||
/// The vertex is given as an index into the most recent PNTS chunk, in VX format. The vector
|
||||
/// contains dimension floating-point values. There can be any number of these chunks, but they
|
||||
/// should all have different types or names.
|
||||
///
|
||||
/// Some common type codes are
|
||||
///
|
||||
/// * **PICK**
|
||||
/// <ul>Selection set. This is a VMAP of dimension 0 that marks points for quick selection by
|
||||
/// name during modeling. It has no effect on the geometry of the object.</ul>
|
||||
/// * **WGHT**
|
||||
/// <ul>Weight maps have a dimension of 1 and are generally used to alter the influence of
|
||||
/// deformers such as bones. Weights can be positive or negative, and the default weight for
|
||||
/// unmapped vertices is 0.0.</ul>
|
||||
/// * **MNVW**
|
||||
/// <ul>Subpatch weight maps affect the shape of geometry created by subdivision patching.</ul>
|
||||
/// * **TXUV**
|
||||
/// <ul>UV texture maps have a dimension of 2.</ul>
|
||||
/// * **RGB**, **RGBA**
|
||||
/// <ul>Color maps, with a dimension of 3 or 4.</ul>
|
||||
/// * **MORF**
|
||||
/// <ul>These contain vertex displacement deltas.</ul>
|
||||
/// * **SPOT**
|
||||
/// <ul>These contain absolute vertex displacements (alternative vertex positions).</ul>
|
||||
///
|
||||
/// Other widely used map types will almost certainly appear in the future.
|
||||
#[binread]
|
||||
#[br(import(length: u32))]
|
||||
#[derive(Debug)]
|
||||
pub struct VertexMappings {
|
||||
#[br(temp)]
|
||||
pub begin_pos: PosValue<()>,
|
||||
pub kind: [u8; 4],
|
||||
#[br(temp)]
|
||||
pub dimension: u16,
|
||||
#[br(align_after = 2)]
|
||||
pub name: NullString,
|
||||
#[br(temp)]
|
||||
pub end_pos: PosValue<()>,
|
||||
#[br(parse_with = |reader, endian, _: ()| until_size_limit(length as u64 - (end_pos.pos - begin_pos.pos))(reader, endian, (dimension,)))]
|
||||
pub mapping: Vec<VertexMapping>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(import(dimension: u16))]
|
||||
#[derive(Debug)]
|
||||
pub struct VertexMapping {
|
||||
#[br(parse_with = vx)]
|
||||
pub vert: u32,
|
||||
#[br(count = dimension)]
|
||||
pub value: Vec<f32>,
|
||||
}
|
||||
@@ -7,5 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
starforcelib = {path = "../starforcelib"}
|
||||
lightwave = {path = "../lightwave"}
|
||||
lightwave-3d = "1.0.0"
|
||||
glob = "0.3.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use glob::glob;
|
||||
use lightwave::LightWaveObject;
|
||||
use lightwave_3d::LightWaveObject;
|
||||
|
||||
fn main() {
|
||||
let mut successful = 0;
|
||||
|
||||
68
rust/mhgd/src/lightwave_object.rs
Normal file
68
rust/mhgd/src/lightwave_object.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use godot::bind::godot_api;
|
||||
use godot::builtin::{GodotString, PackedInt32Array, PackedVector3Array, Vector3};
|
||||
use godot::engine::mesh::ArrayType;
|
||||
use godot::engine::{ArrayMesh, PackedScene};
|
||||
use godot::obj::{EngineEnum, Gd};
|
||||
use godot::prelude::{Array, GodotClass, ToVariant};
|
||||
use lightwave_3d::lwo2::tags::Tag;
|
||||
use lightwave_3d::LightWaveObject;
|
||||
use std::fs::File;
|
||||
|
||||
#[derive(GodotClass)]
|
||||
struct Lwo {}
|
||||
|
||||
#[godot_api]
|
||||
impl Lwo {
|
||||
pub fn get_mesh(path: GodotString) -> Gd<ArrayMesh> {
|
||||
lightwave_to_gd(LightWaveObject::read_file(path.to_string()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
|
||||
let mesh = ArrayMesh::new();
|
||||
let mut arrays = Array::new();
|
||||
arrays.resize(ArrayType::ARRAY_MAX.ord() as usize);
|
||||
|
||||
for tag in lightwave.data {
|
||||
match tag {
|
||||
Tag::PointList(points) => {
|
||||
arrays.set(
|
||||
ArrayType::ARRAY_VERTEX.ord() as usize,
|
||||
PackedVector3Array::from(
|
||||
points
|
||||
.point_location
|
||||
.iter()
|
||||
.map(|[x, y, z]| Vector3 {
|
||||
x: *x,
|
||||
y: *y,
|
||||
z: *z,
|
||||
})
|
||||
.collect::<Vec<Vector3>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.to_variant(),
|
||||
);
|
||||
}
|
||||
Tag::PolygonList(polygons) => match &polygons.kind {
|
||||
b"FACE" => {
|
||||
arrays.set(
|
||||
ArrayType::ARRAY_INDEX.ord() as usize,
|
||||
PackedInt32Array::from(
|
||||
polygons
|
||||
.polygons
|
||||
.iter()
|
||||
.flat_map(|it| it.vert.iter().map(|it| *it as i32))
|
||||
.collect::<Vec<i32>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.to_variant(),
|
||||
);
|
||||
}
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
mesh
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
use crate::formats;
|
||||
use crate::formats::datafile::{Datafile, FileEntry};
|
||||
use crate::formats::{load_data, DatafileFile};
|
||||
use crate::godot::font::load_bitmap_font;
|
||||
use crate::godot::game_object::parse_game_object;
|
||||
use crate::godot::image::{load_bmp_as_image_texture, load_rle_as_sprite_frames};
|
||||
use crate::godot::sprites::load_sprite_frames;
|
||||
use crate::godot::tile_map::{create_tile_map, TileCollision};
|
||||
use crate::godot::ui::convert_ui;
|
||||
use binrw::BinRead;
|
||||
use crate::sproing::game_object::parse_game_object;
|
||||
use crate::sproing::image::{load_bmp_as_image_texture, load_rle_as_sprite_frames};
|
||||
use crate::sproing::sprites::load_sprite_frames;
|
||||
use crate::sproing::tile_map::{create_tile_map, TileCollision};
|
||||
use crate::sproing::ui::convert_ui;
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::resource_loader::CacheMode;
|
||||
use godot::engine::resource_saver::SaverFlags;
|
||||
@@ -18,7 +13,8 @@ use godot::engine::{ResourceFormatLoader, ResourceSaver};
|
||||
use godot::engine::{ResourceFormatLoaderVirtual, ResourceLoader};
|
||||
use godot::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use springylib::archive::Archive;
|
||||
use springylib::DatafileFile;
|
||||
use std::fs::File;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -27,7 +23,7 @@ const DAT_PATH: &str = "E:\\Games\\Schatzjäger\\data\\datafile.dat";
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=ResourceFormatLoader)]
|
||||
pub struct DatafileLoader {
|
||||
pub datafile_table: HashMap<String, FileEntry>,
|
||||
pub datafile_table: Archive,
|
||||
|
||||
#[base]
|
||||
pub base: Base<ResourceFormatLoader>,
|
||||
@@ -86,7 +82,7 @@ impl DatafileLoader {
|
||||
impl ResourceFormatLoaderVirtual for DatafileLoader {
|
||||
fn init(base: Base<Self::Base>) -> Self {
|
||||
let mut file = File::open(DAT_PATH).unwrap();
|
||||
let datafile_table = Datafile::read(&mut file).unwrap().into_index();
|
||||
let datafile_table = Archive::read(&mut file).unwrap();
|
||||
|
||||
DatafileLoader {
|
||||
base,
|
||||
@@ -151,7 +147,7 @@ impl ResourceFormatLoaderVirtual for DatafileLoader {
|
||||
|
||||
if let Some(target) = self.datafile_table.get(datafile_path.as_str()) {
|
||||
let mut file = File::open(DAT_PATH).unwrap();
|
||||
match load_data(target, &mut file) {
|
||||
match target.load_from(&mut file) {
|
||||
Ok(DatafileFile::Level(level)) => {
|
||||
let level_id = datafile_path
|
||||
.split_terminator('\\')
|
||||
@@ -229,13 +225,14 @@ impl ResourceFormatLoaderVirtual for DatafileLoader {
|
||||
};
|
||||
|
||||
if datafile_path.contains("\\fonts\\") {
|
||||
let font = load_bitmap_font(gd_image);
|
||||
panic!();
|
||||
/*let font = load_bitmap_font(gd_image);
|
||||
|
||||
self.save_to_cache(
|
||||
font.share().upcast(),
|
||||
format!("{}.tres", datafile_path),
|
||||
);
|
||||
font.to_variant()
|
||||
font.to_variant()*/
|
||||
} else {
|
||||
let mut texture = ImageTexture::new();
|
||||
texture.set_image(gd_image);
|
||||
@@ -263,16 +260,22 @@ impl ResourceFormatLoaderVirtual for DatafileLoader {
|
||||
);*/
|
||||
tile_collision.to_variant()
|
||||
}
|
||||
Err(formats::Error::UnknownFormat(ext)) => {
|
||||
Err(springylib::error::Error::UnknownFormat(ext)) => {
|
||||
printerr(format!("Unknown format <{}>", ext).to_variant(), &[]);
|
||||
Error::ERR_FILE_UNRECOGNIZED.to_variant()
|
||||
}
|
||||
Err(formats::Error::Deserialization) => {
|
||||
printerr("Failed to deserialize".to_variant(), &[]);
|
||||
Err(springylib::error::Error::InvalidData { info, context }) => {
|
||||
printerr(
|
||||
"Failed to deserialize".to_variant(),
|
||||
&[
|
||||
info.unwrap_or("".to_string()).to_variant(),
|
||||
context.to_variant(),
|
||||
],
|
||||
);
|
||||
Error::ERR_FILE_CORRUPT.to_variant()
|
||||
}
|
||||
Err(formats::Error::Custom(message)) => {
|
||||
printerr(message.to_variant(), &[]);
|
||||
Err(springylib::error::Error::Custom(message)) => {
|
||||
printerr(message.to_string().to_variant(), &[]);
|
||||
Error::ERR_BUG.to_variant()
|
||||
}
|
||||
_ => {
|
||||
@@ -1,11 +1,8 @@
|
||||
use encoding_rs::WINDOWS_1252;
|
||||
use godot::builtin::{Rect2, Vector2, Vector2i};
|
||||
use godot::engine::{FontFile, Image};
|
||||
use godot::prelude::utilities::prints;
|
||||
use godot::prelude::{Gd, Share, ToVariant};
|
||||
|
||||
const CHARSET: &[u8] = include_bytes!("charset.txt");
|
||||
|
||||
pub fn load_bitmap_font(image: Gd<Image>) -> Gd<FontFile> {
|
||||
let mut font_chars = CHARSET.iter();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::formats::rle::RleImage;
|
||||
use godot::builtin::{Color, PackedByteArray};
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::image::Format;
|
||||
use godot::engine::{Image, ImageTexture, SpriteFrames};
|
||||
use godot::obj::Gd;
|
||||
use springylib::media::rle::RleImage;
|
||||
|
||||
const FPS: f64 = 15.0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod datafile;
|
||||
pub mod font;
|
||||
// pub mod font;
|
||||
pub mod game_object;
|
||||
pub mod image;
|
||||
pub mod sprites;
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::formats::sprites::{CropMode, RenderMode, Sprites};
|
||||
use godot::builtin::{GodotString, Rect2, StringName, ToVariant, Vector2};
|
||||
use godot::engine::utilities::printerr;
|
||||
use godot::engine::{
|
||||
@@ -6,6 +5,7 @@ use godot::engine::{
|
||||
};
|
||||
use godot::obj::{Gd, Share};
|
||||
use godot::prelude::GodotClass;
|
||||
use springylib::media::sprites::{CropMode, RenderMode, Sprites};
|
||||
|
||||
const FPS: f64 = 15.0;
|
||||
const SPRITE_EXTENSIONS: &[&str] = &["bmp", "rle"];
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::formats::level::LevelLayer;
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::utilities::{clampi, printerr};
|
||||
use godot::engine::{load, PackedScene};
|
||||
@@ -6,6 +5,7 @@ use godot::engine::{ImageTexture, TileSet};
|
||||
use godot::engine::{TileMap, TileSetAtlasSource};
|
||||
use godot::prelude::*;
|
||||
use godot::prelude::{Gd, PackedByteArray, Share, ToVariant};
|
||||
use springylib::media::level::LevelLayer;
|
||||
|
||||
pub fn create_tile_map(layer: LevelLayer, level_id: u32) -> Gd<PackedScene> {
|
||||
let mut tile_set = TileSet::new();
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::formats::ui_xml::{HorizontalAlign, UiTag};
|
||||
use godot::builtin::{Array, Dictionary, GodotString, ToVariant, Vector2};
|
||||
use godot::engine::control::LayoutPreset;
|
||||
use godot::engine::global::HorizontalAlignment;
|
||||
@@ -6,6 +5,7 @@ use godot::engine::node::InternalMode;
|
||||
use godot::engine::{load, Button, Control, Label, LineEdit, Node, SpinBox, TextureRect};
|
||||
use godot::obj::{Gd, Inherits, Share};
|
||||
use itertools::Itertools;
|
||||
use springylib::media::ui::{HorizontalAlign, UiTag};
|
||||
|
||||
const ACTION_META_NAME: &str = "action";
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn convert_ui(ui: UiTag, base_path: &str) -> Gd<Node> {
|
||||
let mut label = Label::new_alloc();
|
||||
label.set_anchors_preset(LayoutPreset::PRESET_TOP_WIDE, false);
|
||||
label.set_position(to_vec2(text.position), false);
|
||||
label.set_horizontal_alignment(text.horizontal_align.into());
|
||||
label.set_horizontal_alignment(to_h_alignment(text.horizontal_align));
|
||||
label.set_text(text.text.into());
|
||||
label.upcast()
|
||||
}
|
||||
@@ -49,7 +49,7 @@ pub fn convert_ui(ui: UiTag, base_path: &str) -> Gd<Node> {
|
||||
text_field.set_name(name.into());
|
||||
}
|
||||
text_field.set_text(field.text.into());
|
||||
text_field.set_horizontal_alignment(field.horizontal_align.into());
|
||||
text_field.set_horizontal_alignment(to_h_alignment(field.horizontal_align));
|
||||
text_field.set_position(to_vec2([field.area[0], field.area[1]]), false);
|
||||
text_field.set_size(to_vec2([field.area[2], field.area[3]]), false);
|
||||
text_field.set_meta("buffer_var".into(), field.buffer_var.to_variant());
|
||||
@@ -76,7 +76,7 @@ pub fn convert_ui(ui: UiTag, base_path: &str) -> Gd<Node> {
|
||||
gd_button.set_anchors_preset(LayoutPreset::PRESET_TOP_WIDE, false);
|
||||
gd_button.set_flat(true);
|
||||
gd_button.set_position(to_vec2(button.position), false);
|
||||
gd_button.set_text_alignment(button.horizontal_align.into());
|
||||
gd_button.set_text_alignment(to_h_alignment(button.horizontal_align));
|
||||
if let Some(name) = button.name {
|
||||
gd_button.set_name(GodotString::from(name));
|
||||
}
|
||||
@@ -87,13 +87,11 @@ pub fn convert_ui(ui: UiTag, base_path: &str) -> Gd<Node> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<HorizontalAlignment> for HorizontalAlign {
|
||||
fn into(self) -> HorizontalAlignment {
|
||||
match self {
|
||||
HorizontalAlign::Center => HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER,
|
||||
HorizontalAlign::Left => HorizontalAlignment::HORIZONTAL_ALIGNMENT_LEFT,
|
||||
HorizontalAlign::Right => HorizontalAlignment::HORIZONTAL_ALIGNMENT_RIGHT,
|
||||
}
|
||||
fn to_h_alignment(align: HorizontalAlign) -> HorizontalAlignment {
|
||||
match align {
|
||||
HorizontalAlign::Center => HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER,
|
||||
HorizontalAlign::Left => HorizontalAlignment::HORIZONTAL_ALIGNMENT_LEFT,
|
||||
HorizontalAlign::Right => HorizontalAlignment::HORIZONTAL_ALIGNMENT_RIGHT,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ impl Display for Error {
|
||||
Error::InvalidData { info, context } => write!(
|
||||
f,
|
||||
"Invalid data: {}; {}",
|
||||
info.unwrap_or("[no info]".to_string()),
|
||||
info.clone().unwrap_or("[no info]".to_string()),
|
||||
context
|
||||
),
|
||||
Error::Custom(error) => write!(f, "{}", error),
|
||||
|
||||
@@ -55,12 +55,12 @@ impl Default for FadeMode {
|
||||
|
||||
impl UiTag {
|
||||
pub fn post_process(mut self) -> Self {
|
||||
if let UiTag::Menu(mut menu) = &self {
|
||||
if let UiTag::Menu(menu) = &mut self {
|
||||
let children: Vec<UiTag> = menu.children.drain(..).collect();
|
||||
let mut area_stack: Vec<Vec<UiTag>> = vec![vec![]];
|
||||
|
||||
for mut child in children {
|
||||
child.post_process();
|
||||
for child in children {
|
||||
let child = child.post_process();
|
||||
if let UiTag::TextArea(mut area) = child {
|
||||
let children = area_stack.pop().unwrap();
|
||||
let opening_tag = area_stack.last_mut().map(|it| it.last_mut());
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
use crate::formats::datafile::FileEntry;
|
||||
use crate::formats::level::LevelLayer;
|
||||
use crate::formats::rle::RleImage;
|
||||
use crate::formats::sprites::Sprites;
|
||||
use crate::formats::txt::{decrypt_exposed_txt, decrypt_txt, DecryptError};
|
||||
use crate::formats::ui_xml::UiTag;
|
||||
use binrw::BinRead;
|
||||
use encoding_rs::WINDOWS_1252;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
pub mod datafile;
|
||||
pub mod level;
|
||||
pub mod rle;
|
||||
pub mod sprites;
|
||||
pub mod txt;
|
||||
pub mod ui_xml;
|
||||
|
||||
pub enum DatafileFile {
|
||||
Txt(String),
|
||||
Level(LevelLayer),
|
||||
Sprites(Vec<Sprites>),
|
||||
RleSprite(Box<RleImage>),
|
||||
Bitmap(Vec<u8>),
|
||||
Vorbis(Vec<u8>),
|
||||
TileCollision(String),
|
||||
Ui(UiTag),
|
||||
Translations(HashMap<String, Vec<String>>),
|
||||
}
|
||||
|
||||
pub enum Error {
|
||||
Deserialization,
|
||||
UnknownFormat(String),
|
||||
UnknownError,
|
||||
Custom(String),
|
||||
DecryptError(DecryptError),
|
||||
}
|
||||
|
||||
fn custom_err<T>(e: T) -> Error
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
Error::Custom(format!("{:#?}", e))
|
||||
}
|
||||
|
||||
pub fn load_data<R>(entry: &FileEntry, reader: &mut R) -> Result<DatafileFile, Error>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
reader
|
||||
.seek(SeekFrom::Start(entry.pos as u64))
|
||||
.map_err(custom_err)?;
|
||||
let mut data = vec![0u8; entry.len as usize];
|
||||
reader.read_exact(&mut data).map_err(custom_err)?;
|
||||
|
||||
let name = entry.name.to_string();
|
||||
let path = Path::new(&name);
|
||||
|
||||
match path
|
||||
.extension()
|
||||
.and_then(OsStr::to_str)
|
||||
.ok_or(Error::Custom("No extension".to_string()))?
|
||||
{
|
||||
"dat" => Ok(DatafileFile::Level(
|
||||
LevelLayer::read(&mut Cursor::new(data)).map_err(custom_err)?,
|
||||
)),
|
||||
"rle" => Ok(DatafileFile::RleSprite(Box::new(
|
||||
RleImage::read(&mut Cursor::new(data)).map_err(custom_err)?,
|
||||
))),
|
||||
"bmp" => Ok(DatafileFile::Bitmap(data)),
|
||||
"ogg" => Ok(DatafileFile::Vorbis(data)),
|
||||
"xml" => {
|
||||
serde_xml_rs::from_str::<UiTag>(String::from_utf8(data).map_err(custom_err)?.as_str())
|
||||
.map_err(custom_err)
|
||||
.map(|mut it| {
|
||||
it.post_process();
|
||||
DatafileFile::Ui(it)
|
||||
})
|
||||
}
|
||||
"txt" => {
|
||||
let stem = path
|
||||
.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.ok_or(Error::Custom("Stem".to_string()))?;
|
||||
let decr = decrypt_txt(data.into_iter()).map_err(Error::DecryptError)?;
|
||||
if stem.starts_with("tile_collision") {
|
||||
Ok(DatafileFile::TileCollision(decr))
|
||||
} else if stem == "sprites" {
|
||||
Ok(DatafileFile::Sprites(
|
||||
Sprites::parse(decr.as_str()).map_err(custom_err)?,
|
||||
))
|
||||
} else if stem.starts_with("profile") || stem.starts_with("highscores") {
|
||||
Ok(DatafileFile::Txt(
|
||||
decrypt_exposed_txt(decr).map_err(custom_err)?,
|
||||
))
|
||||
} else {
|
||||
Ok(DatafileFile::Txt(decr))
|
||||
}
|
||||
}
|
||||
"csv" => Ok(DatafileFile::Translations(
|
||||
WINDOWS_1252
|
||||
.decode(data.as_slice())
|
||||
.0
|
||||
.split('\n')
|
||||
.map(|l| l.trim())
|
||||
.filter(|l| !l.is_empty())
|
||||
.map(|l| {
|
||||
l.splitn(2, ';')
|
||||
.map(|s| s.to_string())
|
||||
.collect_tuple::<(String, String)>()
|
||||
.expect("Invalid csv")
|
||||
})
|
||||
.into_group_map(),
|
||||
)),
|
||||
ext => Err(Error::UnknownFormat(ext.to_string())),
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub enum UiTag {
|
||||
Menu(UiMenu),
|
||||
Image(UiImage),
|
||||
TextButton(UiTextButton),
|
||||
TextArea(UiTextArea),
|
||||
TextField(UiTextField),
|
||||
StaticText(UiStaticText),
|
||||
ToggleButton(UiToggleButton),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiMenu {
|
||||
pub selected: String,
|
||||
#[serde(rename = "OnBack")]
|
||||
pub on_back: Option<String>,
|
||||
#[serde(rename = "$value", default)]
|
||||
pub children: Vec<UiTag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiTextField {
|
||||
pub name: Option<String>,
|
||||
pub text: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(rename = "bufferVar")]
|
||||
pub buffer_var: String,
|
||||
#[serde(deserialize_with = "deserialize_vec4")]
|
||||
pub area: [i32; 4],
|
||||
#[serde(rename = "halign", default)]
|
||||
pub horizontal_align: HorizontalAlign,
|
||||
#[serde(rename = "fademode")]
|
||||
pub fade_mode: FadeMode,
|
||||
#[serde(rename = "OnSelect")]
|
||||
pub on_select: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiImage {
|
||||
pub texture: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub size: [i32; 2],
|
||||
#[serde(rename = "fademode", default)]
|
||||
pub fade_mode: FadeMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiTextButton {
|
||||
pub name: Option<String>,
|
||||
pub text: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(rename = "halign", default)]
|
||||
pub horizontal_align: HorizontalAlign,
|
||||
#[serde(rename = "fademode", default)]
|
||||
pub fade_mode: FadeMode,
|
||||
#[serde(rename = "OnSelect")]
|
||||
pub on_select: String,
|
||||
}
|
||||
|
||||
/// This is a really weird node, sometimes it has children and sometimes, don't ask me why,
|
||||
/// it appears as a normal tag and then gets closed by an empty tag of this kind.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiTextArea {
|
||||
#[serde(deserialize_with = "deserialize_vec2_opt", default)]
|
||||
pub position: Option<[i32; 2]>,
|
||||
#[serde(deserialize_with = "deserialize_vec2_opt", default)]
|
||||
pub size: Option<[i32; 2]>,
|
||||
#[serde(rename = "$value", default)]
|
||||
pub children: Vec<UiTag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiStaticText {
|
||||
pub text: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(rename = "halign", default)]
|
||||
pub horizontal_align: HorizontalAlign,
|
||||
#[serde(rename = "fademode", default)]
|
||||
pub fade_mode: FadeMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UiToggleButton {
|
||||
pub name: Option<String>,
|
||||
pub text: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
pub value: String,
|
||||
#[serde(rename = "minValue")]
|
||||
pub min_value: i32,
|
||||
#[serde(rename = "maxValue")]
|
||||
pub max_value: i32,
|
||||
#[serde(rename = "valueStep")]
|
||||
pub value_step: i32,
|
||||
pub target: String,
|
||||
#[serde(rename = "targetLOffset", deserialize_with = "deserialize_vec2")]
|
||||
pub target_l_offset: [i32; 2],
|
||||
#[serde(rename = "targetROffset", deserialize_with = "deserialize_vec2")]
|
||||
pub target_r_offset: [i32; 2],
|
||||
#[serde(rename = "noSound", default)]
|
||||
pub no_sound: bool,
|
||||
#[serde(rename = "OnChange")]
|
||||
pub on_change: String,
|
||||
#[serde(rename = "OnSelect")]
|
||||
pub on_select: String,
|
||||
}
|
||||
|
||||
impl UiTag {
|
||||
pub fn post_process(&mut self) {
|
||||
if let UiTag::Menu(menu) = self {
|
||||
let children: Vec<UiTag> = menu.children.drain(..).collect();
|
||||
let mut area_stack: Vec<Vec<UiTag>> = vec![vec![]];
|
||||
|
||||
for mut child in children {
|
||||
child.post_process();
|
||||
if let UiTag::TextArea(mut area) = child {
|
||||
let children = area_stack.pop().unwrap();
|
||||
let opening_tag = area_stack.last_mut().map(|it| it.last_mut());
|
||||
|
||||
if let Some(Some(UiTag::TextArea(opening_tag))) = opening_tag {
|
||||
opening_tag.children = children;
|
||||
} else {
|
||||
area_stack.push(children);
|
||||
}
|
||||
|
||||
if area.position.is_some() && area.size.is_some() {
|
||||
let children = area.children.drain(..).collect();
|
||||
area_stack.last_mut().unwrap().push(UiTag::TextArea(area));
|
||||
area_stack.push(children);
|
||||
}
|
||||
} else {
|
||||
area_stack.last_mut().unwrap().push(child);
|
||||
}
|
||||
}
|
||||
|
||||
menu.children = area_stack.pop().unwrap();
|
||||
debug_assert!(area_stack.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum HorizontalAlign {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Default for HorizontalAlign {
|
||||
fn default() -> HorizontalAlign {
|
||||
HorizontalAlign::Left
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FadeMode {
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for FadeMode {
|
||||
fn default() -> Self {
|
||||
FadeMode::None
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_vec2_opt<'de, D>(deserializer: D) -> Result<Option<[i32; 2]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
if let Some(buf) = Option::<String>::deserialize(deserializer)? {
|
||||
to_vec2::<D>(buf).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_vec2<'de, D>(deserializer: D) -> Result<[i32; 2], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
to_vec2::<D>(String::deserialize(deserializer)?)
|
||||
}
|
||||
|
||||
fn deserialize_vec4<'de, D>(deserializer: D) -> Result<[i32; 4], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
to_vec4::<D>(String::deserialize(deserializer)?)
|
||||
}
|
||||
|
||||
fn to_vec<'de, D>(buf: String) -> Result<Vec<i32>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
buf.split(',')
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
// there's some typos so we have to cover that...
|
||||
value.split_ascii_whitespace().collect::<Vec<&str>>()[0]
|
||||
.trim()
|
||||
.parse::<i32>()
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn to_vec4<'de, D>(buf: String) -> Result<[i32; 4], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut values = to_vec::<D>(buf)?;
|
||||
let w = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
let z = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
let y = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
let x = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
|
||||
Ok([x, y, z, w])
|
||||
}
|
||||
|
||||
fn to_vec2<'de, D>(buf: String) -> Result<[i32; 2], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut values = to_vec::<D>(buf)?;
|
||||
let y = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
let x = values.pop().ok_or(Error::custom("InvalidField"))?;
|
||||
|
||||
Ok([x, y])
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
use crate::godot::datafile::DatafileLoader;
|
||||
use ::godot::engine::class_macros::auto_register_classes;
|
||||
use ::godot::engine::{ResourceFormatLoaderVirtual, ResourceLoader};
|
||||
use ::godot::init::{gdextension, ExtensionLayer};
|
||||
use ::godot::prelude::{ExtensionLibrary, Gd, InitHandle, InitLevel, Share};
|
||||
|
||||
pub mod formats;
|
||||
pub mod godot;
|
||||
|
||||
struct Main {}
|
||||
|
||||
#[gdextension]
|
||||
unsafe impl ExtensionLibrary for Main {
|
||||
fn load_library(handle: &mut InitHandle) -> bool {
|
||||
handle.register_layer(InitLevel::Editor, ResourceLoaderLayer { datafile: None });
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct ResourceLoaderLayer {
|
||||
pub datafile: Option<Gd<DatafileLoader>>,
|
||||
}
|
||||
|
||||
impl ExtensionLayer for ResourceLoaderLayer {
|
||||
fn initialize(&mut self) {
|
||||
auto_register_classes();
|
||||
|
||||
self.datafile = Some(Gd::<DatafileLoader>::with_base(DatafileLoader::init));
|
||||
|
||||
ResourceLoader::singleton()
|
||||
.add_resource_format_loader(self.datafile.as_ref().unwrap().share().upcast(), true);
|
||||
}
|
||||
|
||||
fn deinitialize(&mut self) {
|
||||
if let Some(datafile) = &self.datafile {
|
||||
ResourceLoader::singleton().remove_resource_format_loader(datafile.share().upcast());
|
||||
self.datafile = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
135
rust/src/main.rs
135
rust/src/main.rs
@@ -1,135 +0,0 @@
|
||||
use binrw::{BinRead, NullString};
|
||||
use image::codecs::gif::{GifEncoder, Repeat};
|
||||
use image::{AnimationDecoder, ImageFormat};
|
||||
use mhjnr::formats::datafile::Datafile;
|
||||
use mhjnr::formats::level::level_tile_data_to_image;
|
||||
use mhjnr::formats::rle::RleImage;
|
||||
use mhjnr::formats::sprites::Sprites;
|
||||
use mhjnr::formats::txt::{decrypt_exposed_txt, decrypt_txt};
|
||||
use mhjnr::formats::ui_xml::UiTag;
|
||||
use serde_xml_rs::from_str;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::path::Path;
|
||||
|
||||
fn extract(datafile: &Datafile, file: &mut File) {
|
||||
let target = "E:\\Games\\Schatzjäger\\data3";
|
||||
|
||||
for entry in &datafile.files {
|
||||
let file_name = format!("{}\\{}", target, entry.name);
|
||||
fs::create_dir_all(file_name.rsplit_once('\\').unwrap().0).unwrap();
|
||||
|
||||
file.seek(SeekFrom::Start(entry.pos as u64)).unwrap();
|
||||
let mut data = vec![0u8; entry.len as usize];
|
||||
file.read_exact(&mut data).unwrap();
|
||||
|
||||
if entry.name.to_string().ends_with(".txt") {
|
||||
let mut contents = decrypt_txt(data.into_iter()).unwrap();
|
||||
/*if entry
|
||||
.name
|
||||
.to_string()
|
||||
.split('\\')
|
||||
.collect::<Vec<&str>>()
|
||||
.len()
|
||||
== 1
|
||||
{
|
||||
contents = decrypt_exposed_txt(contents).unwrap();
|
||||
}*/
|
||||
File::create(file_name)
|
||||
.unwrap()
|
||||
.write_all(contents.as_bytes())
|
||||
.unwrap();
|
||||
} else if entry.name.to_string().ends_with(".rle") {
|
||||
let image: RleImage = RleImage::read(&mut Cursor::new(data)).unwrap();
|
||||
let mut encoder = GifEncoder::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(format!(
|
||||
"{}.{}",
|
||||
file_name.strip_suffix(".rle").unwrap(),
|
||||
".gif"
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
encoder.set_repeat(Repeat::Infinite).unwrap();
|
||||
encoder.try_encode_frames(image.into_frames()).unwrap();
|
||||
} else {
|
||||
File::create(file_name)
|
||||
.unwrap()
|
||||
.write_all(data.as_slice())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_name = Some(NullString::from("data\\profile_00.txt"));
|
||||
let dat_path = "E:\\Games\\Schatzjäger\\data\\datafile.dat";
|
||||
|
||||
let mut file = File::open(dat_path).unwrap();
|
||||
let dat: Datafile = Datafile::read(&mut file).unwrap();
|
||||
println!("{:#?}", dat);
|
||||
|
||||
// extract(&dat, &mut file);
|
||||
|
||||
if let Some(file_name) = file_name {
|
||||
let target = dat.files.iter().find(|it| it.name == file_name).unwrap();
|
||||
file.seek(SeekFrom::Start(target.pos as u64)).unwrap();
|
||||
let mut data = vec![0u8; target.len as usize];
|
||||
file.read_exact(&mut data).unwrap();
|
||||
|
||||
match Path::new(&file_name.to_string())
|
||||
.extension()
|
||||
.and_then(OsStr::to_str)
|
||||
{
|
||||
Some("xml") => {
|
||||
let mut data =
|
||||
from_str::<UiTag>(String::from_utf8(data).unwrap().as_str()).unwrap();
|
||||
data.post_process();
|
||||
println!("{:#?}", data)
|
||||
}
|
||||
Some("txt") => {
|
||||
if false {
|
||||
/*let decr = decrypt_txt(&mut data);
|
||||
let entries: String = decrypt_exposed_txt(decr);*/
|
||||
let decr = decrypt_txt(data.into_iter()).unwrap();
|
||||
println!("{}", &decr);
|
||||
let sprites = Sprites::parse(decr.as_str()).unwrap();
|
||||
println!("{:#?}", sprites);
|
||||
} else {
|
||||
println!(
|
||||
"{}",
|
||||
decrypt_exposed_txt(decrypt_txt(data.into_iter()).unwrap()).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
Some("rle") => {
|
||||
let image: RleImage = RleImage::read(&mut Cursor::new(data)).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.gif");
|
||||
println!("{:?}", path);
|
||||
let mut encoder = GifEncoder::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.unwrap(),
|
||||
);
|
||||
encoder.set_repeat(Repeat::Infinite).unwrap();
|
||||
encoder.try_encode_frames(image.into_frames()).unwrap();
|
||||
}
|
||||
Some("dat") => {
|
||||
let image = level_tile_data_to_image(&data).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.png");
|
||||
println!("{:?}", path);
|
||||
image.save_with_format(path, ImageFormat::Png).unwrap();
|
||||
}
|
||||
Some(ext) => eprintln!("Unknown file extension <{}>", ext),
|
||||
None => eprintln!("Failed to read"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn decr2()
|
||||
Reference in New Issue
Block a user