Skip to content

Commit

Permalink
Add double-precision floating point feature use_f64
Browse files Browse the repository at this point in the history
Change float comparison tests to accomodate f64 option

Change use_double flag to use_f64

Fix formatting
  • Loading branch information
ipadjen committed Feb 2, 2023
1 parent 713dd4c commit 6acf62a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 77 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ merging = []
reordering = []
async = []
arbitrary = ["arbitrary/derive"]
use_f64 = []

[dependencies]
arbitrary = { version = "1.1.3", optional = true }
Expand All @@ -33,10 +34,11 @@ log = { version = "0.4.16", optional = true }

[dev-dependencies]
tokio-test = "0.4.2"
float_eq = "1.0.1"

[profile.dev]
split-debuginfo = "unpacked"
opt-level = 3

[package.metadata.docs.rs]
features = ["log", "merging", "reordering", "async"]
features = ["log", "merging", "reordering", "async", "use_f64"]
86 changes: 48 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//!
//! ## Flat Data
//!
//! Values are stored packed as [`f32`]s in flat `Vec`s.
//! Values are stored packed as [`f32`]s (or [`f64`]s with the use_f64 feature) in flat `Vec`s.
//!
//! For example, the `positions` member of a `Mesh` will contain `[x, y, z, x,
//! y, z, ...]` which you can then use however you like.
Expand Down Expand Up @@ -193,6 +193,9 @@
//! * [`async`](load_obj_buf_async) – Adds support for async loading of obj files from a buffer,
//! with an async material loader. Useful in environments that do not
//! support blocking IO (e.g. WebAssembly).
//!
//! * ['use_f64'] - Uses double-precision (f64) instead of single-precision (f32) floating point
//! types
#![cfg_attr(feature = "merging", allow(incomplete_features))]
#![cfg_attr(feature = "merging", feature(generic_const_exprs))]

Expand All @@ -208,6 +211,12 @@ use std::{
str::{FromStr, SplitWhitespace},
};

#[cfg(feature = "use_f64")]
type Float = f64;

#[cfg(not(feature = "use_f64"))]
type Float = f32;

#[cfg(feature = "async")]
use std::future::Future;

Expand Down Expand Up @@ -257,7 +266,7 @@ pub const OFFLINE_RENDERING_LOAD_OPTIONS: LoadOptions = LoadOptions {
/// It is assumed that all meshes will at least have positions, but normals and
/// texture coordinates are optional. If no normals or texture coordinates where
/// found then the corresponding `Vec`s in the `Mesh` will be empty. Values are
/// stored packed as [`f32`]s in flat `Vec`s.
/// stored packed as [`f32`]s (or [`f64`]s with the use_f64 feature) in flat `Vec`s.
///
/// For examples the `positions` member of a loaded mesh will contain `[x, y, z,
/// x, y, z, ...]` which you can then use however you like. Indices are also
Expand Down Expand Up @@ -307,25 +316,25 @@ pub const OFFLINE_RENDERING_LOAD_OPTIONS: LoadOptions = LoadOptions {
pub struct Mesh {
/// Flattened 3 component floating point vectors, storing positions of
/// vertices in the mesh.
pub positions: Vec<f32>,
pub positions: Vec<Float>,
/// Flattened 3 component floating point vectors, storing the color
/// associated with the vertices in the mesh.
///
/// Most meshes do not have vertex colors. If no vertex colors are specified
/// this will be empty.
pub vertex_color: Vec<f32>,
pub vertex_color: Vec<Float>,
/// Flattened 3 component floating point vectors, storing normals of
/// vertices in the mesh.
///
/// Not all meshes have normals. If no normals are specified this will
/// be empty.
pub normals: Vec<f32>,
pub normals: Vec<Float>,
/// Flattened 2 component floating point vectors, storing texture
/// coordinates of vertices in the mesh.
///
/// Not all meshes have texture coordinates. If no texture coordinates are
/// specified this will be empty.
pub texcoords: Vec<f32>,
pub texcoords: Vec<Float>,
/// Indices for vertices of each face. If loaded with
/// [`triangulate`](LoadOptions::triangulate) set to `true` each face in the
/// mesh is a triangle.
Expand Down Expand Up @@ -581,21 +590,21 @@ pub struct Material {
/// Material name as specified in the `MTL` file.
pub name: String,
/// Ambient color of the material.
pub ambient: [f32; 3],
pub ambient: [Float; 3],
/// Diffuse color of the material.
pub diffuse: [f32; 3],
pub diffuse: [Float; 3],
/// Specular color of the material.
pub specular: [f32; 3],
pub specular: [Float; 3],
/// Material shininess attribute. Also called `glossiness`.
pub shininess: f32,
pub shininess: Float,
/// Dissolve attribute is the alpha term for the material. Referred to as
/// dissolve since that's what the `MTL` file format docs refer to it as.
pub dissolve: f32,
pub dissolve: Float,
/// Optical density also known as index of refraction. Called
/// `optical_density` in the `MTL` specc. Takes on a value between 0.001
/// and 10.0. 1.0 means light does not bend as it passes through
/// the object.
pub optical_density: f32,
pub optical_density: Float,
/// Name of the ambient texture file for the material.
pub ambient_texture: String,
/// Name of the diffuse texture file for the material.
Expand Down Expand Up @@ -768,20 +777,20 @@ enum Face {

/// Parse the float information from the words. Words is an iterator over the
/// float strings. Returns `false` if parsing failed.
fn parse_floatn(val_str: &mut SplitWhitespace, vals: &mut Vec<f32>, n: usize) -> bool {
fn parse_floatn(val_str: &mut SplitWhitespace, vals: &mut Vec<Float>, n: usize) -> bool {
let sz = vals.len();
for p in val_str.take(n) {
match FromStr::from_str(p) {
Ok(x) => vals.push(x),
Err(_) => return false,
}
}
// Require that we found the desired number of f32s.
// Require that we found the desired number of floats.
sz + n == vals.len()
}

/// Parse the float3 into the array passed, returns false if parsing failed
fn parse_float3(val_str: SplitWhitespace, vals: &mut [f32; 3]) -> bool {
fn parse_float3(val_str: SplitWhitespace, vals: &mut [Float; 3]) -> bool {
for (i, p) in val_str.enumerate().take(3) {
match FromStr::from_str(p) {
Ok(x) => vals[i] = x,
Expand Down Expand Up @@ -829,10 +838,10 @@ fn add_vertex(
mesh: &mut Mesh,
index_map: &mut HashMap<VertexIndices, u32>,
vert: &VertexIndices,
pos: &[f32],
v_color: &[f32],
texcoord: &[f32],
normal: &[f32],
pos: &[Float],
v_color: &[Float],
texcoord: &[Float],
normal: &[Float],
) -> Result<(), LoadError> {
match index_map.get(vert) {
Some(&i) => mesh.indices.push(i),
Expand Down Expand Up @@ -881,10 +890,10 @@ fn add_vertex(
/// Export a list of faces to a mesh and return it, optionally converting quads
/// to tris.
fn export_faces(
pos: &[f32],
v_color: &[f32],
texcoord: &[f32],
normal: &[f32],
pos: &[Float],
v_color: &[Float],
texcoord: &[Float],
normal: &[Float],
faces: &[Face],
mat_id: Option<usize>,
load_options: &LoadOptions,
Expand Down Expand Up @@ -987,10 +996,10 @@ fn add_vertex_multi_index(
normal_index_map: &mut HashMap<usize, u32>,
texcoord_index_map: &mut HashMap<usize, u32>,
vert: &VertexIndices,
pos: &[f32],
v_color: &[f32],
texcoord: &[f32],
normal: &[f32],
pos: &[Float],
v_color: &[Float],
texcoord: &[Float],
normal: &[Float],
) -> Result<(), LoadError> {
match index_map.get(&vert.v) {
Some(&i) => mesh.indices.push(i),
Expand Down Expand Up @@ -1113,10 +1122,10 @@ fn add_vertex_multi_index(
/// Export a list of faces to a mesh and return it, optionally converting quads
/// to tris.
fn export_faces_multi_index(
pos: &[f32],
v_color: &[f32],
texcoord: &[f32],
normal: &[f32],
pos: &[Float],
v_color: &[Float],
texcoord: &[Float],
normal: &[Float],
faces: &[Face],
mat_id: Option<usize>,
load_options: &LoadOptions,
Expand Down Expand Up @@ -1500,26 +1509,27 @@ fn reorder_data(mesh: &mut Mesh) {
/// Merge identical points. A point has dimension N.
#[cfg(feature = "merging")]
#[inline]
fn merge_identical_points<const N: usize>(points: &mut Vec<f32>, indices: &mut Vec<u32>)
fn merge_identical_points<const N: usize>(points: &mut Vec<Float>, indices: &mut Vec<u32>)
where
[(); size_of::<[f32; N]>()]:,
[(); size_of::<[Float; N]>()]:,
{
if indices.is_empty() {
return;
}

let mut compressed_indices = Vec::new();
let mut canonical_indices = HashMap::<[u8; size_of::<[f32; N]>()], u32>::new();
let mut canonical_indices = HashMap::<[u8; size_of::<[Float; N]>()], u32>::new();

let mut index = 0;
*points = points
.chunks(N)
.filter_map(|position| {
let position: &[f32; N] = &unsafe { *(position.as_ptr() as *const [f32; N]) };
let position: &[Float; N] = &unsafe { *(position.as_ptr() as *const [Float; N]) };

// Ugly, but f32 has no Eq and no Hash.
let bitpattern =
unsafe { std::mem::transmute::<&[f32; N], &[u8; size_of::<[f32; N]>()]>(position) };
// Ugly, but floats have no Eq and no Hash.
let bitpattern = unsafe {
std::mem::transmute::<&[Float; N], &[u8; size_of::<[Float; N]>()]>(position)
};

match canonical_indices.get(bitpattern) {
Some(&other_index) => {
Expand Down
Loading

0 comments on commit 6acf62a

Please sign in to comment.