Replace bindings with Rust equivalent

This commit is contained in:
Layl 2019-05-04 16:56:34 +02:00
parent dffcb0aefa
commit 43ec58f321
10 changed files with 2165 additions and 2611 deletions

View File

@ -1,7 +1,8 @@
[package] [package]
name = "mikktspace" name = "mikktspace"
version = "0.1.1" version = "0.1.1"
authors = ["Benjamin Wasty <benny.wasty@gmail.com>", "David Harvey-Macaulay <alteous@outlook.com>"] edition = "2018"
authors = ["Benjamin Wasty <benny.wasty@gmail.com>", "David Harvey-Macaulay <alteous@outlook.com>", "Layl Bongers <LaylConway@users.noreply.github.com>"]
description = "Mikkelsen tangent space algorithm" description = "Mikkelsen tangent space algorithm"
documentation = "https://docs.rs/mikktspace" documentation = "https://docs.rs/mikktspace"
repository = "https://github.com/gltf-rs/mikktspace" repository = "https://github.com/gltf-rs/mikktspace"
@ -9,17 +10,14 @@ homepage = "https://github.com/gltf-rs/mikktspace"
readme = "README.md" readme = "README.md"
keywords = ["3D", "graphics", "algorithm", "tangent"] keywords = ["3D", "graphics", "algorithm", "tangent"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
build = "build.rs"
exclude = ["examples/**"] exclude = ["examples/**"]
[badges] [badges]
travis-ci = { repository = "gltf-rs/mikktspace" } travis-ci = { repository = "gltf-rs/mikktspace" }
[build-dependencies] [dependencies]
cc = "1.0" libc = "0.2.54"
nalgebra = "0.18.0"
[dev-dependencies]
cgmath = "0.15"
[[example]] [[example]]
name = "generate" name = "generate"

View File

@ -3,7 +3,9 @@
[![crates.io](https://img.shields.io/crates/v/mikktspace.svg)](https://crates.io/crates/mikktspace) [![crates.io](https://img.shields.io/crates/v/mikktspace.svg)](https://crates.io/crates/mikktspace)
[![Build Status](https://travis-ci.org/gltf-rs/mikktspace.svg?branch=master)](https://travis-ci.org/gltf-rs/mikktspace) [![Build Status](https://travis-ci.org/gltf-rs/mikktspace.svg?branch=master)](https://travis-ci.org/gltf-rs/mikktspace)
Bindings to the [Mikkelsen Tangent Space Algorithm](https://en.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps) reference implementation. Port of the [Mikkelsen Tangent Space Algorithm](https://en.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps) reference implementation.
Machine generated with modifications for better integration with Rust.
## Examples ## Examples
@ -15,16 +17,6 @@ Demonstrates generating tangents for a cube with 4 triangular faces per side.
cargo run --example generate cargo run --example generate
``` ```
There is also an equivalent C example to check the correctness of the Rust bindings.
```sh
cd examples
cmake ../libmikktspace
make
cc generate.c libmikktspace.a -I../libmikktspace -lm -o generate
./generate
```
## License agreement ## License agreement
Licensed under either of Licensed under either of

View File

@ -1,8 +0,0 @@
extern crate cc;
fn main() {
cc::Build::new()
.file("libmikktspace/mikktspace.c")
.include("libmikktspace")
.compile("mikktspace");
}

View File

@ -1,211 +0,0 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "mikktspace.h"
struct tangent {
// Vector.
float v[3];
// Sign.
float s;
};
struct vertex {
// Borrows from `input.positions`.
float (*position)[3];
// Borrows from `input.positions`.
float (*normal)[3];
// Borrows from `input.positions`.
float (*tex_coord)[2];
};
struct face {
// Borrows from `positions`, `normals`, and `tex_coords`.
struct vertex vertices[3];
};
struct input {
// Owned.
float (*positions)[3];
// Owned.
float (*normals)[3];
// Owned.
float (*tex_coords)[2];
// Borrows from `positions`, `normals`, and `tex_coords`.
struct face *faces;
// Number of entries in `positions`, `normals`, and `tex_coords`.
size_t nr_vertices;
// Number of entries in `faces`.
size_t nr_faces;
} input;
void print_vec2(float (*t)[2]) {
printf("[%f, %f]", (*t)[0], (*t)[1]);
}
void print_vec3(float (*t)[3]) {
printf("[%f, %f, %f]", (*t)[0], (*t)[1], (*t)[2]);
}
void print_tangent(const struct tangent *t) {
printf("[%f, %f, %f, %f]", t->v[0], t->v[1], t->v[2], t->s);
}
int get_num_faces(const SMikkTSpaceContext *x) {
return input.nr_faces;
}
int get_num_vertices_of_face(const SMikkTSpaceContext *x, int f) {
return 3;
}
void get_position(const SMikkTSpaceContext *x, float *dst, int f, int v) {
float (*src)[3] = input.faces[f].vertices[v].position;
memcpy(dst, *src, 3 * sizeof(float));
}
void get_normal(const SMikkTSpaceContext *x, float *dst, int f, int v) {
float (*src)[3] = input.faces[f].vertices[v].normal;
memcpy(dst, *src, 3 * sizeof(float));
}
void get_tex_coord(const SMikkTSpaceContext *x, float *dst, int f, int v) {
float (*src)[2] = input.faces[f].vertices[v].tex_coord;
memcpy(dst, *src, 2 * sizeof(float));
}
void set_tspace_basic(
const SMikkTSpaceContext *x,
const float *t,
float s,
int f,
int v
) {
// The index of the last output (vertex, tangent) pair.
static int i = 0;
struct vertex *in = &input.faces[f].vertices[v];
printf("%d: { v: ", i);
print_vec3(in->position);
printf(", vn: ");
print_vec3(in->normal);
printf(", vt: ");
print_vec2(in->tex_coord);
printf(", vx: [%f, %f, %f, %f] }", t[0], t[1], t[2], s);
puts("");
++i;
}
void set_tspace(
const SMikkTSpaceContext *x,
const float *t,
const float *b,
float mag_s,
float mag_t,
tbool op,
int f,
int v
) {
set_tspace_basic(x, t, op != 0 ? 1.0f : -1.0f, f, v);
}
int main() {
input.nr_vertices = 30;
input.nr_faces = 24;
input.positions = calloc(input.nr_vertices, sizeof(*input.positions));
input.normals = calloc(input.nr_vertices, sizeof(*input.normals));
input.tex_coords = calloc(input.nr_vertices, sizeof(*input.tex_coords));
input.faces = calloc(input.nr_faces, sizeof(*input.faces));
FILE *fi = fopen("cube.obj", "rb");
assert(fi);
char buffer[1024];
for (size_t i = 0; i < input.nr_vertices; ++i) {
fgets(buffer, sizeof(buffer), fi);
sscanf(
buffer,
"v %f %f %f",
&input.positions[i][0],
&input.positions[i][1],
&input.positions[i][2]
);
}
for (size_t i = 0; i < input.nr_vertices; ++i) {
fgets(buffer, sizeof(buffer), fi);
sscanf(
buffer,
"vn %f %f %f",
&input.normals[i][0],
&input.normals[i][1],
&input.normals[i][2]
);
}
for (size_t i = 0; i < input.nr_vertices; ++i) {
fgets(buffer, sizeof(buffer), fi);
sscanf(
buffer,
"vt %f %f",
&input.tex_coords[i][0],
&input.tex_coords[i][1]
);
}
for (size_t i = 0; i < input.nr_faces; ++i) {
fgets(buffer, sizeof(buffer), fi);
int v[3];
sscanf(
buffer,
"f %d/%d/%d %d/%d/%d %d/%d/%d",
&v[0], &v[0], &v[0],
&v[1], &v[1], &v[1],
&v[2], &v[2], &v[2]
);
for (size_t j = 0; j < 3; ++j) {
input.faces[i].vertices[j].position = &input.positions[v[j] - 1];
input.faces[i].vertices[j].normal = &input.normals[v[j] - 1];
input.faces[i].vertices[j].tex_coord = &input.tex_coords[v[j] - 1];
}
}
SMikkTSpaceInterface interface = {
.m_getNumFaces = get_num_faces,
.m_getNumVerticesOfFace = get_num_vertices_of_face,
.m_getPosition = get_position,
.m_getNormal = get_normal,
.m_getTexCoord = get_tex_coord,
.m_setTSpaceBasic = set_tspace_basic,
.m_setTSpace = set_tspace,
};
SMikkTSpaceContext context = {
.m_pInterface = &interface,
.m_pUserData = NULL,
};
genTangSpaceDefault(&context);
fclose(fi);
free(input.positions);
free(input.normals);
free(input.tex_coords);
free(input.faces);
return 0;
}

View File

@ -1,23 +1,62 @@
extern crate cgmath; use nalgebra::{Point2, Point3, Vector3, Vector4};
extern crate mikktspace;
use cgmath::prelude::*;
pub type Face = [u32; 3]; pub type Face = [u32; 3];
pub type Vec2 = [f32; 2];
pub type Vec3 = [f32; 3];
#[derive(Debug)] #[derive(Debug)]
struct Vertex { struct Vertex {
position: Vec3, position: Point3<f32>,
normal: Vec3, normal: Vector3<f32>,
tex_coord: Vec2, tex_coord: Point2<f32>,
} }
fn make_cube() -> (Vec<Face>, Vec<Vertex>) { struct Mesh {
faces: Vec<Face>,
vertices: Vec<Vertex>,
}
fn vertex(mesh: &Mesh, face: usize, vert: usize) -> &Vertex {
let vs: &[u32; 3] = &mesh.faces[face];
&mesh.vertices[vs[vert] as usize]
}
impl mikktspace::Geometry for Mesh {
fn get_num_faces(&self) -> usize {
self.faces.len()
}
fn get_num_vertices_of_face(&self, _face: usize) -> usize {
3
}
fn get_position(&self, face: usize, vert: usize) -> Point3<f32> {
vertex(self, face, vert).position
}
fn get_normal(&self, face: usize, vert: usize) -> Vector3<f32> {
vertex(self, face, vert).normal
}
fn get_tex_coord(&self, face: usize, vert: usize) -> Point2<f32> {
vertex(self, face, vert).tex_coord
}
fn set_tangent_encoded(&mut self, tangent: Vector4<f32>, face: usize, vert: usize) {
println!(
"{face}-{vert}: v: {v:?}, vn: {vn:?}, vt: {vt:?}, vx: {vx:?}",
face = face,
vert = vert,
v = vertex(self, face, vert).position.coords.data,
vn = vertex(self, face, vert).normal.data,
vt = vertex(self, face, vert).tex_coord.coords.data,
vx = tangent.data,
);
}
}
fn make_cube() -> Mesh {
struct ControlPoint { struct ControlPoint {
uv: Vec2, uv: [f32; 2],
dir: Vec3, dir: [f32; 3],
} }
let mut faces = Vec::new(); let mut faces = Vec::new();
let mut ctl_pts = Vec::new(); let mut ctl_pts = Vec::new();
@ -108,9 +147,9 @@ fn make_cube() -> (Vec<Face>, Vec<Vertex>) {
} }
for pt in ctl_pts { for pt in ctl_pts {
let p: cgmath::Vector3<f32> = pt.dir.into(); let p: Point3<f32> = pt.dir.into();
let n: cgmath::Vector3<f32> = p.normalize(); let n: Vector3<f32> = p.coords.normalize();
let t: cgmath::Vector2<f32> = pt.uv.into(); let t: Point2<f32> = pt.uv.into();
vertices.push(Vertex { vertices.push(Vertex {
position: (p / 2.0).into(), position: (p / 2.0).into(),
normal: n.into(), normal: n.into(),
@ -118,42 +157,14 @@ fn make_cube() -> (Vec<Face>, Vec<Vertex>) {
}); });
} }
(faces, vertices) Mesh {
faces,
vertices,
}
} }
fn main() { fn main() {
let (faces, vertices) = make_cube(); let mut cube = make_cube();
let vertex = |face, vert| { let ret = mikktspace::generate_tangents_default(&mut cube);
let vs: &[u32; 3] = &faces[face]; assert_eq!(true, ret);
&vertices[vs[vert] as usize]
};
let vertices_per_face = || 3;
let face_count = || faces.len();
let position = |face, vert| &vertex(face, vert).position;
let normal = |face, vert| &vertex(face, vert).normal;
let tex_coord = |face, vert| &vertex(face, vert).tex_coord;
{
let mut i = 0;
let mut set_tangent = |face, vert, tangent| {
println!(
"{index}: v: {v:?}, vn: {vn:?}, vt: {vt:?}, vx: {vx:?}",
index = i,
v = position(face, vert),
vn = normal(face, vert),
vt = tex_coord(face, vert),
vx = tangent,
);
i += 1;
};
let ret = mikktspace::generate_tangents(
&vertices_per_face,
&face_count,
&position,
&normal,
&tex_coord,
&mut set_tangent,
);
assert_eq!(true, ret);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,145 +0,0 @@
/** \file mikktspace/mikktspace.h
* \ingroup mikktspace
*/
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef __MIKKTSPACE_H__
#define __MIKKTSPACE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Author: Morten S. Mikkelsen
* Version: 1.0
*
* The files mikktspace.h and mikktspace.c are designed to be
* stand-alone files and it is important that they are kept this way.
* Not having dependencies on structures/classes/libraries specific
* to the program, in which they are used, allows them to be copied
* and used as is into any tool, program or plugin.
* The code is designed to consistently generate the same
* tangent spaces, for a given mesh, in any tool in which it is used.
* This is done by performing an internal welding step and subsequently an order-independent evaluation
* of tangent space for meshes consisting of triangles and quads.
* This means faces can be received in any order and the same is true for
* the order of vertices of each face. The generated result will not be affected
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
* primitives are present or not will not affect the generated results either.
* Once tangent space calculation is done the vertices of degenerate primitives will simply
* inherit tangent space from neighboring non degenerate primitives.
* The analysis behind this implementation can be found in my master's thesis
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
* by this implementation, the interpolated tangent space is still affected by which diagonal is
* chosen to split each quad. A sensible solution is to have your tools pipeline always
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
* If these have the same length then compare the diagonals defined by the texture coordinates.
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
* and also quad triangulator plugin.
*/
typedef int tbool;
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
typedef struct {
// Returns the number of faces (triangles/quads) on the mesh to be processed.
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
// Returns the number of vertices on face number iFace
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
// either (or both) of the two setTSpace callbacks can be set.
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
// This function is used to return the tangent and fSign to the application.
// fvTangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
// This function is used to return tangent space results to the application.
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
// true magnitudes which can be used for relief mapping effects.
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
} SMikkTSpaceInterface;
struct SMikkTSpaceContext
{
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
};
// these are both thread safe!
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
// normal map sampler must use the exact inverse of the pixel shader transformation.
// The most efficient transformation we can possibly do in the pixel shader is
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
// pixel shader (fast transform out)
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// where vNt is the tangent space normal. The normal map sampler must likewise use the
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
// sampler does (exact inverse of pixel shader):
// float3 row0 = cross(vB, vN);
// float3 row1 = cross(vN, vT);
// float3 row2 = cross(vT, vB);
// float fSign = dot(vT, row0)<0 ? -1 : 1;
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
// where vNout is the sampled normal in some chosen 3D space.
//
// Should you choose to reconstruct the bitangent in the pixel shader instead
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
// However, this must be used both by the sampler and your tools/rendering pipeline.
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,129 +0,0 @@
#![allow(bad_style)]
#![allow(dead_code)]
use std::os::raw::*;
pub type tbool = c_int;
pub const TFALSE: tbool = 0;
pub const TTRUE: tbool = 1;
#[repr(C)]
pub struct SMikkTSpaceInterface {
/// Returns the number of faces (triangles/quads) on the mesh to be processed.
pub m_getNumFaces: extern "C" fn(pContext: *const SMikkTSpaceContext) -> c_int,
/// Returns the number of vertices on face number iFace
/// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
pub m_getNumVerticesOfFace: extern "C" fn(
pContext: *const SMikkTSpaceContext,
iFace: c_int,
) -> c_int,
/// Returns the position of the referenced face of vertex number
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads.
pub m_getPosition: extern "C" fn(
pContext: *const SMikkTSpaceContext,
fvPosOut: *mut c_float,
iFace: c_int,
iVert: c_int,
),
/// Returns the normal of the referenced face of vertex number
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads.
pub m_getNormal: extern "C" fn(
pContext: *const SMikkTSpaceContext,
fvNormOut: *mut c_float,
iFace: c_int,
iVert: c_int,
),
/// Returns the texcoord of the referenced face of vertex number
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads.
pub m_getTexCoord: extern "C" fn(
pContext: *const SMikkTSpaceContext,
fvTexcOut: *mut c_float,
iFace: c_int,
iVert: c_int,
),
/// either (or both) of the two setTSpace callbacks can be set.
/// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
/// This function is used to return the tangent and fSign to the application.
/// fvTangent is a unit length vector.
/// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
/// bitangent = fSign * cross(vN, tangent);
/// Note that the results are returned unindexed. It is possible to generate a new index list
/// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
/// DO NOT! use an already existing index list.
pub m_setTSpaceBasic: extern "C" fn(
pContext: *mut SMikkTSpaceContext,
fvTangent: *const c_float,
fSign: c_float,
iFace: c_int,
iVert: c_int,
),
/// This function is used to return tangent space results to the application.
/// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
/// true magnitudes which can be used for relief mapping effects.
/// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
/// However, both are perpendicular to the vertex normal.
/// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
/// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
/// bitangent = fSign * cross(vN, tangent);
/// Note that the results are returned unindexed. It is possible to generate a new index list
/// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
/// DO NOT! use an already existing index list.
pub m_setTSpace: extern "C" fn(
pContext: *mut SMikkTSpaceContext,
fvTangent: *const c_float,
fvBiTangent: *const c_float,
fMagS: c_float,
fMagT: c_float,
bIsOrientationPreserving: tbool,
iFace: c_int,
iVert: c_int,
),
}
/// these are both thread safe!
/// Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
extern "system" {
pub fn genTangSpaceDefault(pContext: *const SMikkTSpaceContext) -> tbool;
#[allow(dead_code)]
pub fn genTangSpace(pContext: *const SMikkTSpaceContext, fAngularThreshold: c_float) -> tbool;
}
/// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
/// normal map sampler must use the exact inverse of the pixel shader transformation.
/// The most efficient transformation we can possibly do in the pixel shader is
/// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
/// pixel shader (fast transform out)
/// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
/// where vNt is the tangent space normal. The normal map sampler must likewise use the
/// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
/// sampler does (exact inverse of pixel shader):
/// float3 row0 = cross(vB, vN);
/// float3 row1 = cross(vN, vT);
/// float3 row2 = cross(vT, vB);
/// float fSign = dot(vT, row0)<0 ? -1 : 1;
/// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
/// where vNout is the sampled normal in some chosen 3D space.
///
/// Should you choose to reconstruct the bitangent in the pixel shader instead
/// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
/// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
/// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
/// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
/// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
/// However, this must be used both by the sampler and your tools/rendering pipeline.
#[repr(C)]
pub struct SMikkTSpaceContext {
/// initialized with callback functions
pub m_pInterface: *const SMikkTSpaceInterface,
/// pointer to client side mesh data etc. (passed as the first parameter with every interface call)
pub m_pUserData: *mut c_void,
}

2036
src/generated.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +1,73 @@
#![allow(bad_style)] mod generated;
mod ffi; use nalgebra::{Point2, Point3, Vector3, Vector4};
use std::os::raw::*; /// The interface by which mikktspace interacts with your geometry.
use std::mem; pub trait Geometry {
use std::ptr;
/// Rust FFI for the MikkTSpace implementation.
const INTERFACE: ffi::SMikkTSpaceInterface = ffi::SMikkTSpaceInterface {
m_getNumFaces: faces,
m_getNumVerticesOfFace: vertices,
m_getPosition: position,
m_getNormal: normal,
m_getTexCoord: tex_coord,
m_setTSpaceBasic: set_tspace_basic,
m_setTSpace: set_tspace,
};
/// Rust front-end API for tangent generation.
struct Closures<'a> {
/// Returns the number of vertices per face.
pub vertices_per_face: &'a Fn() -> usize,
/// Returns the number of faces. /// Returns the number of faces.
pub face_count: &'a Fn() -> usize, fn get_num_faces(&self) -> usize;
/// Returns the positions of the indexed face.
pub position: &'a Fn(usize, usize) -> &'a [f32; 3],
/// Returns the normals of the indexed face. /// Returns the number of vertices of a face.
pub normal: &'a Fn(usize, usize) -> &'a [f32; 3], fn get_num_vertices_of_face(&self, face: usize) -> usize;
/// Returns the texture co-ordinates of the indexed face. /// Returns the position of a vertex.
pub tex_coord: &'a Fn(usize, usize) -> &'a [f32; 2], fn get_position(&self, face: usize, vert: usize) -> Point3<f32>;
/// Sets the generated tangent for the indexed face. /// Returns the normal of a vertex.
pub set_tangent: &'a mut FnMut(usize, usize, [f32; 4]), fn get_normal(&self, face: usize, vert: usize) -> Vector3<f32>;
}
/// Returns the number of faces (triangles/quads) on the mesh to be processed. /// Returns the texture coordinate of a vertex.
extern "C" fn faces(pContext: *const ffi::SMikkTSpaceContext) -> c_int { fn get_tex_coord(&self, face: usize, vert: usize) -> Point2<f32>;
unsafe {
let x = (*pContext).m_pUserData as *const Closures;
((*x).face_count)() as c_int
}
}
/// Returns the number of vertices on face number iFace /// Sets a vertex' generated tangent.
/// iFace is a number in the range {0, 1, ..., getNumFaces()-1} fn set_tangent(
extern "C" fn vertices( &mut self,
pContext: *const ffi::SMikkTSpaceContext, tangent: Vector3<f32>,
_iFace: c_int, _bi_tangent: Vector3<f32>,
) -> c_int { _f_mag_s: f32,
unsafe { _f_mag_t: f32,
let x = (*pContext).m_pUserData as *const Closures; bi_tangent_preserves_orientation: bool,
((*x).vertices_per_face)() as c_int face: usize,
} vert: usize,
} ) {
let sign = if bi_tangent_preserves_orientation {
/// Returns the position of the referenced face of vertex number 1.0
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. } else {
extern "C" fn position( -1.0
pContext: *const ffi::SMikkTSpaceContext,
fvPosOut: *mut c_float,
iFace: c_int,
iVert: c_int,
) {
unsafe {
let x = (*pContext).m_pUserData as *const Closures;
let slice = ((*x).position)(iFace as usize, iVert as usize);
let src = slice.as_ptr() as *const c_float;
ptr::copy_nonoverlapping::<c_float>(src, fvPosOut, 3);
}
}
/// Returns the normal of the referenced face of vertex number
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads.
extern "C" fn normal(
pContext: *const ffi::SMikkTSpaceContext,
fvNormOut: *mut c_float,
iFace: c_int,
iVert: c_int,
) {
unsafe {
let x = (*pContext).m_pUserData as *const Closures;
let slice = ((*x).normal)(iFace as usize, iVert as usize);
let src = slice.as_ptr() as *const c_float;
ptr::copy_nonoverlapping::<c_float>(src, fvNormOut, 3);
}
}
/// Returns the texcoord of the referenced face of vertex number
/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads.
extern "C" fn tex_coord(
pContext: *const ffi::SMikkTSpaceContext,
fvTexcOut: *mut c_float,
iFace: c_int,
iVert: c_int,
) {
unsafe {
let x = (*pContext).m_pUserData as *const Closures;
let slice = ((*x).tex_coord)(iFace as usize, iVert as usize);
let src = slice.as_ptr() as *const c_float;
ptr::copy_nonoverlapping::<c_float>(src, fvTexcOut, 2);
}
}
/// Returns the tangent and its sign to the application.
extern "C" fn set_tspace_basic(
pContext: *mut ffi::SMikkTSpaceContext,
fvTangent: *const c_float,
fSign: c_float,
iFace: c_int,
iVert: c_int,
) {
unsafe {
let x = (*pContext).m_pUserData as *mut Closures;
let mut tangent: [f32; 4] = mem::uninitialized();
let dst: *mut c_float = tangent.as_mut_ptr();
ptr::copy_nonoverlapping::<c_float>(fvTangent, dst, 3);
tangent[3] = fSign;
((*x).set_tangent)(iFace as usize, iVert as usize, tangent);
}
}
/// Returns tangent space results to the application.
extern "C" fn set_tspace(
pContext: *mut ffi::SMikkTSpaceContext,
fvTangent: *const c_float,
_fvBiTangent: *const c_float,
_fMagS: c_float,
_fMagT: c_float,
bIsOrientationPreserving: ffi::tbool,
iFace: c_int,
iVert: c_int,
) {
let fSign = if bIsOrientationPreserving != 0 { 1.0 } else { -1.0 };
set_tspace_basic(pContext, fvTangent, fSign, iFace, iVert);
}
impl<'a> Closures<'a> {
/// Generates tangents.
pub fn generate(mut self) -> bool {
let ctx = ffi::SMikkTSpaceContext {
m_pInterface: &INTERFACE,
m_pUserData: &mut self as *mut Closures as *mut c_void,
}; };
unsafe { self.set_tangent_encoded(tangent.insert_row(3, sign), face, vert);
ffi::genTangSpaceDefault(&ctx) == ffi::TTRUE
}
} }
/// Sets a vertex' generated tangent with the bi-tangent encoded as the W component in the
/// tangent. The W component marks if the bi-tangent is flipped. This will only be called if
/// `set_tangent` is not implemented.
fn set_tangent_encoded(&mut self, _tangent: Vector4<f32>, _face: usize, _vert: usize) {}
} }
/// Generates tangents. /// Default (recommended) Angular Threshold is 180 degrees, which means threshold disabled.
pub fn generate_tangents<'a>( pub fn generate_tangents_default<I: Geometry>(geometry: &mut I) -> bool {
vertices_per_face: &'a Fn() -> usize, unsafe { generated::genTangSpace(geometry, 180.0) }
face_count: &'a Fn() -> usize, }
position: &'a Fn(usize, usize) -> &'a [f32; 3],
normal: &'a Fn(usize, usize) -> &'a [f32; 3], fn get_position<I: Geometry>(geometry: &mut I, index: usize) -> Vector3<f32> {
tex_coord: &'a Fn(usize, usize) -> &'a [f32; 2], let (face, vert) = index_to_face_vert(index);
set_tangent: &'a mut FnMut(usize, usize, [f32; 4]), geometry.get_position(face, vert).coords
) -> bool { }
let closures = Closures {
vertices_per_face, fn get_tex_coord<I: Geometry>(geometry: &mut I, index: usize) -> Vector3<f32> {
face_count, let (face, vert) = index_to_face_vert(index);
position, geometry.get_tex_coord(face, vert).coords.insert_row(2, 1.0)
normal, }
tex_coord,
set_tangent, fn get_normal<I: Geometry>(geometry: &mut I, index: usize) -> Vector3<f32> {
}; let (face, vert) = index_to_face_vert(index);
closures.generate() geometry.get_normal(face, vert)
}
fn index_to_face_vert(index: usize) -> (usize, usize) {
(index >> 2, index & 0x3)
}
fn face_vert_to_index(face: usize, vert: usize) -> usize {
face << 2 | vert & 0x3
} }