Browse Source

Initial commit

master
Thomas Forgione 8 months ago
parent
commit
44248e5555
Signed by: Thomas Forgione <thomas@forgione.fr> GPG Key ID: 203DAEA747F48F41

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
Cargo.lock
/target
**/*.rs.bk

+ 14
- 0
Cargo.toml View File

@@ -0,0 +1,14 @@
[package]
name = "model-converter"
version = "0.1.0"
authors = ["Thomas Forgione <thomas@forgione.fr>"]
edition = "2018"

[dependencies]
clap = "2.32.0"
log = "0.4.6"
stderrlog = "0.4.1"

[[bin]]
name = "3d-convert"
path = "src/convert.rs"

+ 63
- 0
assets/models/cube/cube.mtl View File

@@ -0,0 +1,63 @@
newmtl white
Ka 1.0 1.0 1.0

newmtl cubemtlred1
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 1.0 0.0 0.0
Ks 0.0 0.0 0.0
# map_Kd cube.png

newmtl cubemtlred2
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 0.8 0.2 0.2
Ks 0.0 0.0 0.0
# map_Kd cube.png

newmtl cubemtlgreen1
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 0.0 1.0 0.0
Ks 0.0 0.0 0.0
# map_Kd cube.png

newmtl cubemtlgreen2
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 0.2 0.8 0.2
Ks 0.0 0.0 0.0
# map_Kd cube.png

newmtl cubemtlblue1
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 0.0 0.0 1.0
Ks 0.0 0.0 0.0
# map_Kd cube.png

newmtl cubemtlblue2
Ns 10
Ni 1.0
d 1.0
Tf 1 1 1
illum 2
Kd 0.2 0.2 0.8
Ks 0.0 0.0 0.0
# map_Kd cube.png


+ 53
- 0
assets/models/cube/cube.obj View File

@@ -0,0 +1,53 @@
mtllib cube.mtl

v -0.5 -0.5 -0.5
v -0.5 -0.5 0.5
v -0.5 0.5 -0.5
v -0.5 0.5 0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v 0.5 0.5 -0.5
v 0.5 0.86 1.41

v 0.5 0.5 0.5
v 0.5 0.6 0.6
v 0.6 0.6 0.6

vt 0.0 0.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 1.0

vn 1.0 0.0 0.0
vn 0.0 1.0 0.0
vn 0.0 0.0 1.0
vn -1.0 0.0 0.0
vn 0.0 -1.0 0.0
vn 0.0 0.0 -1.0

usemtl cubemtlred2
f 1/1/4 2/3/4 4/4/4
f 1/1/4 4/4/4 3/2/4

usemtl cubemtlblue1
f 2/1/3 6/3/3 8/4/3
f 2/1/3 8/4/3 4/2/3

usemtl cubemtlred1
f 6/1/1 5/3/1 7/4/1
f 6/1/1 7/4/1 8/2/1

usemtl cubemtlblue2
f 5/1/6 1/3/6 3/4/6
f 5/1/6 3/4/6 7/2/6

usemtl cubemtlgreen1
f 4/1/2 8/3/2 7/4/2
f 4/1/2 7/4/2 3/2/2

usemtl cubemtlgreen2
f 2/1/5 1/3/5 5/4/5
f 2/1/5 5/4/5 6/2/5

usemtl white
f 9 10 11

+ 32
- 0
assets/models/cube/cube.ply View File

@@ -0,0 +1,32 @@
ply
format ascii 1.0
comment TextureFile cube.png
element vertex 8
property float x
property float y
property float z
element face 12
property list uchar int vertex_indices
property list uchar float texcoord
property int texnumber
end_header
-0.5 -0.5 -0.5
-0.5 -0.5 0.5
-0.5 0.5 -0.5
-0.5 0.5 0.5
0.5 -0.5 -0.5
0.5 -0.5 0.5
0.5 0.5 -0.5
0.5 0.5 0.5
3 0 1 3 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 0 3 2 6 0.0 0.0 1.0 1.0 0.0 1.0 0
3 1 5 7 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 1 7 3 6 0.0 0.0 1.0 1.0 0.0 1.0 0
3 5 4 6 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 5 6 7 6 0.0 0.0 1.0 1.0 0.0 1.0 0
3 4 0 2 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 4 2 6 6 0.0 0.0 1.0 1.0 0.0 1.0 0
3 3 7 6 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 3 6 2 6 0.0 0.0 1.0 1.0 0.0 1.0 0
3 1 0 4 6 0.0 0.0 1.0 0.0 1.0 1.0 0
3 1 4 5 6 0.0 0.0 1.0 1.0 0.0 1.0 0

BIN
assets/models/cube/cube.png View File


+ 50
- 0
src/convert.rs View File

@@ -0,0 +1,50 @@
#[macro_use]
extern crate log;

use std::process::exit;

use clap::{Arg, App};

use model_converter::parser::Parser;
use model_converter::parser::obj::ObjParser;
use model_converter::model::Model;

fn main() {
let matches = App::new("3D Model Converter")
.version("0.1.0")
.author("Thomas Forgione <thomas@forgione.fr>")
.about("Converts 3D models")
.arg(Arg::with_name("input")
.short("i")
.long("input")
.help("Input of the conversion")
.takes_value(true)
.value_name("FILE")
.required(true)
.multiple(true))
.arg(Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Display a more verbose output")
.takes_value(false)
.multiple(true))
.get_matches();

stderrlog::new()
.module(module_path!())
.verbosity(matches.occurrences_of("verbose") as usize)
.init()
.expect("Couldn't initialize logger");

let parser = ObjParser::new();
let mut model = Model::new();

for input in matches.values_of("input").unwrap() {
info!("Start parsing {:?}", input);
if let Err(e) = parser.parse_file_into_model(input, &mut model) {
error!("Error while parsing file: {:?}", e);
exit(1);
}
}
info!("Finished parsing");
}

+ 10
- 0
src/lib.rs View File

@@ -0,0 +1,10 @@
#![warn(missing_docs)]

//! This crate contains some tools to manage 3D models.

#[macro_use]
extern crate log;

pub mod math;
pub mod model;
pub mod parser;

+ 3
- 0
src/math/mod.rs View File

@@ -0,0 +1,3 @@
//! Module that contains all the math utilities.

pub mod vector;

+ 205
- 0
src/math/vector.rs View File

@@ -0,0 +1,205 @@
//! Module containing the vector struct.

use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Index, IndexMut};
use std::fmt;

macro_rules! make_vector {
( $name: ident,
$number: expr,
( $( $t: ident) , * ),
$( ($x: ident, $x_mut: ident, $y: expr) ), * ) => {
/// The vector struct.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct $name<T> {
data: [T; $number],
}

impl<T> $name<T> {
/// Creates a vector from its coordinates.
pub fn new( $( $x: T ) , *) -> $name<T> {
$name::<T> {
data: [ $( $x ) , *],
}
}

/// Returns the length of the vector.
pub fn len(&self) -> usize {
return $number;
}
}

impl<T> Into<[T; $number]> for $name<T> {
fn into(self) -> [T; $number] {
self.data
}
}

impl<T> From<[T; $number]> for $name<T> {
fn from(data: [T; $number]) -> $name<T> {
$name {
data: data,
}
}
}

impl<T: Copy + Clone> Into<($( $t ) ,* )> for $name<T> {
fn into(self) -> ($( $t ) ,* ) {
( $( self.data[$y] ), *)
}
}

impl<T: Add<Output=T> + Copy> Add for $name<T> {
type Output = $name<T>;

fn add(self, rhs: $name<T>) -> Self::Output {
$name::<T>::new( $( self.data[$y] + rhs.data[$y] ) , *)
}
}

impl<T: AddAssign + Copy> AddAssign for $name<T> {
fn add_assign(&mut self, rhs: $name<T>) {
$(
self.data[$y] += rhs.data[$y];
)*
}
}

impl<T: Sub<Output=T> + Copy> Sub for $name<T> {
type Output = $name<T>;

fn sub(self, rhs: $name<T>) -> Self::Output {
$name::<T>::new( $( self.data[$y] - rhs.data[$y] ) , *)
}
}

impl<T: SubAssign + Copy> SubAssign for $name<T> {
fn sub_assign(&mut self, rhs: $name<T>) {
$(
self.data[$y] -= rhs.data[$y];
)*
}
}

impl<T: Mul<Output=T> + Copy> Mul<T> for $name<T> {
type Output = $name<T>;

fn mul(self, rhs: T) -> Self::Output {
$name::<T>::new( $( self.data[$y] * rhs ), *)
}
}

impl<T: MulAssign + Copy> MulAssign<T> for $name<T> {
fn mul_assign(&mut self, rhs: T) {
$(
self.data[$y] *= rhs;
)*
}
}

impl<T: Div<Output=T> + Copy> Div<T> for $name<T> {
type Output = $name<T>;

fn div(self, rhs: T) -> Self::Output {
$name::<T>::new( $( self.data[$y] / rhs ), *)
}
}

impl<T: DivAssign + Copy> DivAssign<T> for $name<T> {
fn div_assign(&mut self, rhs: T) {
$(
self.data[$y] /= rhs;
)*
}
}

impl<T> Index<usize> for $name<T> {
type Output = T;

fn index(&self, index: usize) -> &T {
&self.data[index]
}
}

impl<T> IndexMut<usize> for $name<T> {
fn index_mut(&mut self, index: usize) -> &mut T {
&mut self.data[index]
}
}

impl<T: fmt::Display> fmt::Display for $name<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "(")?;
for i in &self.data {
write!(formatter, "{}, ", i)?;
}
write!(formatter, ")")
}
}
};
}


make_vector!(Vector2, 2, (T, T), (x, x_mut, 0), (y, y_mut, 1));
make_vector!(Vector3, 3, (T, T, T), (x, x_mut, 0), (y, y_mut, 1), (z, z_mut, 2));
make_vector!(Vector4, 4, (T, T, T, T), (x, x_mut, 0), (y, y_mut, 1), (z, z_mut, 2), (t, t_mut, 3));

#[cfg(test)]
mod test {

mod vector2 {

use crate::math::vector::Vector2;

#[test]
fn create() {
let v = Vector2::new(0, 1);
assert_eq!(v.data[0], 0);
assert_eq!(v.data[1], 1);
}

#[test]
fn index() {
let v = Vector2::new(0, 1);
assert_eq!(v[0], 0);
assert_eq!(v[1], 1);
}

#[test]
fn index_mut() {
let mut v = Vector2::new(0, 1);
v[0] = 1;
v[1] = 0;
assert_eq!(v.data[0], 1);
assert_eq!(v.data[1], 0);
}

#[test]
fn add_1() {
let v1 = Vector2::new(0, 1);
let v2 = Vector2::new(1, 0);
let v = v1 + v2;
assert_eq!(v.data[0], 1);
assert_eq!(v.data[1], 1);
}

#[test]
fn add_2() {
let v1 = Vector2::new(1, 1);
let v2 = Vector2::new(1, 1);
let v = v1 + v2;
assert_eq!(v.data[0], 2);
assert_eq!(v.data[1], 2);

}

#[test]
fn add_3() {
let v1 = Vector2::new(1, 2);
let v2 = Vector2::new(2, 2);
let v = v1 + v2;
assert_eq!(v.data[0], 3);
assert_eq!(v.data[1], 4);
}

}
}

+ 60
- 0
src/model/mod.rs View File

@@ -0,0 +1,60 @@
//! This module contains the struct Model, which is what you get after you parse a model file.

use crate::math::vector::{Vector2, Vector3};

/// A triplet (vertex, texture_coordinate, normal).
///
/// It represents the indices of the corresponding elements in a model.
pub struct FaceVertex {
/// The index of the vertex in the model.
vertex: usize,

/// The index of the texture coordinate in the model (if any).
texture_coordinate: Option<usize>,

/// The index of the normal in the model (if any).
normal: Option<usize>,
}

/// A triangle of a model.
///
/// It contains three FaceVertex that represents the vertices of the triangle.
pub struct Face(FaceVertex, FaceVertex, FaceVertex);

/// A part of a 3D model.
///
/// It can be bound to only one texture and contain many faces.
pub struct Part {

/// The faces of the model.
faces: Vec<Face>,

}

/// A 3D model.
pub struct Model {

/// All the vertices of the model.
vertices: Vec<Vector3<f64>>,

/// All the texture coordinates of the model.
texture_coordinates: Vec<Vector2<f64>>,

/// All the normals of the model.
normals: Vec<Vector3<f64>>,

}

impl Model {

/// Creates a new empty model.
pub fn new() -> Model {
Model {
vertices: vec![],
texture_coordinates: vec![],
normals: vec![],
}
}

}


+ 97
- 0
src/parser/mod.rs View File

@@ -0,0 +1,97 @@
//! This module contains all the parsers.

use std::convert::AsRef;
use std::path::Path;
use std::fs::File;

use std::io;
use std::io::{Read, BufRead, BufReader};

use crate::model::Model;

pub mod obj;

/// An error that can occur while parsing a 3D model file.
#[derive(Debug)]
pub enum Error {
/// An error occured while trying to open of read the file.
IoError(io::Error),
}

impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::IoError(e)
}
}

/// A line of a file.
///
/// It contain the line but also some metadata to help give better errors.
pub struct Line {
/// The content of the line.
pub content: String,

/// The path to the file in which the file is if it was opened from a file.
pub filename: Option<String>,

/// The corresponding line number if it is a text file.
pub number: Option<usize>,
}

/// The parser trait.
///
/// Any 3D model parser will implement this interface.
pub trait Parser {
/// Parses something that is readable.
fn parse<R: Read>(&self, reader: R) -> Result<Model, Error> {
let mut model = Model::new();
self.parse_into_model(reader, &mut model)?;
Ok(model)
}

/// Parses the content of the reader into the model.
fn parse_into_model<R: Read>(&self, reader: R, model: &mut Model) -> Result<(), Error>;

/// Parses a file and return a result to a model.
fn parse_file<P: AsRef<Path>>(&self, path: P) -> Result<Model, Error> {
let file = File::open(path.as_ref())?;
self.parse(file)
}

/// Parses a file into a model.
fn parse_file_into_model<P: AsRef<Path>>(&self, path: P, model: &mut Model) -> Result<(), Error> {
let file = File::open(path.as_ref())?;
self.parse_into_model(file, model)
}
}

/// A parser that parse text files.
pub trait LineParser {
/// Parses a single line of the file, and append the corresponding data to the model.
fn parse_line(&self, line: Line, model: &mut Model) -> Result<(), Error>;
}

impl<T> Parser for T where T: LineParser {
fn parse_into_model<R: Read>(&self, reader: R, model: &mut Model) -> Result<(), Error> {
let mut file = BufReader::new(reader);

loop {
let mut buffer = String::new();
let bytes = file.read_line(&mut buffer)?;

if bytes == 0 {
break;
}

let line = Line {
content: buffer,
filename: None,
number: None,
};

self.parse_line(line, model)?;
}

Ok(())
}
}

+ 22
- 0
src/parser/obj.rs View File

@@ -0,0 +1,22 @@
//! The WaveFront OBJ file parser.

use crate::parser::{Line, LineParser, Error};
use crate::model::Model;

/// The WaveFront OBJ file parser;
pub struct ObjParser;

impl ObjParser {
/// Creates a new ObjParser.
pub fn new() -> ObjParser {
ObjParser {

}
}
}

impl LineParser for ObjParser {
fn parse_line(&self, line: Line, model: &mut Model) -> Result<(), Error> {
Ok(())
}
}

Loading…
Cancel
Save