From a9295e7d37ac157a15eea33d892185ef43ff05f8 Mon Sep 17 00:00:00 2001 From: scurest Date: Sun, 11 Dec 2016 15:14:07 -0600 Subject: [PATCH] It works! Commit it --- .gitignore | 1 + Cargo.lock | 728 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 + src/errors.rs | 10 + src/geometry.rs | 156 +++++++++ src/gfx.rs | 185 ++++++++++ src/main.rs | 45 +++ src/nitro/info_block.rs | 36 ++ src/nitro/mdl/mod.rs | 46 +++ src/nitro/mdl/model.rs | 229 ++++++++++++ src/nitro/mdl/xform.rs | 141 ++++++++ src/nitro/mod.rs | 43 +++ src/nitro/tex/image.rs | 235 +++++++++++++ src/nitro/tex/mod.rs | 45 +++ src/nitro/tex/texture.rs | 81 +++++ src/render.rs | 107 ++++++ src/util/bits.rs | 20 ++ src/util/cur.rs | 71 ++++ src/util/fields.rs | 44 +++ src/util/fixed.rs | 25 ++ src/util/mod.rs | 7 + src/util/name.rs | 51 +++ src/util/view.rs | 136 ++++++++ src/viewer.rs | 308 +++++++++++++++++ 24 files changed, 2763 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/errors.rs create mode 100644 src/geometry.rs create mode 100644 src/gfx.rs create mode 100644 src/main.rs create mode 100644 src/nitro/info_block.rs create mode 100644 src/nitro/mdl/mod.rs create mode 100644 src/nitro/mdl/model.rs create mode 100644 src/nitro/mdl/xform.rs create mode 100644 src/nitro/mod.rs create mode 100644 src/nitro/tex/image.rs create mode 100644 src/nitro/tex/mod.rs create mode 100644 src/nitro/tex/texture.rs create mode 100644 src/render.rs create mode 100644 src/util/bits.rs create mode 100644 src/util/cur.rs create mode 100644 src/util/fields.rs create mode 100644 src/util/fixed.rs create mode 100644 src/util/mod.rs create mode 100644 src/util/name.rs create mode 100644 src/util/view.rs create mode 100644 src/viewer.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..05e1698 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,728 @@ +[root] +name = "demense" +version = "0.1.0" +dependencies = [ + "cgmath 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glium 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "android_glue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "approx" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cgl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gleam 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cgmath" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cocoa" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dbghelp-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dlib" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libloading 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dwmapi-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fs2" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gcc" +version = "0.3.39" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gdi32-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gl_generator" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gleam" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glium" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glutin" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-kbd 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-window 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "khronos_api" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libloading" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "target_build_utils 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memmap" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "objc" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-serialize" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "shared_library" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "shell32-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "target_build_utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "user32-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wayland-client" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-kbd" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-scanner" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "xml-rs 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-sys" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-window" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "x11-dl" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xml-rs" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" +"checksum android_glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e2b80445d331077679dfc6f3014f3e9ab7083e588423d35041d3fc017198189" +"checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" +"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" +"checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80" +"checksum backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3602e8d8c43336088a8505fa55cae2b3884a9be29440863a11528a42f46f6bb7" +"checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c" +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" +"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" +"checksum cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8bdd78cca65a739cb5475dbf6b6bbb49373e327f4a6f2b499c0f98632df38c10" +"checksum cgmath 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "232472604dbad61c384edfc4d833315d487894826856836b7227b37fe3abe02b" +"checksum cocoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3afe4613f57a171039a98db1773f5840b5743cf85aaf03afb65ddfade4f4a9db" +"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd" +"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5" +"checksum core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0c56c6022ba22aedbaa7d231be545778becbe1c7aceda4c82ba2f2084dd4c723" +"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" +"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" +"checksum dlib 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "148bce4ce1c36c4509f29cb54e62c2bd265551a9b00b38070fad551a851866ec" +"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" +"checksum dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07c4c7cc7b396419bc0a4d90371d0cee16cb5053b53647d287c0b728000c41fe" +"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" +"checksum error-chain 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd681735364a04cd5d69f01a4f6768e70473941f8d86d8c224faf6955a75799" +"checksum fs2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd414e5a1a979b931bb92f41b7a54106d3f6d2e6c253e9ce943b7cd468251ef" +"checksum gcc 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)" = "771e4a97ff6f237cf0f7d5f5102f6e28bb9743814b6198d684da5c58b76c11e0" +"checksum gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "65256ec4dc2592e6f05bfc1ca3b956a4e0698aa90b1dff1f5687d55a5a3fd59a" +"checksum gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1d8edc81c5ae84605a62f5dac661a2313003b26d59839f81d47d46cf0f16a55" +"checksum gleam 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)" = "b0b10e85c0e5fc984464336007e592a9072dcd976aa0687d2b67966bc83322d8" +"checksum glium 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30bfe6ac8600d25f7f2a1e3203566b156d66e7c2f358f6bb79b5419ddaecd671" +"checksum glutin 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "06786fae66e7aa8464b3d8d3fb7a7c470f89d62ae511f9613ea7fbbeef61d680" +"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09c9d3760673c427d46f91a0350f0a84a52e6bc5a84adf26dc610b6c52436630" +"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417" +"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" +"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" +"checksum libloading 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "84816a8c6ed8163dfe0dbdd2b09d35c6723270ea77a4c7afa4bedf038a36cb99" +"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +"checksum memmap 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f20f72ed93291a72e22e8b16bb18762183bb4943f0f483da5b8be1a9e8192752" +"checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" +"checksum objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "877f30f37acef6749b1841cceab289707f211aecfc756553cd63976190e6cc2e" +"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +"checksum phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "0c6afb2057bb5f846a7b75703f90bc1cef4970c35209f712925db7768e999202" +"checksum phf_codegen 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "6b63f121bf9a128f2172a65d8313a8e0e79d63874eeb4b4b7d82e6dda6b62f7c" +"checksum phf_generator 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "50ffbd7970f75afa083c5dd7b6830c97b72b81579c7a92d8134ef2ee6c0c7eb0" +"checksum phf_shared 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "286385a0e50d4147bce15b2c19f0cf84c395b0e061aaf840898a7bf664c2cfb7" +"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa" +"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" +"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" +"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" +"checksum rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1430d286cadb237c17c885e25447c982c97113926bb579f4379c0eca8d9586dc" +"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" +"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +"checksum serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0e0732aa8ec4267f61815a396a942ba3525062e3bd5520aa8419927cfc0a92" +"checksum serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "58a19c0871c298847e6b68318484685cd51fa5478c0c905095647540031356e5" +"checksum serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7d3c184d35801fb8b32b46a7d58d57dbcc150b0eb2b46a1eb79645e8ecfd5b" +"checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a" +"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" +"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410" +"checksum target_build_utils 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54c550e226618cd35334b75e92bfa5437c61474bdb75c38bf330ab5a8037b77c" +"checksum tempfile 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9270837a93bad1b1dac18fe67e786b3c960513af86231f6f4f57fddd594ff0c8" +"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" +"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" +"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af" +"checksum user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6717129de5ac253f5642fc78a51d0c7de6f9f53d617fc94e9bae7f6e71cf5504" +"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" +"checksum wayland-client 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ced3094c157b5cc0a08d40530e1a627d9f88b9a436971338d2646439128a559e" +"checksum wayland-kbd 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "73bc10e84c1da90777beffecd24742baea17564ffc2a9918af41871c748eb050" +"checksum wayland-scanner 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "5a1869370d6bafcbabae8724511d803f4e209a70e94ad94a4249269534364f66" +"checksum wayland-sys 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9633f7fe5de56544215f82eaf1b76bf1b584becf7f08b58cbef4c2c7d10e803a" +"checksum wayland-window 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "309b69d3a863c9c21422d889fb7d98cf02f8a2ca054960a49243ce5b67ad884c" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum x11-dl 2.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4c7f0a7fb861a1bde4aa23bbda9509bda6b87de4d47c322f86e4c88241ebdd" +"checksum xml-rs 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b15eed12692bd59d15e98ee7f8dc8408465b992d8ddb4d1672c24865132ec7" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2272afc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "demense" +version = "0.1.0" +authors = ["scurest "] +license = "CC0-1.0" + +[dependencies] +cgmath = "0.12.0" +env_logger = "0.3.5" +error-chain = "0.7.1" +glium = "0.15.0" +log = "0.3.6" +time = "0.1.35" diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..4477efa --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,10 @@ +error_chain! {} + +macro_rules! check { + ($b:expr) => { + if !$b { + error!("expected: {})", stringify!($b)); + return Err("sanity check failed".into()); + } + }; +} diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 0000000..b39225f --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,156 @@ +use cgmath::Point2; +use cgmath::Point3; +use gfx; +use std::default::Default; +use std::ops::Range; + +#[derive(Debug, Copy, Clone)] +pub struct Vertex { + pub position: [f32; 3], + pub texcoord: [f32; 2], + pub color: [f32; 3], +} + +#[derive(Debug, Clone)] +pub struct MeshRange { + pub vertex_range: Range, + pub index_range: Range, + pub mat_id: u8, +} + +#[derive(Debug, Clone, Default)] +pub struct GeometryData { + pub vertices: Vec, + pub indices: Vec, + pub mesh_ranges: Vec, +} + +#[derive(Debug, Clone)] +pub struct Sink { + pub data: GeometryData, + pub cur_texture_dim: (u32, u32), + cur_mesh_range: MeshRange, + cur_prim_type: u32, + next_texcoord: Point2, + next_color: Point3, +} + +impl Sink { + pub fn new() -> Sink { + Sink { + data: Default::default(), + cur_mesh_range: MeshRange { + vertex_range: 0..0, + index_range: 0..0, + mat_id: 0, + }, + cur_prim_type: 0, + cur_texture_dim: (1,1), + next_texcoord: Point2::new(0.0, 0.0), + next_color: Point3::new(1.0, 1.0, 1.0), + } + } + pub fn begin_mesh(&mut self, mat_id: u8) { + let len = self.data.indices.len(); + self.cur_mesh_range.index_range = len .. len; + self.cur_mesh_range.mat_id = mat_id; + self.next_texcoord = Point2::new(0.0, 0.0); + self.next_color = Point3::new(1.0, 1.0, 1.0); + } + pub fn end_mesh(&mut self) { + self.data.mesh_ranges.push(self.cur_mesh_range.clone()); + } +} + +impl gfx::Sink for Sink { + fn begin(&mut self, prim_type: u32) { + self.cur_prim_type = prim_type; + let len = self.data.vertices.len() as u16; + self.cur_mesh_range.vertex_range = len .. len; + } + fn end(&mut self) { + let r = self.cur_mesh_range.vertex_range.clone(); + match self.cur_prim_type { + 0 => { + // Seperate triangles + let mut i = r.start; + while i != r.end { + self.data.indices.extend_from_slice(&[ + i, i+1, i+2, + ]); + i += 3; + } + } + 1 => { + // Seperate quads + let mut i = r.start; + while i != r.end { + self.data.indices.extend_from_slice(&[ + i, i+1, i+2, + i+2, i+3, i, + ]); + i += 4; + } + } + 2 => { + // Triangle strip + let mut i = r.start; + if i != r.end { + self.data.indices.extend_from_slice(&[ + i, i+1, i+2, + ]); + i += 3; + } + while i != r.end { + self.data.indices.extend_from_slice(&[ + i, i-1, i-2, + ]); + i += 1; + if i == r.end { break; } + self.data.indices.extend_from_slice(&[ + i, i-2, i-1, + ]); + i += 1; + } + } + 3 => { + // Quad strip + let mut i = r.start; + if i != r.end { + self.data.indices.extend_from_slice(&[ + i, i+1, i+2, + i+2, i+1, i+3, + ]); + i += 4; + } + while i != r.end { + self.data.indices.extend_from_slice(&[ + i-2, i-1, i, + i, i-1, i+1, + ]); + i += 2; + } + } + _ => unreachable!(), + } + self.cur_mesh_range.index_range.end = self.data.indices.len(); + } + fn texcoord(&mut self, texcoord: Point2) { + self.next_texcoord = Point2::new( + texcoord.x / self.cur_texture_dim.0 as f64, + // TODO: t coordinate seems to be wrong for mirrored textures + 1.0 - texcoord.y / self.cur_texture_dim.1 as f64, + ); + } + fn color(&mut self, color: Point3) { + self.next_color = color; + } + fn vertex(&mut self, v: Point3) { + self.data.vertices.push(Vertex { + position: [v.x as f32, v.y as f32, v.z as f32], + texcoord: [self.next_texcoord.x as f32, self.next_texcoord.y as f32], + color: [self.next_color.x, self.next_color.y, self.next_color.z], + }); + self.cur_mesh_range.vertex_range.end += 1; + } +} diff --git a/src/gfx.rs b/src/gfx.rs new file mode 100644 index 0000000..ac06fb5 --- /dev/null +++ b/src/gfx.rs @@ -0,0 +1,185 @@ +use cgmath::Matrix4; +use cgmath::Point2; +use cgmath::Point3; +use cgmath::Transform; +use cgmath::vec3; +use cgmath::vec4; +use errors::Result; +use util::bits::BitField; +use util::fixed::fix16; +use util::fixed::fix32; +use util::view::View; + +pub trait Sink { + fn begin(&mut self, prim_type: u32); + fn end(&mut self); + fn vertex(&mut self, v: Point3); + fn texcoord(&mut self, tc: Point2); + fn color(&mut self, color: Point3); +} + +pub struct GfxState { + pub vertex: Point3, + pub cur_mat: Matrix4, + pub mat_stack: Vec>, + pub texture_mat: Matrix4, +} + +impl GfxState { + pub fn new() -> GfxState { + GfxState { + vertex: Point3::new(0.0, 0.0, 0.0), + cur_mat: Matrix4::from_scale(1.0), + mat_stack: vec![Matrix4::from_scale(1.0); 32], + texture_mat: Matrix4::from_scale(1.0), + } + } + + pub fn restore(&mut self, id: u32) { + self.cur_mat = self.mat_stack[id as usize]; + } + + pub fn run_commands(&mut self, sink: &mut S, mut cmds: &[u8]) -> Result<()> { + let mut fifo = &cmds[0..0]; + while cmds.len() != 0 { + if fifo.len() == 0 { + fifo = &cmds[0..4]; + cmds = &cmds[4..]; + } + let opcode = fifo[0]; + fifo = &fifo[1..]; + let size = gfx_cmd_size(opcode)?; + let params = View::from_buf(&cmds[0..4*size]); + cmds = &cmds[4*size..]; + self.run_command(sink, opcode, params); + } + Ok(()) + } + + pub fn run_command(&mut self, sink: &mut S, opcode: u8, params: View) { + match opcode { + 0x00 => { + // NOP + } + 0x14 => { + // MTX_RESTORE - Restore Current Matrix from Stack + let restore_id = params.get(0); + self.restore(restore_id); + } + 0x1b => { + // MTX_SCALE - Multiply Current Matrix by Scale Matrix + let sx = fix32(params.get(0), 1, 19, 12); + let sy = fix32(params.get(1), 1, 19, 12); + let sz = fix32(params.get(2), 1, 19, 12); + self.cur_mat = self.cur_mat * Matrix4::from_nonuniform_scale(sx, sy, sz); + } + 0x40 => { + // BEGIN_VTXS - Start of Vertex List + let prim_type = params.get(0); + sink.begin(prim_type); + } + 0x41 => { + // END_VTXS - End of Vertex List + sink.end(); + } + 0x23 => { + // VTX_16 - Set Vertex XYZ Coordinates + let p0 = params.get(0); + let p1 = params.get(1); + let x = fix16(p0.bits(0,16) as u16, 1, 3, 12); + let y = fix16(p0.bits(16,32) as u16, 1, 3, 12); + let z = fix16(p1.bits(0,16) as u16, 1, 3, 12); + let v = Point3::new(x, y, z); + self.push_vertex(sink, v); + } + 0x24 => { + // VTX_10 - Set Vertex XYZ Coordinates + let p = params.get(0); + let x = fix16(p.bits(0,10) as u16, 1, 3, 6); + let y = fix16(p.bits(10,20) as u16, 1, 3, 6); + let z = fix16(p.bits(20,30) as u16, 1, 3, 6); + let v = Point3::new(x, y, z); + self.push_vertex(sink, v); + } + 0x25 => { + // VTX_XY - Set Vertex XY Coordinates + let p = params.get(0); + let x = fix16(p.bits(0,16) as u16, 1, 3, 12); + let y = fix16(p.bits(16,32) as u16, 1, 3, 12); + let v = Point3::new(x, y, self.vertex.z); + self.push_vertex(sink, v); + } + 0x26 => { + // VTX_XZ - Set Vertex XZ Coordinates + let p = params.get(0); + let x = fix16(p.bits(0,16) as u16, 1, 3, 12); + let z = fix16(p.bits(16,32) as u16, 1, 3, 12); + let v = Point3::new(x, self.vertex.y, z); + self.push_vertex(sink, v); + } + 0x27 => { + // VTX_YZ - Set Vertex YZ Coordinates + let p = params.get(0); + let y = fix16(p.bits(0,16) as u16, 1, 3, 12); + let z = fix16(p.bits(16,32) as u16, 1, 3, 12); + let v = Point3::new(self.vertex.x, y, z); + self.push_vertex(sink, v); + } + 0x28 => { + // VTX_DIFF - Set Relative Vertex Coordinates + let p = params.get(0); + // Differences are 10-bit numbers, scaled by 1/2^3 to put them + // in the same 1,3,12 format as the others VTX commands. + let scale = 0.5f64.powi(3); + let dx = scale * fix16(p.bits(0,10) as u16, 1, 0, 9); + let dy = scale * fix16(p.bits(10,20) as u16, 1, 0, 9); + let dz = scale * fix16(p.bits(20,30) as u16, 1, 0, 9); + let v = self.vertex + vec3(dx, dy, dz); + self.push_vertex(sink, v); + } + 0x22 => { + // TEXCOORD - Set Texture Coordinates + let p = params.get(0); + let s = fix16(p.bits(0,16) as u16, 1, 11, 4); + let t = fix16(p.bits(16,32) as u16, 1, 11, 4); + let tc = self.texture_mat * vec4(s,t,0.0,0.0); + let texcoord = Point2::new(tc.x, tc.y); + sink.texcoord(texcoord); + } + 0x20 => { + let p = params.get(0); + let r = p.bits(0,5) as f32 / 31.0; + let g = p.bits(5,10) as f32 / 31.0; + let b = p.bits(10,15) as f32 / 31.0; + let color = Point3::new(r,g,b); + sink.color(color); + } + 0x21 => { + // normal + } + _ => { panic!("unknown opcode {:#x}", opcode); } + } + } + + fn push_vertex(&mut self, sink: &mut S, vertex: Point3) { + self.vertex = vertex; + sink.vertex(self.cur_mat.transform_point(vertex)); + } +} + + +fn gfx_cmd_size(opcode: u8) -> Result { + static SIZES: [i8; 66] = [ + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 0, 1, 1, 1, 0, + 16, 12, 16, 12, 9, 3, 3, -1, -1, -1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, + ]; + let opcode = opcode as usize; + if opcode >= SIZES.len() || SIZES[opcode] == -1 { + return Err(format!("unknown geometry opcode: {:#x}", opcode).into()); + } + Ok(SIZES[opcode] as usize) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9002d78 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,45 @@ +#![recursion_limit = "1024"] // for error_chain + +#[macro_use] +extern crate log; +#[macro_use] +extern crate error_chain; +extern crate cgmath; +extern crate env_logger; +#[macro_use] +extern crate glium; +extern crate time; + +#[macro_use] +mod errors; +#[macro_use] +mod util; +mod nitro; +mod gfx; +mod viewer; +mod geometry; +mod render; + +use std::fs::File; +use std::io::Read; +use util::cur::Cur; + +fn main() { + env_logger::init().unwrap(); + + let arg = std::env::args().nth(1).unwrap(); + let mut f = File::open(&arg).unwrap(); + let mut v: Vec = vec![]; + f.read_to_end(&mut v).unwrap(); + + let cur = Cur::new(&v[..]); + let res = nitro::read_bmd(cur); + + match res { + Ok(bmd) => { + let model = &bmd.mdl.models[0]; + viewer::viewer(model, &bmd.tex).unwrap(); + } + Err(e) => error!("err {:#?}", e), + } +} diff --git a/src/nitro/info_block.rs b/src/nitro/info_block.rs new file mode 100644 index 0000000..69fb18c --- /dev/null +++ b/src/nitro/info_block.rs @@ -0,0 +1,36 @@ +use errors::Result; +use std::fmt::Debug; +use std::iter::Zip; +use util::cur::Cur; +use util::name::Name; +use util::view::View; +use util::view::Viewable; + +pub type Iterator<'a, T> = Zip, View<'a, Name>>; + +/// Returns an iterator over the offset/name pairs in an info block. +pub fn read(cur: Cur) -> Result> where + T: Viewable + Debug +{ + fields!(cur, info_block { + dummy: u8, + count: u8, + header_size: u16, + + unknown_subheader_size: u16, + unknown_section_size: u16, + unknown_constant: u32, + unknown_data: [u32; count], + + size_of_datum: u16, + data_section_size: u16, + data: [T; count], + + names: [Name; count], + }); + + check!(dummy == 0); + check!(size_of_datum as usize == ::size()); + + Ok(data.zip(names)) +} diff --git a/src/nitro/mdl/mod.rs b/src/nitro/mdl/mod.rs new file mode 100644 index 0000000..331f62d --- /dev/null +++ b/src/nitro/mdl/mod.rs @@ -0,0 +1,46 @@ +mod model; +mod xform; + +use cgmath::Matrix4; +use nitro::tex::TextureParameters; +use util::cur::Cur; +use util::name::Name; + +pub use self::model::read_mdl; + +#[derive(Debug, Clone)] +pub struct Mdl<'a> { + pub models: Vec>, +} + +#[derive(Debug, Clone)] +pub struct Model<'a> { + pub name: Name, + pub materials: Vec, + pub meshes: Vec>, + pub objects: Vec, + pub render_cmds_cur: Cur<'a>, +} + +#[derive(Debug, Clone)] +pub struct Material { + pub name: Name, + pub texture_name: Option, + pub palette_name: Option, + pub params: TextureParameters, + pub width: u16, + pub height: u16, + pub texture_mat: Matrix4, +} + +#[derive(Debug, Clone)] +pub struct Mesh<'a> { + pub name: Name, + pub commands: &'a [u8], +} + +#[derive(Debug, Clone)] +pub struct Object { + pub name: Name, + pub xform: Matrix4, +} diff --git a/src/nitro/mdl/model.rs b/src/nitro/mdl/model.rs new file mode 100644 index 0000000..16b9816 --- /dev/null +++ b/src/nitro/mdl/model.rs @@ -0,0 +1,229 @@ +use cgmath::Matrix4; +use cgmath::One; +use errors::Result; +use nitro::info_block; +use nitro::mdl::Material; +use nitro::mdl::Mdl; +use nitro::mdl::Mesh; +use nitro::mdl::Model; +use nitro::mdl::Object; +use nitro::mdl::xform; +use nitro::tex::TextureParameters; +use util::bits::BitField; +use util::cur::Cur; +use util::name::Name; + +pub fn read_mdl(cur: Cur) -> Result { + fields!(cur, MDL0 { + stamp: [u8; 4], + section_size: u32, + end: Cur, + }); + check!(stamp == b"MDL0"); + + let models = info_block::read::(end)? + .map(|(off, name)| read_model((cur + off as usize)?, name)) + .collect::>()?; + + Ok(Mdl { models: models }) +} + +fn read_model<'a>(cur: Cur<'a>, name: Name) -> Result> { + trace!("model: {}", name); + fields!(cur, model { + section_size: u32, + render_cmds_off: u32, + materials_off: u32, + mesh_off: u32, + unknown_off: u32, + unknown1: [u8; 3], + num_objects: u8, + num_materials: u8, + num_meshes: u8, + unknown2: [u8; 2], + scale: (fix32(1,19,12)), + bounding_box_scale: (fix32(1,19,12)), + num_verts: u16, + num_surfs: u16, + num_tris: u16, + num_quads: u16, + bounding_box_x_min: (fix16(1,3,12)), + bounding_box_y_min: (fix16(1,3,12)), + bounding_box_z_min: (fix16(1,3,12)), + bounding_box_x_max: (fix16(1,3,12)), + bounding_box_y_max: (fix16(1,3,12)), + bounding_box_z_max: (fix16(1,3,12)), + unknown3: [u8; 8], + end: Cur, + }); + + let render_cmds_cur = (cur + render_cmds_off as usize)?; + let objects = read_objects(end)?; + let materials = read_materials((cur + materials_off as usize)?)?; + let meshes = read_meshes((cur + mesh_off as usize)?)?; + + Ok(Model { + name: name, + materials: materials, + meshes: meshes, + objects: objects, + render_cmds_cur: render_cmds_cur, + }) +} + +fn read_meshes(cur: Cur) -> Result> { + info_block::read::(cur)? + .map(|(off, name)| read_mesh((cur + off as usize)?, name)) + .collect() +} + +fn read_mesh(cur: Cur, name: Name) -> Result { + trace!("mesh: {}", name); + fields!(cur, mesh { + dummy: u16, + section_size: u16, + unknown: u32, + commands_off: u32, + commands_len: u32, + }); + check!(section_size == 16); + check!(commands_len % 4 == 0); + + let commands = (cur + commands_off as usize)? + .next_n_u8s(commands_len as usize)?; + + Ok(Mesh { + name: name, + commands: commands, + }) +} + +fn read_materials(cur: Cur) -> Result> { + fields!(cur, materials { + texture_pairing_off: u16, + palette_pairing_off: u16, + end: Cur, + }); + + let mut materials = info_block::read::(end)? + .map(|(off, name)| read_material((cur + off as usize)?, name)) + .collect::>>()?; + + // Pair each texture with materials. + let tex_cur = (cur + texture_pairing_off as usize)?; + for ((off, num, _), name) in info_block::read::<(u16, u8, u8)>(tex_cur)? { + trace!("texture pairing: {}", name); + fields!((cur + off as usize)?, texture_pairings { + material_ids: [u8; num], + }); + for &mat_id in material_ids { + materials[mat_id as usize].texture_name = Some(name); + } + } + + // Pair each palette with materials. + let pal_cur = (cur + palette_pairing_off as usize)?; + for ((off, num, _), name) in info_block::read::<(u16, u8, u8)>(pal_cur)? { + trace!("palette pairing: {}", name); + fields!((cur + off as usize)?, palette_pairings { + material_ids: [u8; num], + }); + for &mat_id in material_ids { + materials[mat_id as usize].palette_name = Some(name); + } + } + + Ok(materials) +} + +fn read_material(cur: Cur, name: Name) -> Result { + trace!("material: {:?}", name); + fields!(cur, material { + dummy: u16, + section_size: u16, + dif_amb: u32, + spe_emi: u32, + polygon_attr: u32, + unknown2: u32, + params: u32, + unknown3: [u32; 2], + width: u16, + height: u16, + end: Cur, + }); + + let params = TextureParameters(params); + + let texture_mat = match params.texcoord_transform_mode() { + 0 => Matrix4::from_scale(1.0), + 1 => { + // This is probably wrong. It might also be 8 fix16s. + // But it handles the common case with a3=a4=2 for + // mirrored textures. + fields!(end, texcoord_matrix { + a1: (fix32(1,19,12)), // always 1? + a2: (fix32(1,19,12)), // always 1? + a3: (fix32(1,19,12)), + a4: (fix32(1,19,12)), + }); + Matrix4::from_nonuniform_scale(a3, a4, 1.0) + } + 2 | 3 => unimplemented!(), + _ => unreachable!(), + }; + + Ok(Material { + name: name, + texture_name: None, + palette_name: None, + params: params, + width: width, + height: height, + texture_mat: texture_mat, + }) +} + +fn read_objects(cur: Cur) -> Result> { + info_block::read::(cur)? + .map(|(off, name)| read_object((cur + off as usize)?, name)) + .collect() +} + +fn read_object(cur: Cur, name: Name) -> Result { + trace!("object: {}", name); + fields!(cur, object_transform { + flags: u16, + m0: (fix16(1,3,12)), + end: Cur, + }); + + let t = flags.bits(0,1); + let r = flags.bits(1,2); + let s = flags.bits(2,3); + let p = flags.bits(3,4); + + let mut cur = end; + let mut xform = Matrix4::one(); + + if t == 0 { + let translation = xform::read_translation(&mut cur)?; + xform = translation; + } + if p == 1 { + let rotation = xform::read_rotation(&mut cur, flags)?; + xform = xform * rotation; + } + if p == 0 && r == 0 { + let matrix = xform::read_matrix(&mut cur, m0)?; + xform = xform * matrix; + } + if s == 0 { + let scale = xform::read_scale(&mut cur)?; + xform = xform * scale; + } + + Ok(Object { + name: name, + xform: xform, + }) +} diff --git a/src/nitro/mdl/xform.rs b/src/nitro/mdl/xform.rs new file mode 100644 index 0000000..1ad3732 --- /dev/null +++ b/src/nitro/mdl/xform.rs @@ -0,0 +1,141 @@ +use cgmath::Matrix; +use cgmath::Matrix4; +use cgmath::vec3; +use cgmath::Vector4; +use errors::Result; +use util::bits::BitField; +use util::cur::Cur; + +pub fn read_translation(cur: &mut Cur) -> Result> { + fields!(*cur, tranlation { + x: (fix32(1,19,12)), + y: (fix32(1,19,12)), + z: (fix32(1,19,12)), + end: Cur, + }); + *cur = end; + Ok(Matrix4::from_translation(vec3(x, y, z))) +} + +pub fn read_rotation(cur: &mut Cur, flags: u16) -> Result> { + fields!(*cur, rot { + a: (fix16(1,3,12)), + b: (fix16(1,3,12)), + end: Cur, + }); + *cur = end; + let select = flags.bits(4,8); + let neg = flags.bits(8,12); + pivot_mat(select, neg, a, b) +} + +pub fn read_matrix(cur: &mut Cur, m0: f64) -> Result> { + fields!(*cur, rot { + m1: (fix16(1,3,12)), + m2: (fix16(1,3,12)), + m3: (fix16(1,3,12)), + m4: (fix16(1,3,12)), + m5: (fix16(1,3,12)), + m6: (fix16(1,3,12)), + m7: (fix16(1,3,12)), + m8: (fix16(1,3,12)), + end: Cur, + }); + *cur = end; + Ok(Matrix4::new( + m0, m1, m2, 0.0, + m3, m4, m5, 0.0, + m6, m7, m8, 0.0, + 0.0, 0.0, 0.0, 1.0, + )) +} + +pub fn read_scale(cur: &mut Cur) -> Result> { + fields!(*cur, scale { + sx: (fix32(1,19,12)), + sy: (fix32(1,19,12)), + sz: (fix32(1,19,12)), + end: Cur, + }); + *cur = end; + Ok(Matrix4::from_nonuniform_scale(sx,sy,sz)) +} + +fn pivot_mat(select: u16, neg: u16, a: f64, b: f64) -> Result> { + if select > 9 { + return Err(format!("unknown pivot select: {}", select).into()); + } + + if select == 9 { + // Does this actually happen? + return Ok(Matrix4::new( + -a, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + )); + } + + let o = if neg.bits(0,1) == 0 { 1.0 } else { -1.0 }; + let c = if neg.bits(1,2) == 0 { b } else { -b }; + let d = if neg.bits(2,3) == 0 { a } else { -a }; + + // `select` chooses the result in the following way + // + // o.. .o. ..o + // 0=.ac 3=a.c 6=ac. + // .bd b.d bd. + // + // .ac a.c ac. + // 1=o.. 4=.o. 7=..o + // .bd b.d bd. + // + // .ac a.c ac. + // 2=.bd 5=b.d 8=bd. + // o.. .o. ..o + // + // Note that they are all permutations of the rows + // and columns of the first matrix. + let mat = Matrix4::new( + o, 0.0, 0.0, 0.0, + 0.0, a, b, 0.0, + 0.0, c, d, 0.0, + 0.0, 0.0, 0.0, 1.0, + ); + let pi_cols = match select / 3 { + 0 => pi(1,2,3,4), + 1 => pi(2,1,3,4), + 2 => pi(2,3,1,4), + _ => unreachable!(), + }; + let pi_rows = match select % 3 { + 0 => pi(1,2,3,4), + 1 => pi(2,1,3,4), + 2 => pi(2,3,1,4), + _ => unreachable!(), + }; + Ok(pi_rows.transpose() * mat * pi_cols) +} + +/// The permutation matrix corresponding to the permutation of {1,2,3,4} sending +/// 1 to a, 2 to b, etc. +/// +/// The matrix is the image of the permutation regarded as a map {1,2,3,4} -> {1,2,3,4} +/// under the free vector space functor. That is, it is the identity matrix with the +/// columns permuted according to the given permutation. +/// +/// The upshot is m * pi(...) permutes the columns of m and pi(...)^T * m permutes +/// the rows. +fn pi(a: usize, b: usize, c: usize, d: usize) -> Matrix4 { + assert_eq!( + [1,2,3,4], + { let mut arr = [a,b,c,d]; arr.sort(); arr } + ); + let basis = [ + Vector4::unit_x(), + Vector4::unit_y(), + Vector4::unit_z(), + Vector4::unit_w(), + ]; + Matrix4::from_cols(basis[a-1], basis[b-1], basis[c-1], basis[d-1]) +} diff --git a/src/nitro/mod.rs b/src/nitro/mod.rs new file mode 100644 index 0000000..77198ce --- /dev/null +++ b/src/nitro/mod.rs @@ -0,0 +1,43 @@ +pub mod mdl; +pub mod tex; +mod info_block; + +use nitro::mdl::Mdl; +use nitro::mdl::read_mdl; +use nitro::tex::Tex; +use nitro::tex::read_tex; +use util::cur::Cur; +use errors::Result; + +#[derive(Debug, Clone)] +pub struct Bmd<'a> { + pub mdl: Mdl<'a>, + pub tex: Tex<'a>, +} + +pub fn read_bmd(cur: Cur) -> Result { + fields!(cur, BMD0 { + stamp: [u8; 4], + bom: u16, + version: u16, + file_size: u32, + header_size: u16, + num_sections: u16, + section_offs: [u32; num_sections], + }); + check!(stamp == b"BMD0"); + check!(bom == 0xfeff); + check!(header_size == 16); + check!(num_sections > 0); + + let mdl_cur = (cur + section_offs.get(0) as usize)?; + let tex_cur = (cur + section_offs.get(1) as usize)?; + + let mdl = read_mdl(mdl_cur)?; + let tex = read_tex(tex_cur)?; + + Ok(Bmd { + mdl: mdl, + tex: tex, + }) +} diff --git a/src/nitro/tex/image.rs b/src/nitro/tex/image.rs new file mode 100644 index 0000000..35b6bb8 --- /dev/null +++ b/src/nitro/tex/image.rs @@ -0,0 +1,235 @@ +use nitro::tex::PaletteInfo; +use nitro::tex::Tex; +use nitro::tex::TextureInfo; +use errors::Result; +use util::bits::BitField; +use util::view::View; + +pub fn gen_image( + tex: &Tex, + tex_info: &TextureInfo, + pal_info: Option<&PaletteInfo>, +) -> Result> +{ + match tex_info.params.format() { + 2 | 3 | 4 | 1 | 6 => { + match pal_info { + Some(pal_info) => Ok(gen_palette_image(tex, tex_info, pal_info)), + None => Err("texture with palette format was not paired with a palette".into()), + } + } + 5 => { + match pal_info { + Some(pal_info) => Ok(gen_compressed_image(tex, tex_info, pal_info)), + None => Err("texture with palette format was not paired with a palette".into()), + } + } + _ => unimplemented!(), + } +} + +fn gen_palette_image(tex: &Tex, tex_info: &TextureInfo, pal_info: &PaletteInfo) -> Vec +{ + let texture_off = tex_info.params.offset(); + let width = tex_info.params.width() as usize; + let height = tex_info.params.height() as usize; + let format = tex_info.params.format(); + let color0_is_transparent = tex_info.params.is_color0_transparent(); + let palette_off = pal_info.off; + + let bpps = [0u8, 8, 2, 4, 8, 0, 8]; + let bpp = bpps[format as usize] as usize; + + let palette_bytes = &tex.palette_data[palette_off ..]; + let palette: View = View::from_buf(palette_bytes); + + let texture_size = width * height * bpp / 8; + let texture = &tex.texture_data[texture_off .. texture_off + texture_size]; + + let mut pixels = vec![0u8; 4 * width * height]; // 4 bytes (RGBA) for every texel + let mut i = 0; + + match format { + 2 => { + // 4-Color Palette Texture + for &x in texture { + for &v in &[x.bits(0,2), x.bits(2,4), x.bits(4,6), x.bits(6,8)] { + let transparent = v == 0 && color0_is_transparent; + write_pixel(&mut pixels, &mut i, rgb555a5( + palette.get(v as usize), + if transparent { 0 } else { 31 }, + )); + } + } + } + 3 => { + // 16-Color Palette Texture + for &x in texture { + for &v in &[x.bits(0,4), x.bits(4,8)] { + let transparent = v == 0 && color0_is_transparent; + write_pixel(&mut pixels, &mut i, rgb555a5( + palette.get(v as usize), + if transparent { 0 } else { 31 }, + )); + } + } + } + 4 => { + // 256-Color Palette Texture + for &v in texture { + let transparent = v == 0 && color0_is_transparent; + write_pixel(&mut pixels, &mut i, rgb555a5( + palette.get(v as usize), + if transparent { 0 } else { 31 }, + )); + } + } + 1 => { + // A3I5 Translucent Texture (3-bit Alpha, 5-bit Color Index) + for &x in texture { + write_pixel(&mut pixels, &mut i, rgb555a5( + palette.get(x.bits(0,5) as usize), + a3_to_a5(x.bits(5,8)), + )); + } + } + 6 => { + // A5I3 Translucent Texture (5-bit Alpha, 3-bit Color Index) + for &x in texture { + write_pixel(&mut pixels, &mut i, rgb555a5( + palette.get(x.bits(0,3) as usize), + x.bits(3,8), + )); + } + } + _ => unreachable!(), + } + + pixels +} + +pub fn gen_compressed_image(tex: &Tex, tex_info: &TextureInfo, pal_info: &PaletteInfo) -> Vec { + let texture_off = tex_info.params.offset(); + let width = tex_info.params.width() as usize; + let height = tex_info.params.height() as usize; + let palette_off = pal_info.off; + let num_blocks_x = width / 4; + let palette: View = View::from_buf(&tex.palette_data[palette_off..]); + let block_data: View = View::from_buf(&tex.compressed_texture_data[texture_off..]); + let extra_data: View = View::from_buf(&tex.compressed_texture_extra_data[texture_off / 2..]); + + let mut pixels = vec![0u8; 4*width*height]; + let mut i = 0; + + for y in 0..height { + for x in 0..width { + let idx = num_blocks_x * (y/4) + (x/4); + let block = block_data.get(idx); + let extra = extra_data.get(idx); + + let texel_off = 2 * (4 * (y%4) + (x%4)) as u32; + let texel = block.bits(texel_off, texel_off+2); + + let pal_addr = (extra.bits(0,14) as usize) << 1; + let colors = [ + rgb555a5(palette.get(pal_addr+0), 31), + rgb555a5(palette.get(pal_addr+1), 31), + rgb555a5(palette.get(pal_addr+2), 31), + rgb555a5(palette.get(pal_addr+3), 31), + ]; + + let mode = extra.bits(14,16); + + let color = match mode { + 0 => { + match texel { + 0 => colors[0], + 1 => colors[1], + 2 => colors[2], + 3 => [0, 0, 0, 0], + _ => unreachable!(), + } + } + 1 => { + match texel { + 0 => colors[0], + 1 => colors[1], + 2 => avg(colors[0], colors[1]), + 3 => [0, 0, 0, 0], + _ => unreachable!(), + } + } + 2 => { + match texel { + 0 => colors[0], + 1 => colors[1], + 2 => colors[2], + 3 => colors[3], + _ => unreachable!(), + } + } + 3 => { + match texel { + 0 => colors[0], + 1 => colors[1], + 2 => avg358(colors[1], colors[0]), + 3 => avg358(colors[0], colors[1]), + _ => unreachable!(), + } + } + _ => unreachable!(), + }; + write_pixel(&mut pixels, &mut i, color); + } + } + + pixels +} + +fn write_pixel(pixels: &mut [u8], i: &mut usize, pixel: [u8; 4]) { + pixels[*i+0] = pixel[0]; + pixels[*i+1] = pixel[1]; + pixels[*i+2] = pixel[2]; + pixels[*i+3] = pixel[3]; + *i += 4; +} + +/// Converts RGB555 color and A5 alpha into RGBA8888. +fn rgb555a5(rgb555: u16, a5: u8) -> [u8; 4] { + let r5 = rgb555.bits(0,5) as u8; + let g5 = rgb555.bits(5,10) as u8; + let b5 = rgb555.bits(10,15) as u8; + let r8 = extend_5bit_to_8bit(r5); + let g8 = extend_5bit_to_8bit(g5); + let b8 = extend_5bit_to_8bit(b5); + let a8 = extend_5bit_to_8bit(a5); + [r8, g8, b8, a8] +} + +fn a3_to_a5(x: u8) -> u8 { + (x << 2) | (x >> 1) +} + +fn extend_5bit_to_8bit(x: u8) -> u8 { + (x << 3) | (x >> 2) +} + +/// (c1 + c2) / 2 +fn avg(c1: [u8; 4], c2: [u8; 4]) -> [u8; 4] { + [ + ((c1[0] as u32 + c2[0] as u32) / 2) as u8, + ((c1[1] as u32 + c2[1] as u32) / 2) as u8, + ((c1[2] as u32 + c2[2] as u32) / 2) as u8, + ((c1[3] as u32 + c2[3] as u32) / 2) as u8, + ] +} + +/// (3*c1 + 5*c2) / 8 +fn avg358(c1: [u8; 4], c2: [u8; 4]) -> [u8; 4] { + [ + ((3*c1[0] as u32 + 5*c2[0] as u32) / 8) as u8, + ((3*c1[1] as u32 + 5*c2[1] as u32) / 8) as u8, + ((3*c1[2] as u32 + 5*c2[2] as u32) / 8) as u8, + ((3*c1[3] as u32 + 5*c2[3] as u32) / 8) as u8, + ] +} diff --git a/src/nitro/tex/mod.rs b/src/nitro/tex/mod.rs new file mode 100644 index 0000000..8d62802 --- /dev/null +++ b/src/nitro/tex/mod.rs @@ -0,0 +1,45 @@ +use util::bits::BitField; +use util::name::Name; + +pub mod image; +mod texture; + +pub use self::texture::read_tex; + +#[derive(Debug, Clone)] +pub struct Tex<'a> { + pub texinfo: Vec, + pub palinfo: Vec, + pub texture_data: &'a [u8], + pub compressed_texture_data: &'a [u8], + pub compressed_texture_extra_data: &'a [u8], + pub palette_data: &'a [u8], +} + +#[derive(Debug, Clone)] +pub struct TextureInfo { + pub name: Name, + pub params: TextureParameters, +} + +#[derive(Debug, Clone)] +pub struct PaletteInfo { + pub name: Name, + pub off: usize, +} + +#[derive(Debug, Copy, Clone)] +pub struct TextureParameters(pub u32); + +impl TextureParameters { + pub fn offset(self) -> usize { (self.0.bits(0,16) as usize) << 3 } + pub fn repeat_s(self) -> bool { self.0.bits(16,17) != 0 } + pub fn repeat_t(self) -> bool { self.0.bits(17,18) != 0 } + pub fn mirror_s(self) -> bool { self.0.bits(18,19) != 0 } + pub fn mirror_t(self) -> bool { self.0.bits(19,20) != 0 } + pub fn width(self) -> u32 { 8 << self.0.bits(20,23) } + pub fn height(self) -> u32 { 8 << self.0.bits(23,26) } + pub fn format(self) -> u32 { self.0.bits(26,29) } + pub fn is_color0_transparent(self) -> bool { self.0.bits(29,30) != 0 } + pub fn texcoord_transform_mode(self) -> u32 { self.0.bits(30,32) } +} diff --git a/src/nitro/tex/texture.rs b/src/nitro/tex/texture.rs new file mode 100644 index 0000000..de5e0ad --- /dev/null +++ b/src/nitro/tex/texture.rs @@ -0,0 +1,81 @@ +use errors::Result; +use nitro::info_block; +use nitro::tex::PaletteInfo; +use nitro::tex::Tex; +use nitro::tex::TextureInfo; +use nitro::tex::TextureParameters; +use util::cur::Cur; + +pub fn read_tex(cur: Cur) -> Result { + fields!(cur, tex0 { + stamp: [u8; 4], + section_size: u32, + padding: u32, + texture_data_size_shr_3: u16, + texture_info_off: u16, + padding: u32, + texture_data_off: u32, + padding: u32, + compressed_texture_data_size_shr_3: u16, + compressed_texture_info_off: u16, + padding: u32, + compressed_texture_data_off: u32, + compressed_texture_extra_off: u32, + padding: u32, + palette_data_size_shr_3: u16, + unknown: u16, + palette_info_off: u32, + palette_data_off: u32, + }); + check!(stamp == &b"TEX0"[..]); + + let texture_data_size = (texture_data_size_shr_3 as usize) << 3; + let compressed_texture_data_size = (compressed_texture_data_size_shr_3 as usize) << 3; + let compressed_texture_extra_size = compressed_texture_data_size / 2; + let palette_data_size = (palette_data_size_shr_3 as usize) << 3; + + let texinfo = read_tex_info((cur + texture_info_off as usize)?)?; + let palinfo = read_pal_info((cur + palette_info_off as usize)?)?; + + let texture_data = (cur + texture_data_off as usize)? + .next_n_u8s(texture_data_size)?; + let compressed_texture_data = (cur + compressed_texture_data_off as usize)? + .next_n_u8s(compressed_texture_data_size)?; + let compressed_texture_extra_data = (cur + compressed_texture_extra_off as usize)? + .next_n_u8s(compressed_texture_extra_size)?; + let palette_data = (cur + palette_data_off as usize)? + .next_n_u8s(palette_data_size)?; + + Ok(Tex { + texinfo: texinfo, + palinfo: palinfo, + texture_data: texture_data, + compressed_texture_data: compressed_texture_data, + compressed_texture_extra_data: compressed_texture_extra_data, + palette_data: palette_data, + }) +} + +fn read_pal_info(cur: Cur) -> Result> { + Ok(info_block::read::<(u16, u16)>(cur)? + .map(|((off_shr_3, _), name)| { + PaletteInfo { + name: name, + off: (off_shr_3 as usize) << 3, + } + }) + .collect() + ) +} + +fn read_tex_info(cur: Cur) -> Result> { + Ok(info_block::read::<(u32, u32)>(cur)? + .map(|((params, _), name)| { + TextureInfo { + name: name, + params: TextureParameters(params), + } + }) + .collect() + ) +} diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..1bb28bc --- /dev/null +++ b/src/render.rs @@ -0,0 +1,107 @@ +use errors::Result; +use gfx::GfxState; +use nitro::mdl::Object; +use util::cur::Cur; + +pub trait Sink { + fn draw(&mut self, gs: &mut GfxState, mesh_id: u8, material_id: u8) -> Result<()>; +} + +pub struct Renderer { + pub gfx_state: GfxState, + pub cur_material: u8, + pub cur_stack_ptr: u8, +} + +impl Renderer { + pub fn new() -> Renderer { + Renderer { + gfx_state: GfxState::new(), + cur_material: 0, + cur_stack_ptr: 0, + } + } + + pub fn run_render_cmds(&mut self, sink: &mut S, objects: &[Object], mut cur: Cur) -> Result<()> { + loop { + let opcode = cur.next::()?; + let num_params = cmd_size(opcode, cur)?; + let params = cur.next_n_u8s(num_params)?; + trace!("{:#2x} {:?}", opcode, params); + match opcode { + 0x00 => { /* NOP */ } + 0x01 => { return Ok(()) } + 0x02 => { + // unknown + } + 0x03 => { + self.gfx_state.restore(params[0] as u32); + } + 0x04 | 0x24 | 0x44 => { + self.cur_material = params[0]; + } + 0x05 => { + sink.draw(&mut self.gfx_state, params[0], self.cur_material)?; + } + 0x06 | 0x26 | 0x46 | 0x66 => { + let object_id = params[0]; + let _parent_id = params[1]; + let _dummy = params[2]; + let (stack_id, restore_id) = match opcode { + 0x06 => (None, None), + 0x26 => (Some(params[3]), None), + 0x46 => (None, Some(params[3])), + 0x66 => (Some(params[3]), Some(params[4])), + _ => unreachable!(), + }; + + if let Some(restore_id) = restore_id { + self.gfx_state.restore(restore_id as u32); + } + self.gfx_state.cur_mat = self.gfx_state.cur_mat * objects[object_id as usize].xform; + if let Some(stack_id) = stack_id { + self.cur_stack_ptr = stack_id; + } + self.gfx_state.mat_stack[self.cur_stack_ptr as usize] = self.gfx_state.cur_mat; + self.cur_stack_ptr += 1; + } + 0x09 => { + // unknown + } + _ => { + trace!("unknown render command: {:#x} {:?}", opcode, params); + } + } + } + } +} + +fn cmd_size(opcode: u8, cur: Cur) -> Result { + let len = match opcode { + 0x00 => 0, + 0x01 => 0, + 0x02 => 2, + 0x03 => 1, + 0x04 => 1, + 0x05 => 1, + 0x06 => 3, + 0x07 => 1, + 0x08 => 1, + 0x09 => { + // The only variable-length command. + // 1 byte + 1 byte (count) + count u8[3]s + 2 + 3 * cur.clone().next_n_u8s(2)?[1] as usize + } + 0x0b => 0, + 0x24 => 1, + 0x26 => 4, + 0x2b => 0, + 0x40 => 0, + 0x44 => 1, + 0x46 => 4, + 0x66 => 5, + 0x80 => 0, + _ => return Err(format!("unknown render command opcode: {:#x}", opcode).into()), + }; + Ok(len) +} diff --git a/src/util/bits.rs b/src/util/bits.rs new file mode 100644 index 0000000..6f53a7b --- /dev/null +++ b/src/util/bits.rs @@ -0,0 +1,20 @@ +pub trait BitField { + fn bits(self, lo: u32, hi: u32) -> Self; +} + +macro_rules! def_bitfield { + ($t:ty, $bitwidth:expr) => { + impl BitField for $t { + #[inline(always)] + fn bits(self, lo: u32, hi: u32) -> $t { + assert!(lo <= hi); + assert!(hi <= $bitwidth); + (self >> lo) & (!0 >> ($bitwidth - (hi - lo) as $t)) + } + } + } +} + +def_bitfield!(u8, 8); +def_bitfield!(u16, 16); +def_bitfield!(u32, 32); diff --git a/src/util/cur.rs b/src/util/cur.rs new file mode 100644 index 0000000..9061f16 --- /dev/null +++ b/src/util/cur.rs @@ -0,0 +1,71 @@ +use errors::Result; +use std::fmt; +use std::ops::Add; +use util::view::{View, Viewable}; + +#[derive(Copy, Clone)] +pub struct Cur<'a> { + buf_: &'a [u8], + pos_: usize, +} + +impl<'a> Cur<'a> { + pub fn new(buf: &[u8]) -> Cur { + Cur { buf_: buf, pos_: 0 } + } + + pub fn pos(&self) -> usize { + self.pos_ + } + + pub fn next(&mut self) -> Result { + Ok(self.next_n::(1)?.get(0)) + } + + pub fn next_n(&mut self, n: usize) -> Result> { + let size = ::size(); + let buf = self.next_n_u8s(size * n)?; + Ok(View::from_buf(buf)) + } + + pub fn next_n_u8s(&mut self, n: usize) -> Result<&'a [u8]> { + let end_pos = self.pos_ + n; + if end_pos > self.buf_.len() { + return Err("buffer was too short".into()); + } + let res = &self.buf_[self.pos_ .. self.pos_ + n]; + self.pos_ += n; + Ok(res) + } + + pub fn jump_forward(&mut self, amt: usize) -> Result<()> { + let pos = self.pos_; + self.jump_to(pos + amt)?; + Ok(()) + } + + pub fn jump_to(&mut self, pos: usize) -> Result<()> { + if pos > self.buf_.len() { + return Err("jumped past end".into()); + } + self.pos_ = pos; + Ok(()) + } +} + +impl<'a> Add for Cur<'a> { + type Output = Result>; + + fn add(self, amt: usize) -> Result> { + let mut cur = self; + cur.jump_forward(amt)?; + Ok(cur) + } +} + +impl<'a> fmt::Debug for Cur<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Cur {{ pos: {} }}", self.pos()) + } +} + diff --git a/src/util/fields.rs b/src/util/fields.rs new file mode 100644 index 0000000..db0128c --- /dev/null +++ b/src/util/fields.rs @@ -0,0 +1,44 @@ +macro_rules! field_helper2 { + ($cur:ident, [u8; $n:expr]) => { $cur.next_n_u8s($n as usize)? }; + ($cur:ident, [$t:ty; $n:expr]) => { $cur.next_n::<$t>($n as usize)? }; + ($cur:ident, (fix16($s:expr,$i:expr,$f:expr))) => { + { + let x = $cur.next::()?; + ::util::fixed::fix16(x, $s, $i, $f) + } + }; + ($cur:ident, (fix32($s:expr,$i:expr,$f:expr))) => { + { + let x = $cur.next::()?; + ::util::fixed::fix32(x, $s, $i, $f) + } + }; + ($cur:ident, Cur) => { $cur.clone() }; + ($cur:ident, $t:ty) => { $cur.next::<$t>()? }; +} + +macro_rules! field_helper { + ($c:ident, $name:ident, $field:ident, Cur) => { + let $field = field_helper2!($c, Cur); + }; + ($c:ident, $name:ident, $field:ident, $ty:tt) => { + let pos = $c.pos(); + let $field = field_helper2!($c, $ty); + trace!("{}.{}@{:#x}: {:?}", + stringify!($name), + stringify!($field), + pos, + $field, + ); + } +} + +macro_rules! fields { + ($cur:expr, $name:ident { $($field:ident : $ty:tt,)* }) => { + let mut c = $cur; + $(field_helper!(c, $name, $field, $ty);)* + }; + ($cur:ident, $name:ident { $($field:ident : $ty:tt),* }) => { + fields!($cur, $name { $($field : $ty,)* }); + }; +} diff --git a/src/util/fixed.rs b/src/util/fixed.rs new file mode 100644 index 0000000..d065961 --- /dev/null +++ b/src/util/fixed.rs @@ -0,0 +1,25 @@ +use util::bits::BitField; + +pub fn fix16(x: u16, sign_bits: u32, int_bits: u32, frac_bits: u32) -> f64 { + assert!(sign_bits + int_bits + frac_bits <= 16); + fix32(x as u32, sign_bits, int_bits, frac_bits) +} + +pub fn fix32(x: u32, sign_bits: u32, int_bits: u32, frac_bits: u32) -> f64 { + assert!(sign_bits <= 1); + assert!(int_bits + frac_bits > 0); + assert!(sign_bits + int_bits + frac_bits <= 32); + let x = x.bits(0, sign_bits + int_bits + frac_bits); + let y = if sign_bits == 0 { + x as f64 + } else { + // sign extend + let sign_mask = (1 << (int_bits + frac_bits)) as u32; + if x & sign_mask != 0 { + (x | !(sign_mask - 1)) as i32 as f64 + } else { + x as f64 + } + }; + y * 0.5f64.powi(frac_bits as i32) +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..94e779c --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,7 @@ +pub mod bits; +pub mod cur; +#[macro_use] +pub mod fields; +pub mod fixed; +pub mod name; +pub mod view; diff --git a/src/util/name.rs b/src/util/name.rs new file mode 100644 index 0000000..df5c8e9 --- /dev/null +++ b/src/util/name.rs @@ -0,0 +1,51 @@ +use std::fmt; +use std::fmt::Write; +use util::view::Viewable; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Name(pub [u8; 16]); + +impl Name { + pub fn from_bytes(buf: &[u8]) -> Name { + let mut arr = [0; 16]; + for (i, &b) in buf.iter().enumerate() { + arr[i] = b; + } + Name(arr) + } +} + +impl Viewable for Name { + fn size() -> usize { 16 } + fn view(buf: &[u8]) -> Name { + Name::from_bytes(buf) + } +} + +impl fmt::Display for Name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for &b in trim_trailing_nul(&self.0[..]) { + f.write_char(if b < 0x20 { '.' } else { b as char })?; + } + Ok(()) + } +} + +impl fmt::Debug for Name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_char('"')?; + for &b in trim_trailing_nul(&self.0[..]) { + for c in (b as char).escape_default() { + f.write_char(c)?; + } + } + f.write_char('"') + } +} + +fn trim_trailing_nul(mut buf: &[u8]) -> &[u8] { + while let Some((&0, rest)) = buf.split_last() { + buf = rest; + } + buf +} diff --git a/src/util/view.rs b/src/util/view.rs new file mode 100644 index 0000000..f6d23f4 --- /dev/null +++ b/src/util/view.rs @@ -0,0 +1,136 @@ +use std::fmt; +use std::fmt::Debug; +use std::fmt::Formatter; +use std::fmt::Write; +use std::iter::Iterator; +use std::marker::PhantomData; + +/// Types that can be viewed as a fixed-length byte sequence. +pub trait Viewable: Sized { + fn size() -> usize; + fn view(buf: &[u8]) -> Self; +} + +impl Viewable for u8 { + fn size() -> usize { 1 } + fn view(buf: &[u8]) -> u8 { buf[0] } +} + +impl Viewable for u16 { + fn size() -> usize { 2 } + fn view(buf: &[u8]) -> u16 { + buf[0] as u16 | (buf[1] as u16) << 8 + } +} + +impl Viewable for u32 { + fn size() -> usize { 4 } + fn view(buf: &[u8]) -> u32 { + buf[0] as u32 | (buf[1] as u32) << 8 | (buf[2] as u32) << 16 | (buf[3] as u32) << 24 + } +} + +impl Viewable for (T,S) where + T: Viewable, + S: Viewable +{ + fn size() -> usize { ::size() + ::size() } + fn view(buf: &[u8]) -> (T,S) { + let split = ::size(); + let t = ::view(&buf[..split]); + let s = ::view(&buf[split..]); + (t,s) + } +} + +impl Viewable for (T,S,P) where + T: Viewable, + S: Viewable, + P: Viewable, +{ + fn size() -> usize { <(T,(S,P)) as Viewable>::size() } + fn view(buf: &[u8]) -> (T,S,P) { + let (t,(s,p)) = <(T,(S,P)) as Viewable>::view(buf); + (t,s,p) + } +} + +/// An byte buffer interpreted as an array of Viewable elements. +#[derive(Copy, Clone)] +pub struct View<'a, T> { + buf: &'a [u8], + _marker: PhantomData<*const T>, // TODO: Is this the right type? +} + +impl<'a, T: Viewable> View<'a, T> { + pub fn from_buf(buf: &[u8]) -> View { + let size = ::size(); + assert!(size == 0 || buf.len() % size == 0); + View { buf: buf, _marker: PhantomData } + } + + pub fn len(&self) -> usize { + let size = ::size(); + self.buf.len() / size + } + + pub fn get(&self, pos: usize) -> T { + let size = ::size(); + let begin = size * pos; + let end = begin + size; + if end > self.buf.len() { + panic!("index {} out of range for view of length {}", pos, self.buf.len()); + } + let bytes = &self.buf[begin..end]; + ::view(bytes) + } +} + +impl<'a, T: Viewable + Debug> Debug for View<'a, T> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "View [")?; + if self.len() != 0 { + write!(f, "{:?}", self.get(0))?; + for i in 1..self.len() { + write!(f, ", {:?}", self.get(i))?; + } + } + f.write_char(']') + } +} + +impl<'a, T: Viewable> Iterator for View<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + if self.buf.len() == 0 { + None + } else { + let size = ::size(); + let item = ::view(&self.buf[0..size]); + self.buf = &self.buf[size..]; + Some(item) + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, T: Viewable> DoubleEndedIterator for View<'a, T> { + fn next_back(&mut self) -> Option { + if self.buf.len() == 0 { + None + } else { + let size = ::size(); + let idx = self.buf.len() - size; + let item = ::view(&self.buf[idx..]); + self.buf = &self.buf[..idx]; + Some(item) + } + } +} + +impl<'a, T: Viewable> ExactSizeIterator for View<'a, T> {} diff --git a/src/viewer.rs b/src/viewer.rs new file mode 100644 index 0000000..4d1df64 --- /dev/null +++ b/src/viewer.rs @@ -0,0 +1,308 @@ +use cgmath::EuclideanSpace; +use cgmath::InnerSpace; +use cgmath::Matrix4; +use cgmath::PerspectiveFov; +use cgmath::Point3; +use cgmath::Rad; +use cgmath::Transform; +use cgmath::vec3; +use errors::Result; +use geometry; +use geometry::Vertex; +use gfx::GfxState; +use glium; +use nitro::mdl::Model; +use nitro::tex::image::gen_image; +use nitro::tex::Tex; +use render; +use std::f32::consts::PI; +use time; + +implement_vertex!(Vertex, position, texcoord, color); + +struct Sink<'a, 'b: 'a> { + geosink: geometry::Sink, + model: &'a Model<'b>, +} + +impl<'a, 'b: 'a> render::Sink for Sink<'a, 'b> { + fn draw(&mut self, gs: &mut GfxState, mesh_id: u8, material_id: u8) -> Result<()> { + let material = &self.model.materials[material_id as usize]; + gs.texture_mat = material.texture_mat; + self.geosink.begin_mesh(material_id); + self.geosink.cur_texture_dim = (material.width as u32, material.height as u32); + gs.run_commands(&mut self.geosink, self.model.meshes[mesh_id as usize].commands)?; + self.geosink.end_mesh(); + Ok(()) + } +} + +struct Eye { + position: Point3, + azimuth: f32, + altitude: f32, +} + +struct MouseState { + pos: (i32, i32), + grabbed: GrabState, +} + +enum GrabState { + NotGrabbed, + Grabbed { saved_pos: (i32, i32) }, +} + +pub fn viewer(model: &Model, tex: &Tex) -> Result<()> { + use glium::{DisplayBuild, Surface}; + let display = glium::glutin::WindowBuilder::new() + .with_dimensions(512,384) + .with_depth_buffer(24) + .build_glium() + .unwrap(); + + let mut s = Sink { + geosink: geometry::Sink::new(), + model: model, + }; + let mut r = render::Renderer::new(); + r.run_render_cmds(&mut s, &model.objects[..], model.render_cmds_cur)?; + let geom = s.geosink.data; + //println!("{:#?}", geom.vertices); + let vertex_buffer = glium::VertexBuffer::new( + &display, + &geom.vertices + ).unwrap(); + let indices = glium::IndexBuffer::new( + &display, + glium::index::PrimitiveType::TrianglesList, + &geom.indices + ).unwrap(); + + // Build textures + let textures = model.materials.iter() + .map(|material| { + let palinfo = material.palette_name + .and_then(|name| tex.palinfo.iter().find(|info| info.name == name)); + let texinfo = material.texture_name + .and_then(|name| tex.texinfo.iter().find(|info| info.name == name)); + texinfo.map(|texinfo| { + let rgba = gen_image(tex, texinfo, palinfo).unwrap(); + let dim = (texinfo.params.width(), texinfo.params.height()); + let image = glium::texture::RawImage2d::from_raw_rgba_reversed(rgba, dim); + glium::texture::Texture2d::new(&display, image).unwrap() + }) + }) + .collect::>(); + // The default image is just a 1x1 white texture. Or it should be. For some reason + // I don't understand, on the Windows box I'm testing on, 1x1 textures don't seem + // to work. The work-around is just to make it 2x1. :( + let default_image = glium::texture::RawImage2d::from_raw_rgba( + vec![255u8,255,255,255,255u8,255,255,255], + (2,1) + ); + let default_texture = glium::texture::Texture2d::new(&display, default_image).unwrap(); + + let vertex_shader_src = r#" + #version 140 + in vec3 position; + in vec2 texcoord; + in vec3 color; + out vec2 v_texcoord; + out vec3 v_color; + uniform mat4 matrix; + void main() { + v_texcoord = texcoord; + v_color = color; + gl_Position = matrix * vec4(position, 1.0); + } + "#; + + let fragment_shader_src = r#" + #version 140 + in vec2 v_texcoord; + in vec3 v_color; + out vec4 color; + uniform sampler2D tex; + void main() { + vec4 sample = texture(tex, v_texcoord); + if (sample.w == 0.0) discard; + color = sample * vec4(v_color, 1.0); + } + "#; + + let program = glium::Program::new(&display, + glium::program::ProgramCreationInput::SourceCode { + vertex_shader: vertex_shader_src, + fragment_shader: fragment_shader_src, + geometry_shader: None, + tessellation_control_shader: None, + tessellation_evaluation_shader: None, + transform_feedback_varyings: None, + outputs_srgb: true, + uses_point_size: false, + } + ).unwrap(); + + let mut eye = Eye { + position: Point3::new(0.0, 0.0, 0.0), + azimuth: 0.0, + altitude: 0.0, + }; + + let mut mouse = MouseState { + pos: (0,0), + grabbed: GrabState::NotGrabbed, + }; + + let mut t = vec3(0.0, 0.0, 0.0); + let mut last_time = time::precise_time_s() as f32; + loop { + let (w, h) = display.get_window().unwrap() + .get_inner_size_pixels().unwrap(); + let (w,h) = (w as i32, h as i32); + let asp = w as f32 / h as f32; + + let mut target = display.draw(); + + let middle_grey = (0.4666, 0.4666, 0.4666, 1.0); + target.clear_color_srgb_and_depth(middle_grey, 1.0); + + let drawparams = glium::DrawParameters { + depth: glium::Depth { + test: glium::draw_parameters::DepthTest::IfLess, + write: true, + .. Default::default() + }, + backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise, + .. Default::default() + }; + + let persp = PerspectiveFov { + fovy: Rad(1.1), + aspect: asp, + near: 0.01, + far: 100.0, + }; + let mat = + Matrix4::from(persp) * + Matrix4::from_angle_x(Rad(-eye.altitude)) * + Matrix4::from_angle_y(Rad(-eye.azimuth)) * + Matrix4::from_translation(-eye.position.to_vec()); + + for range in geom.mesh_ranges.iter() { + let tx = textures[range.mat_id as usize].as_ref() + .unwrap_or(&default_texture); + let mut sampler = glium::uniforms::Sampler::new(tx) + .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest) + .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest); + let wrap_fn = |repeat, mirror| { + use glium::uniforms::SamplerWrapFunction as Wrap; + match (repeat, mirror) { + (false, _) => Wrap::Clamp, + (true, false) => Wrap::Repeat, + (true, true) => Wrap::Mirror, + } + }; + let params = model.materials[range.mat_id as usize].params; + sampler.1.wrap_function.0 = wrap_fn(params.repeat_s(), params.mirror_s()); + sampler.1.wrap_function.1 = wrap_fn(params.repeat_t(), params.mirror_t()); + + let uniforms = uniform! { + matrix: [ + [mat.x.x, mat.x.y, mat.x.z, mat.x.w], + [mat.y.x, mat.y.y, mat.y.z, mat.y.w], + [mat.z.x, mat.z.y, mat.z.z, mat.z.w], + [mat.w.x, mat.w.y, mat.w.z, mat.w.w], + ], + tex: sampler, + }; + target.draw( + &vertex_buffer, + &indices.slice(range.index_range.clone()).unwrap(), + &program, + &uniforms, + &drawparams, + ).unwrap(); + } + + target.finish().unwrap(); + + let xform = + Matrix4::from_angle_y(Rad(eye.azimuth)); + + let forward = xform.transform_vector(vec3(0.0, 0.0, -1.0)); + let side = xform.transform_vector(vec3(1.0, 0.0, 0.0)); + let up = xform.transform_vector(vec3(0.0, 1.0, 0.0)); + + for ev in display.poll_events() { + use glium::glutin::Event as Ev; + use glium::glutin::ElementState as State; + use glium::glutin::VirtualKeyCode as Key; + match ev { + Ev::Closed => return Ok(()), + Ev::KeyboardInput(State::Pressed, _, Some(Key::S)) => t.x = -1.0, + Ev::KeyboardInput(State::Pressed, _, Some(Key::W)) => t.x = 1.0, + Ev::KeyboardInput(State::Pressed, _, Some(Key::A)) => t.y = -1.0, + Ev::KeyboardInput(State::Pressed, _, Some(Key::D)) => t.y = 1.0, + Ev::KeyboardInput(State::Pressed, _, Some(Key::F)) => t.z = -1.0, + Ev::KeyboardInput(State::Pressed, _, Some(Key::R)) => t.z = 1.0, + + Ev::KeyboardInput(State::Released, _, Some(Key::S)) => t.x = 0.0, + Ev::KeyboardInput(State::Released, _, Some(Key::W)) => t.x = 0.0, + Ev::KeyboardInput(State::Released, _, Some(Key::A)) => t.y = 0.0, + Ev::KeyboardInput(State::Released, _, Some(Key::D)) => t.y = 0.0, + Ev::KeyboardInput(State::Released, _, Some(Key::F)) => t.z = 0.0, + Ev::KeyboardInput(State::Released, _, Some(Key::R)) => t.z = 0.0, + + Ev::MouseInput(State::Pressed, glium::glutin::MouseButton::Left) => { + mouse.grabbed = GrabState::Grabbed { saved_pos: mouse.pos }; + display.get_window().unwrap().set_cursor_position(w/2, h/2); + display.get_window().unwrap().set_cursor_state(glium::glutin::CursorState::Hide)?; + } + Ev::MouseInput(State::Released, glium::glutin::MouseButton::Left) => { + if let GrabState::Grabbed { saved_pos } = mouse.grabbed { + display.get_window().unwrap().set_cursor_state(glium::glutin::CursorState::Normal)?; + display.get_window().unwrap().set_cursor_position(saved_pos.0, saved_pos.1); + } + mouse.grabbed = GrabState::NotGrabbed; + } + Ev::Focused(false) => { + display.get_window().unwrap().set_cursor_state(glium::glutin::CursorState::Normal)?; + mouse.grabbed = GrabState::NotGrabbed; + t = vec3(0.0, 0.0, 0.0); + } + Ev::MouseMoved(x,y) => { + mouse.pos = (x,y); + if let GrabState::Grabbed { .. } = mouse.grabbed { + let (dx, dy) = (x - w/2, y - h/2); + eye.azimuth -= 0.01 * dx as f32; + eye.altitude -= 0.01 * dy as f32; + if eye.azimuth >= 2.0 * PI { + eye.azimuth -= 2.0*PI; + } else if eye.azimuth < 0.0 { + eye.azimuth += 2.0*PI; + } + if eye.altitude > 0.498*PI { + eye.altitude = 0.498*PI; + } + if eye.altitude < -0.498*PI { + eye.altitude = -0.498*PI; + } + display.get_window().unwrap().set_cursor_position(w/2, h/2); + } + } + _ => () + } + } + let cur_time = time::precise_time_s() as f32; + let dt = cur_time - last_time; + last_time = cur_time; + + let mag = t.magnitude(); + if mag != 0.0 { + let tt = t / mag; + eye.position += &(&forward * tt.x + &side * tt.y + &up * tt.z) * dt; + } + } +}