Skip to content

Commit

Permalink
add minecraft format model parsers copy from minecraft_assets
Browse files Browse the repository at this point in the history
  • Loading branch information
BreakingLead committed Aug 27, 2024
1 parent 12c1580 commit afb5e11
Show file tree
Hide file tree
Showing 23 changed files with 2,912 additions and 54 deletions.
4 changes: 4 additions & 0 deletions blockworld-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
maplit = "1.0.2"
serde = "1.0.209"
serde_json = "1.0.127"
thiserror = "1.0.63"
4 changes: 4 additions & 0 deletions blockworld-utils/src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# README

I used part of [minecraft_assets](https://github.com/BGR360/minecraft-assets-rs) from BGR360 to create this crate. License: MIT or Apache-2.0.

1 change: 0 additions & 1 deletion blockworld-utils/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
pub const DEFAULT_RESOURCE_LOCATION_DOMAIN: &str = "blockworld";
pub const GAME_NAME: &str = "blockworld";
10 changes: 4 additions & 6 deletions blockworld-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ use std::{
sync::{Arc, Mutex},
};

pub mod constants;
pub mod registry;
pub mod resource_key;
pub mod resource_location;
pub mod text;
mod constants;
mod registry;
mod resource;

pub use resource_location::ResourceLocation;
pub use resource::resource_location::ResourceLocation;

pub type AM<T> = Arc<Mutex<T>>;
pub type RR<T> = Rc<RefCell<T>>;
Expand Down
8 changes: 7 additions & 1 deletion blockworld-utils/src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use std::collections::HashMap;

use crate::resource_location::ResourceLocation;
use crate::ResourceLocation;

pub struct Registry<V> {
data: HashMap<ResourceLocation, V>,
}

impl<V> Default for Registry<V> {
fn default() -> Self {
Self::new()
}
}

impl<V> Registry<V> {
pub fn new() -> Self {
Self {
Expand Down
203 changes: 203 additions & 0 deletions blockworld-utils/src/resource/api/asset_pack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use std::{ops::Deref, path::Path};

use serde::de::DeserializeOwned;

use crate::resource::{
api::{
FileSystemResourceProvider, ModelIdentifier, ResourceIdentifier, ResourceProvider, Result,
},
schemas::{BlockStates, Model},
};

/// Top-level API for accessing Minecraft assets.
pub struct AssetPack {
provider: Box<dyn ResourceProvider>,
}

impl AssetPack {
/// Returns a new [`AssetPack`] that can read data from the given directory.
///
/// The provided `root_dir` should be the directory that contains the
/// `assets/` and/or `data/` directories.
///
/// # Example
///
/// ```no_run
/// use minecraft_assets::api::AssetPack;
///
/// let assets = AssetPack::at_path("~/.minecraft/");
///
/// // Load the block states for `oak_planks`
/// let states = assets.load_blockstates("oak_planks").unwrap();
/// let variants = states.variants().unwrap();
///
/// assert_eq!(variants.len(), 1);
///
/// let model_properties = &variants[""].models()[0];
/// assert_eq!(model_properties.model, "block/oak_planks");
/// ```
pub fn at_path(root_dir: impl AsRef<Path>) -> Self {
let provider = FileSystemResourceProvider::new(root_dir);
Self {
provider: Box::new(provider),
}
}

/// Returns a new [`AssetPack`] that uses the given [`ResourceProvider`].
pub fn new<P>(provider: P) -> Self
where
P: ResourceProvider + 'static,
{
Self {
provider: Box::new(provider),
}
}

/// Loads the [`BlockStates`] of the block with the provided id.
///
/// # Example
///
/// ```no_run
/// # use minecraft_assets::api::*;
/// # let assets = AssetPack::at_path("foo");
/// let states = assets.load_blockstates("stone");
/// let states = assets.load_blockstates("minecraft:dirt");
/// ```
pub fn load_blockstates(&self, block_id: &str) -> Result<BlockStates> {
self.load_resource(&ResourceIdentifier::blockstates(block_id))
}

/// Loads the block [`Model`] identified by the given name or path.
///
/// # Example
///
/// ```no_run
/// # use minecraft_assets::api::*;
/// # let assets = AssetPack::at_path("foo");
/// let model = assets.load_block_model("stone");
/// let model = assets.load_block_model("block/dirt");
/// ```
pub fn load_block_model(&self, model: &str) -> Result<Model> {
self.load_resource(&ResourceIdentifier::block_model(model))
}

/// Loads the block [`Model`] identified by the given name or path, as well
/// as all of its parents and ancestors.
///
/// The models are returned as a list, with the first element being the
/// model that was originally requested, the next element being its parent,
/// and so on with the last element being the topmost parent.
///
/// # Example
///
/// ```no_run
/// # use minecraft_assets::api::*;
/// # let assets = AssetPack::at_path("foo");
/// let models = assets.load_block_model_recursive("block/cube_all").unwrap();
///
/// let expected = vec![
/// assets.load_block_model("block/cube_all").unwrap(),
/// assets.load_block_model("block/cube").unwrap(),
/// assets.load_block_model("block/block").unwrap(),
/// ];
/// assert_eq!(models, expected);
/// ```
pub fn load_block_model_recursive(&self, model: &str) -> Result<Vec<Model>> {
self.load_model_recursive(&ResourceIdentifier::block_model(model))
}

/// Loads the item [`Model`] identified by the given name or path.
///
/// # Example
///
/// ```no_run
/// # use minecraft_assets::api::*;
/// # let assets = AssetPack::at_path("foo");
/// let model = assets.load_item_model("compass");
/// let model = assets.load_item_model("item/diamond_hoe");
/// ```
pub fn load_item_model(&self, model: &str) -> Result<Model> {
self.load_resource(&ResourceIdentifier::item_model(model))
}

/// Loads the item [`Model`] identified by the given name or path, as well
/// as all of its parents and ancestors.
///
/// The models are returned as a list, with the first element being the
/// model that was originally requested, the next element being its parent,
/// and so on with the last element being the topmost parent.
///
/// # Example
///
/// ```no_run
/// # use minecraft_assets::api::*;
/// # let assets = AssetPack::at_path("foo");
/// let models = assets.load_item_model_recursive("item/diamond_hoe").unwrap();
///
/// let expected = vec![
/// assets.load_item_model("item/diamond_hoe").unwrap(),
/// assets.load_item_model("item/handheld").unwrap(),
/// assets.load_item_model("item/generated").unwrap(),
/// ];
/// assert_eq!(models, expected);
/// ```
pub fn load_item_model_recursive(&self, model: &str) -> Result<Vec<Model>> {
self.load_model_recursive(&ResourceIdentifier::item_model(model))
}

fn load_resource<T>(&self, resource: &ResourceIdentifier) -> Result<T>
where
T: DeserializeOwned,
{
let bytes = self.provider.load_resource(resource)?;
Ok(serde_json::from_reader(&bytes[..])?)
}

fn load_model_recursive(&self, resource: &ResourceIdentifier) -> Result<Vec<Model>> {
let mut models = Vec::new();

Self::for_each_parent(
resource.clone(),
|model| models.push(model),
|next_id| self.load_resource(next_id),
)?;

Ok(models)
}

pub(crate) fn for_each_parent<F, L, E>(
mut current: ResourceIdentifier,
mut op: F,
mut load_model: L,
) -> Result<(), E>
where
F: FnMut(Model),
L: FnMut(&ResourceIdentifier) -> Result<Model, E>,
{
loop {
let model = load_model(&current)?;

let parent_owned = model.parent.clone();

op(model);

match parent_owned {
Some(parent) if !ModelIdentifier::is_builtin(&parent) => {
//println!("{}", parent.as_str());
current = ResourceIdentifier::new_owned(current.kind(), parent);
}
_ => break,
}
}

Ok(())
}
}

impl Deref for AssetPack {
type Target = dyn ResourceProvider;

fn deref(&self) -> &Self::Target {
&*self.provider
}
}
48 changes: 48 additions & 0 deletions blockworld-utils/src/resource/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! An API for programmatically accessing Minecraft resources and associated
//! metadata.
//!
//! ## Resource Identifiers
//!
//! Every resource is associated with a unique [`ResourceIdentifier`], which is a
//! combination of a [`ResourceKind`] and a *namespaced identifier*.
//!
//! ## Providers
//!
//! Resources can be enumerated and loaded using the [`ResourceProvider`] trait.
//! This crate provides the [`FileSystemResourceProvider`] as a convenient
//! implementation of this trait.
//!
//! ## Asset Pack
//!
//! Resources can be ergonomically loaded through the [`AssetPack`] API.
use std::io;

mod asset_pack;
mod provider;
mod resolve;
mod resource;

pub use asset_pack::AssetPack;
pub use provider::{
EnumerateResources, FileSystemResourceProvider, LoadResource, ResourceProvider,
};
pub use resolve::ModelResolver;
pub use resource::{
ModelIdentifier, ResourceCategory, ResourceIdentifier, ResourceKind, ResourcePath,
MINECRAFT_NAMESPACE,
};

/// Error types that can be returned from API methods.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
IoError(#[from] io::Error),

#[error(transparent)]
ParseError(#[from] serde_json::Error),
}

/// Result alias for convenience.
pub type Result<T, E = Error> = std::result::Result<T, E>;
Loading

0 comments on commit afb5e11

Please sign in to comment.