Commit 3d2fdb9b authored by Sander van Veen's avatar Sander van Veen

Added compilerbouw & graphics ass 7: raytracing 2

parent 2a52117f
[submodule "compilerbouw"]
path = compilerbouw
url = ssh://sander@vo20.nl/git/compilerbouw.git
compilerbouw @ 28ecce81
Subproject commit 28ecce816a2ffceb9eb23e05b39c4b403bd57539
CC=gcc
WARNING_FLAGS=-Wall -Wextra -Werror-implicit-function-declaration -Wshadow -Wstrict-prototypes -pedantic-errors
CFLAGS=-g -O3 -std=c99 $(WARNING_FLAGS)
LDFLAGS=-g -lGL -lglut -lGLU
.c.o:
$(CC) -c $(CFLAGS) $<
all: main
main: bbox.o bvh.o intersection.o main.o perlin.o plymodel.o quat.o rply.o scene.o shaders.o
$(CC) $(LDFLAGS) -o main bbox.o bvh.o intersection.o main.o perlin.o plymodel.o quat.o rply.o scene.o shaders.o
clean:
rm -f *.o
rm -f main
shaders.o : constants.h intersection.h perlin.h quat.h scene.h shaders.h v3math.h shaders.c
shaders.o : types.h shaders.h
perlin.o : perlin.h
main.o : bvh.h constants.h intersection.h perlin.h plymodel.h scene.h shaders.h v3math.h main.c
perlin.o : perlin.h perlin.c
rply.o : rply.h rply.c
v3math.o : v3math.h
quat.o : v3math.h quat.h
rply.o : rply.h
quat.o : constants.h quat.h quat.c
scene.o : bvh.h plymodel.h scene.h scene.c
scene.o : types.h scene.h
types.o : v3math.h types.h
bvh.o : bvh.h scene.h v3math.h bvh.c
plymodel.o : plymodel.h rply.h v3math.h plymodel.c
plymodel.o : types.h plymodel.h
bvh.o : bbox.h bvh.h
constants.o : types.h constants.h
intersection.o : types.h intersection.h
intersection.o : bvh.h constants.h intersection.h scene.h v3math.h intersection.c
bbox.o : bbox.h constants.h v3math.h bbox.c
bbox.o : types.h bbox.h
#include "constants.h"
#include "v3math.h"
#include "bbox.h"
#include "intersection.h"
// Create a bounding box (initialized to be empty, i.e. min > max)
boundingbox
bbox_create(void)
{
boundingbox bbox;
bbox.min = v3_create(C_INFINITY, C_INFINITY, C_INFINITY);
bbox.max = v3_create(-C_INFINITY, -C_INFINITY, -C_INFINITY);
return bbox;
}
// Update the given bounding box to include the point p
void
bbox_update(boundingbox* bbox, vec3 p)
{
if (p.x < bbox->min.x) bbox->min.x = p.x;
if (p.y < bbox->min.y) bbox->min.y = p.y;
if (p.z < bbox->min.z) bbox->min.z = p.z;
if (p.x > bbox->max.x) bbox->max.x = p.x;
if (p.y > bbox->max.y) bbox->max.y = p.y;
if (p.z > bbox->max.z) bbox->max.z = p.z;
}
// Return the bounding box resulting from the combination of the
// two given bounding boxes
boundingbox
bbox_combine(boundingbox bbox1, boundingbox bbox2)
{
boundingbox bbox = bbox1;
if (bbox2.min.x < bbox.min.x) bbox.min.x = bbox2.min.x;
if (bbox2.min.y < bbox.min.y) bbox.min.y = bbox2.min.y;
if (bbox2.min.z < bbox.min.z) bbox.min.z = bbox2.min.z;
if (bbox2.max.x > bbox.max.x) bbox.max.x = bbox2.max.x;
if (bbox2.max.y > bbox.max.y) bbox.max.y = bbox2.max.y;
if (bbox2.max.z > bbox.max.z) bbox.max.z = bbox2.max.z;
return bbox;
}
// Intersect a ray with given origin and direction, with the given
// bounding box.
// Returns 1 (and sets t_min and t_max) when an intersection is found,
// returns 0 otherwise
int
bbox_intersect(float* t_min, float* t_max, boundingbox bbox,
vec3 origin, vec3 direction, float t0, float t1)
{
// This code makes handy use of IEEE floating-point behaviour, such
// as division by zero. See
// 1) Shirley et.al. Section 10.9.1
// 2) Williams et.al., "An Efficient and Robust Ray-Box Intersection Algorithm"
float tmin, tmax;
float tymin, tymax, tzmin, tzmax;
float inv;
num_bboxes_tested++;
inv = 1.0 / direction.x;
if (inv >= 0.0)
{
tmin = (bbox.min.x - origin.x) * inv;
tmax = (bbox.max.x - origin.x) * inv;
}
else
{
tmin = (bbox.max.x - origin.x) * inv;
tmax = (bbox.min.x - origin.x) * inv;
}
inv = 1.0 / direction.y;
if (inv >= 0.0)
{
tymin = (bbox.min.y - origin.y) * inv;
tymax = (bbox.max.y - origin.y) * inv;
}
else
{
tymin = (bbox.max.y - origin.y) * inv;
tymax = (bbox.min.y - origin.y) * inv;
}
if (tmin > tymax || tymin > tmax)
return 0;
if (tymin > tmin)
tmin = tymin;
if (tymax < tmax)
tmax = tymax;
inv = 1.0 / direction.z;
if (inv >= 0.0)
{
tzmin = (bbox.min.z - origin.z) * inv;
tzmax = (bbox.max.z - origin.z) * inv;
}
else
{
tzmin = (bbox.max.z - origin.z) * inv;
tzmax = (bbox.min.z - origin.z) * inv;
}
if (tmin > tzmax || tzmin > tmax)
return 0;
if (tzmin > tmin)
tmin = tzmin;
if (tzmax < tmax)
tmax = tzmax;
*t_min = tmin;
*t_max = tmax;
return (tmin < t1 && tmax > t0);
}
float
bbox_volume(boundingbox bbox)
{
return (bbox.max.x - bbox.min.x) * (bbox.max.y - bbox.min.y) * (bbox.max.z - bbox.min.z);
}
#ifndef BBOX_H
#define BBOX_H
#include "types.h"
//
// An axis-aligned bounding box (AABB)
//
typedef struct
{
// The two corners of the bounding box
vec3 min;
vec3 max;
}
boundingbox;
// Create a bounding box (initialized to be empty, i.e. min > max)
boundingbox bbox_create(void);
// Update the given bounding box to include the point p
void bbox_update(boundingbox* bbox, vec3 p);
// Return the bounding box resulting from the combination of the
// two given bounding boxes
boundingbox bbox_combine(boundingbox bbox1, boundingbox bbox2);
// XXX ray segment, t0, t1
// Intersect a ray with given origin and direction (and delimited by
// t0 and t1), with the given bounding box.
// Returns 1 (and sets t_min and t_max) when the ray intersects the bbox,
// returns 0 otherwise
int bbox_intersect(float* t_min, float* t_max, boundingbox bbox,
vec3 origin, vec3 direction, float t0, float t1);
// Return the volume of the bounding box
float bbox_volume(boundingbox bbox);
#endif
light 9 -4.0 10.0 0.7
light -10 2.0 15.0 0.3
material 1
sphere 0.65 -0.6 0.4 0.2
sphere -0.6 0.75 0.4 0.4
sphere -0.5 0.15 0.2 0.2
ply_file ../scenes/plateau_subdivided.ply
ply_file ../scenes/kubus.ply
ply_file ../scenes/tetra.ply
ply_file ../scenes/buddha.ply
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "bvh.h"
#include "scene.h"
#include "v3math.h"
//#define VERBOSE
int use_bvh=1;
// The root node of the BVH
bvh_node *bvh_root;
static int max_depth=16;
static int acceptable_leaf_size=4;
static int num_leafs, num_inner_nodes;
static int max_leaf_size;
static bvh_node*
create_leaf_node(boundingbox bbox, triangle* triangles, int num_triangles)
{
bvh_node *leaf;
leaf = (bvh_node*) malloc(sizeof(bvh_node));
leaf->is_leaf = 1;
leaf->bbox = bbox;
// We slightly enlarge the bbox around the group of triangles,
// to make sure all triangles lie *within* the bbox
leaf->bbox.min.x -= 1.0e-6;
leaf->bbox.min.y -= 1.0e-6;
leaf->bbox.min.z -= 1.0e-6;
leaf->bbox.max.x += 1.0e-6;
leaf->bbox.max.y += 1.0e-6;
leaf->bbox.max.z += 1.0e-6;
leaf->u.leaf.triangles = triangles;
leaf->u.leaf.num_triangles = num_triangles;
num_leafs++;
if (num_triangles > max_leaf_size)
max_leaf_size = num_triangles;
return leaf;
}
static bvh_node*
create_inner_node(bvh_node* left_child, bvh_node* right_child)
{
bvh_node *inner;
boundingbox bbox;
inner = (bvh_node*) malloc(sizeof(bvh_node));
inner->is_leaf = 0;
// An inner node's bbox is always the exact combination of
// its children bboxes.
bbox = left_child->bbox;
if (right_child->bbox.min.x < bbox.min.x) bbox.min.x = right_child->bbox.min.x;
if (right_child->bbox.min.y < bbox.min.y) bbox.min.y = right_child->bbox.min.y;
if (right_child->bbox.min.z < bbox.min.z) bbox.min.z = right_child->bbox.min.z;
if (right_child->bbox.max.x > bbox.max.x) bbox.max.x = right_child->bbox.max.x;
if (right_child->bbox.max.y > bbox.max.y) bbox.max.y = right_child->bbox.max.y;
if (right_child->bbox.max.z > bbox.max.z) bbox.max.z = right_child->bbox.max.z;
inner->bbox = bbox;
inner->u.inner.left_child = left_child;
inner->u.inner.right_child = right_child;
num_inner_nodes++;
return inner;
}
static int
is_left(triangle *t, int split_axis, float split_value)
{
float value, min_value, max_value;
// Determine the extents that the triangle's vertices span
// on the given axis
min_value = v3_component(scene_vertices[t->v[0]], split_axis);
max_value = min_value;
for (int i = 1; i < 3; i++)
{
value = v3_component(scene_vertices[t->v[i]], split_axis);
if (value < min_value) min_value = value;
if (value > max_value) max_value = value;
}
// Determine if the triangle is (mostly) on the left side of
// the split plane. We say "mostly" because a triangle does not
// need to be fully on the left side of the split plane to be
// considered "left of", it may span the split plane.
return (0.5*(min_value+max_value)) <= split_value;
}
// Determine the bounding box that encloses the given triangles
static boundingbox
bound_triangles(triangle *triangles, int num_triangles)
{
int t, i;
boundingbox bbox;
bbox = bbox_create();
// Iterate over all triangles
for (t = 0; t < num_triangles; t++)
{
// Iterate over this triangle's vertices
for (i = 0; i < 3; i++)
bbox_update(&bbox, scene_vertices[triangles[t].v[i]]);
}
return bbox;
}
// Partition the given triangles into two groups: those (mostly) left of
// and mostly right of the given split plane, based on a triangle's
// bound for the given split axis. Sets the parameters num_triangles_left
// and num_triangles_right when done.
static void
partition_on_split_value(int *num_triangles_left, int *num_triangles_right,
triangle *first_triangle, int num_triangles,
int split_axis, float split_value)
{
triangle *l, *r, t;
triangle *last_triangle;
last_triangle = first_triangle + num_triangles - 1;
l = first_triangle;
r = last_triangle;
while (l < r)
{
// Increase "l" to first triangle that needs to be on the *right* (yes, right)
while (l <= last_triangle && is_left(l, split_axis, split_value))
l++;
// Decrease "r" to first triangle that needs to be on the *left*
while (r >= first_triangle && !is_left(r, split_axis, split_value))
r--;
if (l < r)
{
// Swap triangles to put them in the correct lists
t = *l;
*l = *r;
*r = t;
// Update pointers as we have now processed 2 triangles
l++;
r--;
}
}
// When we get here, l >= r
if (l > last_triangle)
{
// All triangles on the left of the split plane
*num_triangles_left = num_triangles;
*num_triangles_right = 0;
}
else if (r < first_triangle)
{
// All triangles on the right
*num_triangles_left = 0;
*num_triangles_right = num_triangles;
}
else
{
// Triangles on both sides of the split plane
*num_triangles_left = l - first_triangle;
*num_triangles_right = num_triangles - *num_triangles_left;
if (l == r)
{
// The triangle pointed to by both l and r hasn't been processed yet
if (is_left(l, split_value, split_axis))
{
// Correct the triangle count in this case, as we included
// the triangle on the right initially, while it is actually
// on the left of the split plane
*num_triangles_left++;
*num_triangles_right--;
}
}
}
}
// Recursively subdivide triangles into two groups, by determining
// a split plane and sorting the triangles into "left-of" and "right-of".
static bvh_node*
bvh_build_recursive(int depth, boundingbox bbox, triangle* triangles, int num_triangles)
{
vec3 bbox_size, bbox_center;
int i, j, t;
int sorted_dimensions[3];
int split_axis;
float split_value;
int num_triangles_left, num_triangles_right;
boundingbox left_bbox, right_bbox;
bvh_node *left_child, *right_child;
if (depth == max_depth || num_triangles <= acceptable_leaf_size)
{
// Create a leaf node
#ifdef VERBOSE
if (num_triangles > acceptable_leaf_size)
printf("[%d] Maximum depth reached, forced to create a leaf of %d triangles\n", depth, num_triangles);
else
printf("[%d] Creating a leaf node of %d triangles\n", depth, num_triangles);
#endif
return create_leaf_node(bbox, triangles, num_triangles);
}
//
// Split the triangles into two groups, using a split plane based
// on largest bbox side
//
bbox_size = v3_subtract(bbox.max, bbox.min);
bbox_center = v3_multiply(v3_add(bbox.min, bbox.max), 0.5);
// Sort bbox sides by size in descending order
sorted_dimensions[0] = 0;
sorted_dimensions[1] = 1;
sorted_dimensions[2] = 2;
// Good old bubble sort :)
for (i = 0; i < 2; i++)
{
for (j = i+1; j < 3; j++)
{
if (v3_component(bbox_size, sorted_dimensions[i])
<
v3_component(bbox_size, sorted_dimensions[j]))
{
t = sorted_dimensions[i];
sorted_dimensions[i] = sorted_dimensions[j];
sorted_dimensions[j] = t;
}
}
}
// Iterate over the three split dimensions in descending order of
// bbox size on that dimension. When the triangles are unsplitable
// (or not very favorably) continue to the next dimension. Create a
// leaf with all triangles in case none of dimensions is a good
// split candidate.
for (i = 0; i < 3; i++)
{
// Partition the triangles on the chosen split axis, with the
// split plane at the center of the bbox
split_axis = sorted_dimensions[i];
split_value = v3_component(bbox_center, split_axis);
#ifdef VERBOSE
printf("[%d] Splitting on axis %d, value %.3f\n", depth, split_axis, split_value);
#endif
partition_on_split_value(&num_triangles_left, &num_triangles_right,
triangles, num_triangles, split_axis, split_value);
#ifdef VERBOSE
printf("[%d] %d left, %d right\n", depth, num_triangles_left, num_triangles_right);
#endif
if (num_triangles_left == 0 || num_triangles_right == 0)
continue;
// Determine bboxes for the two new groups of triangles
left_bbox = bound_triangles(triangles, num_triangles_left);
if (v3_component(v3_subtract(left_bbox.max, left_bbox.min), split_axis)
>
0.9 * v3_component(bbox_size, split_axis))
{
//printf("[%d] skipping dimension (left)\n", split_axis);
continue;
}
#ifdef VERBOSE
printf("[%d] left bbox: %.3f, %.3f, %.3f .. %.3f, %.3f, %.3f\n",
depth, left_bbox.min.x, left_bbox.min.y, left_bbox.min.z,
left_bbox.max.x, left_bbox.max.y, left_bbox.max.z);
#endif
right_bbox = bound_triangles(triangles+num_triangles_left, num_triangles_right);
if (v3_component(v3_subtract(right_bbox.max, right_bbox.min), split_axis)
>
0.9 * v3_component(bbox_size, split_axis))
{
//printf("[%d] skipping dimension (right)\n", split_axis);
continue;
}
#ifdef VERBOSE
printf("[%d] right bbox: %.3f, %.3f, %.3f .. %.3f, %.3f, %.3f\n",
depth, right_bbox.min.x, right_bbox.min.y, right_bbox.min.z,
right_bbox.max.x, right_bbox.max.y, right_bbox.max.z);
#endif
// Recurse using the two new groups
left_child = bvh_build_recursive(depth+1, left_bbox, triangles, num_triangles_left);
right_child = bvh_build_recursive(depth+1, right_bbox, triangles+num_triangles_left, num_triangles_right);
// Create an inner code with two children
return create_inner_node(left_child, right_child);
}
// Split dimensions exhausted, forced to create a leaf
#ifdef VERBOSE
printf("Split dimensions exhausted, creating a leaf of %d triangles\n", num_triangles);
#endif
return create_leaf_node(bbox, triangles, num_triangles);
}
// Build a BVH for the triangles in the scene
void
bvh_build(void)
{
boundingbox bbox;
int estimated_num_leafs;
// Compute the bounding box of all the triangles.
// This equals the bounding box of all the vertices (assuming
// all vertices are actually used)
bbox = bbox_create();
for (int i = 0; i < scene_num_vertices; i++)
bbox_update(&bbox, scene_vertices[i]);
printf("bvh_build():\n");
printf("... scene bounding box:\n");
printf("... (min) %.3f, %.3f, %.3f\n", bbox.min.x, bbox.min.y, bbox.min.z);
printf("... (max) %.3f, %.3f, %.3f\n", bbox.max.x, bbox.max.y, bbox.max.z);
num_leafs = 0;
num_inner_nodes = 0;
max_leaf_size = 0;
// A balanced BVH built fully to depth D wil have
// 2^(D-1)-1 inner nodes
// and
// 2^(D-1) leaf nodes
// Divide the total number of triangles to store by acceptable_leaf_size
// to get an estimate of the number of leaf nodes needed. Then, set
// the maximum depth to a value that will allow that number of
// leafs to be reached.
// As the tree in reality will almost always not be balanced, this
// does not guarantee that there won't be instances in which the
// maximum depth is reached and a leaf needs to be created. So we
// add 10 more levels just to be sure :)
estimated_num_leafs = (int)(1.0 * scene_num_triangles / acceptable_leaf_size + 0.5);
printf("... Estimated number of leaf nodes needed = %d\n", estimated_num_leafs);
max_depth = 10 + ceil(1 + log10(estimated_num_leafs) / log10(2.0));
printf("... Setting max_depth to %d\n", max_depth);
// Build the BVH
bvh_root = bvh_build_recursive(1, bbox, scene_triangles, scene_num_triangles);
// Done
printf("Done building BVH for %d scene triangles\n", scene_num_triangles);
printf("... tree has %d leaf nodes, %d inner nodes\n",
num_leafs, num_inner_nodes);
printf("... maximum leaf size %d\n", max_leaf_size);
}
#ifndef BVH_H
#define BVH_H
#include <assert.h>
#include "bbox.h"
// Flag that controls if intersection testing is done with the use
// of the BVH
int use_bvh;
//
// One node in the Bounding Volume Hierarchy
// We use a single data type for this to avoid having to cast
// pointer values and such.
//
// We can't create a typedef bvh_node, as a _bvh_node contains
// pointers to _bvh_node's (its children)
#define bvh_node struct _bvh_node
struct _bvh_node
{
// Boolean determining if this node is a leaf node or an inner node
int is_leaf;
// The bounding box for this node
boundingbox bbox;
// Depending on the is_leaf flag access .leaf or .inner
union
{
// Leaf node
struct
{
int num_triangles;
triangle *triangles;
}
leaf;
// Inner node
struct
{
// Neither of these will be NULL
bvh_node *left_child, *right_child;
}
inner;
}
u;
};
// Build a BVH for the triangles in the scene.
// This function is called from read_scene().
void bvh_build(void);
// The root node of the hierarchy.
// Set by bvh_build()
bvh_node *bvh_root;
// Return a child node for an inner node
// (either left or right child)
static inline bvh_node*
inner_node_left_child(const bvh_node *n)
{
assert (!n->is_leaf && "Node n is not an inner node!");
return n->u.inner.left_child;
}
static inline bvh_node*
inner_node_right_child(const bvh_node *n)
{
assert (!n->is_leaf && "Node n is not an inner node!");
return n->u.inner.right_child;
}
// Return the (number of) triangles for a leaf node
static inline int
leaf_node_num_triangles(const bvh_node *n)
{
assert (n->is_leaf && "Node n is not a leaf node!");
return n->u.leaf.num_triangles;
}
static inline triangle*
leaf_node_triangles(const bvh_node *n)
{
assert (n->is_leaf && "Node n is not a leaf node!");
return n->u.leaf.triangles;
}
#endif
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <math.h>
#include "types.h"
#define C_EPSILON 1.0e-6
#define C_INFINITY 1.0e6
#ifndef M_PI
#define M_PI 3.1415926535897932384626
#endif
#endif
light 9 -4.0 10.0 0.7
light -10 2.0 15.0 0.3
material 1
sphere 0.65 -0.6 0.4 0.2
sphere -0.6 0.75 0.4 0.4
sphere -0.5 0.15 0.2 0.2
ply_file ../scenes/plateau_subdivided.ply
ply_file ../scenes/kubus.ply
ply_file ../scenes/tetra.ply
ply_file ../scenes/cow.ply
/* Computer Graphics, Assignment, Ray-tracing 2
*
* Student name ....
* Student email ...
* Collegekaart ....
* Date ............
* Comments ........
*
*
* (always fill in these fields before submitting!!)
*/
#include <math.h>
#include <stdio.h>
#include "intersection.h"
#include "v3math.h"
#include "constants.h"
#include "scene.h"
#include "bvh.h"
// A few counters for gathering statistics on the number and types
// of ray shot
// The total number of rays
int num_rays_shot = 0;
// Number of shadow rays
int num_shadow_rays_shot = 0;
// Number of triangles tested for intersection
int num_triangles_tested = 0;
// Number of bounding boxes tested for intersection
int num_bboxes_tested = 0;
// Forward declarations
static int find_first_intersected_bvh_triangle(intersection_point* ip,
vec3 ray_origin, vec3 ray_direction);
// Checks if the given triangle is intersected by ray with given
// origin and direction.
//
// Returns 1 if there is an intersection, or 0 otherwise.
//
// When an intersection is found the fields of 'ip' will be filled in
// with the relevant values.
//
// Note: this routine does NOT return an intersection for triangles
// whose back side faces the ray (by definition a triangle normal
// points to the triangle's front side).
// I.e. we do back-face culling here ...
//
// Code based on Moller & Trumbore, 1997, "Fast, minimum storage
// ray/triangle intersection"
static int
ray_intersects_triangle(intersection_point* ip, triangle tri,
vec3 ray_origin, vec3 ray_direction)
{
vec3 edge1, edge2;
vec3 tvec, pvec, qvec;
double det, inv_det;
double t, u, v; // u, v are barycentric coordinates
// t is ray parameter
num_triangles_tested++;
edge1 = v3_subtract(scene_vertices[tri.v[1]], scene_vertices[tri.v[0]]);
edge2 = v3_subtract(scene_vertices[tri.v[2]], scene_vertices[tri.v[0]]);
pvec = v3_crossprod(ray_direction, edge2);
det = v3_dotprod(edge1, pvec);
if (det < 1.0e-6)
return 0;
tvec = v3_subtract(ray_origin, scene_vertices[tri.v[0]]);
u = v3_dotprod(tvec, pvec);
if (u < 0.0 || u > det)
return 0;
qvec = v3_crossprod(tvec, edge1);
v = v3_dotprod(ray_direction, qvec);
if (v < 0.0 || u+v > det)
return 0;
t = v3_dotprod(edge2, qvec);
if (t < 0.0)
return 0;
inv_det = 1.0 / det;
t *= inv_det;
u *= inv_det;
v *= inv_det;
// We have a triangle intersection!
// Return the relevant intersection values.
// Compute the actual intersection point
ip->t = t;
ip->p = v3_add(ray_origin, v3_multiply(ray_direction, t));
// Compute an interpolated normal for this intersection point, i.e.
// we use the barycentric coordinates as weights for the vertex normals
ip->n = v3_normalize(v3_add(
v3_add(
v3_multiply(tri.vn[0], 1.0-u-v),
v3_multiply(tri.vn[1], u)
),
v3_multiply(tri.vn[2], v)));
ip->i = v3_normalize(v3_negate(ray_direction));
ip->material = tri.material;
return 1;
}
// Check if the given sphere is intersected by the given ray.
// See Shirley et.al., section 10.3.1
// Returns 1 if there is an intersection (and sets the appropriate
// fields of ip), or 0 otherwise.
static int
ray_intersects_sphere(intersection_point* ip, sphere sph,
vec3 ray_origin, vec3 ray_direction)
{
float A, B, C, D;
vec3 diff;
float t_hit;
A = v3_dotprod(ray_direction, ray_direction);
diff = v3_subtract(ray_origin, sph.center);
B = 2.0 * v3_dotprod(diff, ray_direction);
C = v3_dotprod(diff, diff) - sph.radius * sph.radius;
D = B*B - 4*A*C;
if (D < 0.0)
return 0;
D = sqrt(D);
// We're only interested in the first hit, i.e. the one with
// the smallest t_hit, so we check -B-D first, followed by -B+D
t_hit = (-B - D)/(2*A);
if (t_hit < 0.0)
{
t_hit = (-B + D)/(2*A);
if (t_hit < 0.0)
return 0;
}
ip->t = t_hit;
ip->p = v3_add(ray_origin, v3_multiply(ray_direction, t_hit));
ip->n = v3_normalize(v3_subtract(ip->p, sph.center));
ip->i = v3_normalize(v3_negate(ray_direction));
ip->material = sph.material;
return 1;
}
// Checks for an intersection of the given ray with the triangles
// stored in the BVH.
//
// Returns 1 if there is an intersection. The fields of 'ip' will be
// set to the relevant values. The intersection returned
// will be the one closest to the ray origin.
//
// Returns 0 if there are no intersections
static int
find_first_intersected_bvh_triangle(intersection_point* ip,
vec3 ray_origin, vec3 ray_direction)
{
return 0;
}
// Returns the nearest hit of the given ray with objects in the scene
// (either a sphere or a triangle).
//
// Returns 1 and sets the intersection point values if there
// is an intersection, returns 0 otherwise.
int
find_first_intersection(intersection_point *ip, vec3 ray_origin, vec3 ray_direction)
{
int have_hit;
float t_nearest;
intersection_point ip2;
num_rays_shot++;
// We have found no hit yet
t_nearest = C_INFINITY;
have_hit = 0;
// First check against spheres in the scene
for (int s = 0; s < scene_num_spheres; s++)
{
// We need a second set of p and n variables, as there's the
// possibility that we'll overwrite a closer intersection already
// found
if (ray_intersects_sphere(&ip2, scene_spheres[s], ray_origin, ray_direction))
{
if (ip2.t < t_nearest)
{
*ip = ip2;
t_nearest = ip2.t;
have_hit = 1;
}
}
}
// Then check against triangles in the scene
if (use_bvh)
{
// Use the BVH to speed up intersection testing
if (find_first_intersected_bvh_triangle(&ip2, ray_origin, ray_direction))
{
if (ip2.t < t_nearest)
{
*ip = ip2;
t_nearest = ip2.t;
have_hit = 1;
}
}
}
else
{
// Simply iterate over all the triangles in the scene and check for intersection
for (int t = 0; t < scene_num_triangles; t++)
{
if (ray_intersects_triangle(&ip2, scene_triangles[t], ray_origin, ray_direction))
{
if (ip2.t < t_nearest)
{
*ip = ip2;
t_nearest = ip2.t;
have_hit = 1;
}
}
}
}
return have_hit;
}
// Optimized routine for tracing a shadow ray.
//
// This routine doesn't return the nearest intersection, but simply
// checks if there is any intersection.
int
shadow_check(vec3 ray_origin, vec3 ray_direction)
{
intersection_point ip;
num_rays_shot++;
num_shadow_rays_shot++;
for (int s = 0; s < scene_num_spheres; s++)
{
if (ray_intersects_sphere(&ip, scene_spheres[s], ray_origin, ray_direction))
return 1;
}
if (use_bvh)
{
// Use the BVH for speedy intersection testing
if (find_first_intersected_bvh_triangle(&ip, ray_origin, ray_direction))
return 1;
}
else
{
// Simply iterate over all the triangles in the scene and check for intersection
for (int t = 0; t < scene_num_triangles; t++)
{
if (ray_intersects_triangle(&ip, scene_triangles[t], ray_origin, ray_direction))
return 1;
}
}
return 0;
}
#ifndef INTERSECTION_H
#define INTERSECTION_H
#include "types.h"
int num_rays_shot;
int num_shadow_rays_shot;
int num_triangles_tested;
int num_bboxes_tested;
int find_first_intersection(intersection_point *ip,
vec3 ray_origin, vec3 ray_direction);
int shadow_check(vec3 ray_origin, vec3 ray_direction);
#endif
/* Computer Graphics, Assignment, Ray-tracing 2
*
* Student name ....
* Student email ...
* Collegekaart ....
* Date ............
* Comments ........
*
*
* (always fill in these fields before submitting!!)
*/
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include "plymodel.h"
#include "v3math.h"
#include "shaders.h"
#include "perlin.h"
#include "intersection.h"
#include "scene.h"
#include "constants.h"
#include "bvh.h"
// Number of drawable pixels, i.e. x coordinates passed to PutPixel()
// should be in the range [0, framebuffer_width[. Analogous for y.
// (These values must both be a power of 2)
const int framebuffer_width = 512;
const int framebuffer_height = 512;
// Camera vertical field-of-view
const float VFOV = 45.0;
byte *framebuffer;
int show_raytraced=0;
int needs_rerender=1;
int show_bvh=0;
int draw_bvh_mode=0;
int show_normals=0;
int do_antialiasing=0;
float camDistance = 6.5;
float camRotZ = 25.0, camAzimuth = -40.0;
float saved_camRotZ, saved_camAzimuth, saved_camDistance;
int mouse_mode = 0;
int mx, my;
viewpoint viewpoints[6] =
{
{ -40.0, 25.0, 6.5 },
{ -27.5, -30.5, 2.0 },
{ -73.8, 37.0, 3.8 },
{ 46.2, 0.0, 6.2 },
{ -35.0, -187.2, 2.8 },
{ -80.3, 27.0, 1.1 },
};
void
init_opengl(void)
{
// Set the background color
glClearColor(1, 1, 1, 0);
// Allocate a framebuffer, to be filled during ray tracing
framebuffer = (byte*) malloc(framebuffer_width*framebuffer_height*3);
// Setup texturing state (as we display the framebuffer
// using a textured quad)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
GLfloat light_ambient[] = {0.4,0.4,0.4,0.0};
GLfloat mat_shininess[] = { 50.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
}
void
resize(int w, int h)
{
if (h == 0)
h = 1;
glViewport(0, 0, w, h);
}
void
put_pixel(int x, int y, float r, float g, float b)
{
if (x < 0 || y < 0 || x >= framebuffer_width || y >= framebuffer_height)
{
printf("put_pixel(): x, y coordinates (%d, %d) outside of visible area!\n",
x, y);
return;
}
// The pixels in framebuffer[] are layed out sequentially,
// with the R, G and B values one after the each, e.g
// RGBRGBRGB...
framebuffer[3*(framebuffer_width*y+x)] = (int)(255*r);
framebuffer[3*(framebuffer_width*y+x)+1] = (int)(255*g);
framebuffer[3*(framebuffer_width*y+x)+2] = (int)(255*b);
}
void
setup_camera(void)
{
float cx, cy, cz;
float t;
float beta, gamma;
// degrees -> radians
beta = camAzimuth / 180.0 * M_PI;
gamma = camRotZ / 180.0 * M_PI;
cx = camDistance;
cy = cz = 0.0;
// Rotate around Y
t = cx;
cx = cx * cos(beta) + cz * sin(beta);
// cy remains unchanged
cz = -t * sin(beta) + cz * cos(beta);
// Rotate around Z
t = cx;
cx = cx * cos(gamma) - cy * sin(gamma);
cy = t * sin(gamma) + cy * cos(gamma);
// cz remains unchanged
scene_camera_position.x = cx;
scene_camera_position.y = cy;
scene_camera_position.z = cz;
scene_camera_lookat.x = 0.0;
scene_camera_lookat.y = 0.0;
scene_camera_lookat.z = 0.5;
// Assumes matrix mode is model-view
glLoadIdentity();
gluLookAt (cx, cy, cz, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0);
}
void
ray_trace(void)
{
vec3 forward_vector, right_vector, up_vector;
int i, j;
vec3 right_step, down_step;
float image_plane_width, image_plane_height;
vec3 image_plane_topleft, image_plane_point;
vec3 ray_direction;
vec3 color;
char buf[128];
struct timeval t0, t1;
float time_taken;
fprintf(stderr, "Ray tracing ...");
gettimeofday(&t0, NULL);
num_rays_shot = num_shadow_rays_shot = num_triangles_tested = num_bboxes_tested = 0;
// Compute camera coordinate system from camera position
// and look-at point
up_vector = v3_create(0, 0, 1);
forward_vector = v3_normalize(v3_subtract(scene_camera_lookat, scene_camera_position));
right_vector = v3_normalize(v3_crossprod(forward_vector, up_vector));
up_vector = v3_crossprod(right_vector, forward_vector);
// Compute size of image plane from the chosen field-of-view
// and image aspect ratio. This is the size of the plane at distance
// of one unit from the camera position.
image_plane_height = 2.0 * tan(0.5*VFOV/180*M_PI);
image_plane_width = image_plane_height * (1.0 * framebuffer_width / framebuffer_height);
// Compute the size of one "pixel step"
right_step = v3_multiply(right_vector, image_plane_width / (framebuffer_width-1));
down_step = v3_multiply(v3_negate(up_vector), image_plane_height / (framebuffer_height-1));
// Compute top-left corner of image plane
image_plane_topleft = v3_add(scene_camera_position, forward_vector);
image_plane_topleft = v3_add(image_plane_topleft,
v3_multiply(right_vector, -0.5*image_plane_width));
image_plane_topleft = v3_add(image_plane_topleft,
v3_multiply(up_vector, 0.5*image_plane_height));
// Loop over all pixels in the framebuffer
for (j = 0; j < framebuffer_height; j++)
{
for (i = 0; i < framebuffer_width; i++)
{
// Compute corresponding point on image plane
image_plane_point = v3_add(image_plane_topleft, v3_multiply(right_step, i+0.5));
image_plane_point = v3_add(image_plane_point, v3_multiply(down_step, j+0.5));
// Compute direction for shooting the ray
ray_direction = v3_subtract(image_plane_point, scene_camera_position);
// Determine ray color
color = ray_color(0, scene_camera_position, ray_direction);
// Output pixel color
put_pixel(i, j, color.x, color.y, color.z);
}
sprintf(buf, "Ray-tracing ::: %.0f%% done", 100.0*j/framebuffer_height);
glutSetWindowTitle(buf);
}
// Done!
gettimeofday(&t1, NULL);
glutSetWindowTitle("Ray-tracing ::: done");
// Output some statistics
time_taken = 1.0 * (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1000000.0;
fprintf(stderr, " done in %.1f seconds\n", time_taken);
fprintf(stderr, "... %d total rays shot, of which %d camera rays and %d shadow rays\n",
num_rays_shot,
do_antialiasing ? 4*framebuffer_width*framebuffer_height : framebuffer_width*framebuffer_height,
num_shadow_rays_shot);
fprintf(stderr, "... %d triangles intersection tested (avg %.1f tri/ray)\n",
num_triangles_tested, 1.0*num_triangles_tested/num_rays_shot);
fprintf(stderr, "... %d bboxes intersection tested (avg %.1f bbox/ray)\n",
num_bboxes_tested, 1.0*num_bboxes_tested/num_rays_shot);
}
// Draw the node bboxes of the BVH, for inner nodes at a certain
// level in the tree
static void
draw_bvh_inner_nodes(int level, bvh_node* node)
{
vec3 center, size;
if (node->is_leaf)
return;
if (level == draw_bvh_mode)
{
center = v3_multiply(v3_add(node->bbox.min, node->bbox.max), 0.5);
size = v3_subtract(node->bbox.max, node->bbox.min);
glColor3f(1, 0, 0);
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(size.x, size.y, size.z);
glutWireCube(1.0);
glPopMatrix();
}
else
{
draw_bvh_inner_nodes(level+1, node->u.inner.left_child);
draw_bvh_inner_nodes(level+1, node->u.inner.right_child);
}
}
// Draw leaf node bounding boxes
static void
draw_bvh_leaf_nodes(bvh_node* node)
{
vec3 center, size;
if (node->is_leaf)
{
center = v3_multiply(v3_add(node->bbox.min, node->bbox.max), 0.5);
size = v3_subtract(node->bbox.max, node->bbox.min);
glColor3f(0, 0, 1);
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(size.x, size.y, size.z);
glutWireCube(1.0);
glPopMatrix();
}
else
{
draw_bvh_leaf_nodes(node->u.inner.left_child);
draw_bvh_leaf_nodes(node->u.inner.right_child);
}
}
void
draw_scene(void)
{
// clear the draw buffer
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
if (show_raytraced)
{
if (needs_rerender)
{
// clear the framebuffer
memset(framebuffer, 255, 3*framebuffer_width*framebuffer_height);
// trace a new picture
ray_trace();
needs_rerender = 0;
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Draw the framebuffer using a textured quad
glOrtho(0, framebuffer_width, 0, framebuffer_height, -1, 1);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,
framebuffer_width, framebuffer_height,
0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer);
glColor3f(1, 1, 1);
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(0, framebuffer_height);
glTexCoord2i(0, 1);
glVertex2i(0, 0);
glTexCoord2i(1, 1);
glVertex2i(framebuffer_width, 0);
glTexCoord2i(1, 0);
glVertex2i(framebuffer_width, framebuffer_height);
glEnd();
}
else
{
// Draw scene using OpenGL
//glutSetWindowTitle("OpenGL view");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(VFOV, 1.0*framebuffer_width/framebuffer_height, 0.1, 1000.0);
glMatrixMode(GL_MODELVIEW);
setup_camera();
glEnable(GL_LIGHTING);
glEnable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
// Try to set up the lighting to match the scene
GLfloat v[4];
v[0] = scene_ambient_light;
v[1] = scene_ambient_light;
v[2] = scene_ambient_light;
v[3] = 1.0;
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, v);
for (int l = 0; l < scene_num_lights; l++)
{
glEnable(GL_LIGHT0 + l);
v[0] = scene_lights[l].position.x;
v[1] = scene_lights[l].position.y;
v[2] = scene_lights[l].position.z;
v[3] = 1.0; // we want a positional light source
glLightfv(GL_LIGHT0 + l, GL_POSITION, v);
v[0] = v[1] = v[2] = v[3] = 0.0;
glLightfv(GL_LIGHT0 + l, GL_AMBIENT, v);
glLightfv(GL_LIGHT0 + l, GL_SPECULAR, v);
v[0] = v[1] = v[2] = scene_lights[l].intensity;
v[3] = 1.0;
glLightfv(GL_LIGHT0 + l, GL_DIFFUSE, v);
}
GLfloat one[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat zero[] = { 0.0, 0.0, 0.0, 1.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, one);
// Draw the triangles in the scene
triangle tri;
int p, q, r;
glBegin(GL_TRIANGLES);
for (int t = 0; t < scene_num_triangles; t++)
{
tri = scene_triangles[t];
p = tri.v[0];
q = tri.v[1];
r = tri.v[2];
glNormal3f(tri.vn[0].x, tri.vn[0].y, tri.vn[0].z);
glVertex3f(scene_vertices[p].x, scene_vertices[p].y, scene_vertices[p].z);
glNormal3f(tri.vn[1].x, tri.vn[1].y, tri.vn[1].z);
glVertex3f(scene_vertices[q].x, scene_vertices[q].y, scene_vertices[q].z);
glNormal3f(tri.vn[2].x, tri.vn[2].y, tri.vn[2].z);
glVertex3f(scene_vertices[r].x, scene_vertices[r].y, scene_vertices[r].z);
}
glEnd();
if (show_normals)
{
// Draw vertex normals as red lines
glDisable(GL_LIGHTING);
glColor3f(1, 0, 0);
glBegin(GL_LINES);
vec3 n;
for (int t = 0; t < scene_num_triangles; t++)
{
tri = scene_triangles[t];
for (int vi = 0; vi < 3; vi++)
{
p = tri.v[vi];
n = scene_vertices[p];
glVertex3f(n.x, n.y, n.z);
n = v3_add(n, v3_multiply(tri.vn[vi], 0.05));
glVertex3f(n.x, n.y, n.z);
}
}
glEnd();
glEnable(GL_LIGHTING);
}
// Draw the spheres in the scene
for (int s = 0; s < scene_num_spheres; s++)
{
glPushMatrix();
glTranslatef(scene_spheres[s].center.x, scene_spheres[s].center.y, scene_spheres[s].center.z);
glutSolidSphere(scene_spheres[s].radius, 12, 12);
glPopMatrix();
}
// Show BVH node bboxes
if (show_bvh)
{
glDisable(GL_LIGHTING);
if (draw_bvh_mode == 0)
draw_bvh_leaf_nodes(bvh_root);
else
draw_bvh_inner_nodes(1, bvh_root);
}
/*
// Draw some axes
glDisable(GL_LIGHTING);
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3f(0, 0, 0);
glVertex3f(10, 0, 0);
glColor3f(0, 1, 0);
glVertex3f(0, 0, 0);
glVertex3f(0, 10, 0);
glColor3f(0, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, 10);
glEnd();
*/
}
// finally, swap the draw buffers to make the triangles appear on screen
glutSwapBuffers();
}
void
save_image(void)
{
FILE *f;
f = fopen("image.ppm", "wt");
if (!f)
{
fprintf(stderr, "Could not create image file\n");
return;
}
fprintf(f, "P3\n# Raytraced image\n%d %d\n255\n", framebuffer_width, framebuffer_height);
for (int i = 0; i < 3*framebuffer_width*framebuffer_height; i++)
fprintf(f, "%d\n", framebuffer[i]);
printf("Image saved to image.ppm\n");
fclose(f);
}
void
key_pressed(unsigned char key, int x, int y)
{
switch (key)
{
case 'r':
{
// Toggle between OpenGL and ray-traced output
show_raytraced = 1 - show_raytraced;
if (show_raytraced)
glutSetWindowTitle("Ray-tracing [ray-traced output]");
else
glutSetWindowTitle("Ray-tracing [OpenGL view]");
glutPostRedisplay();
break;
}
case 'a':
{
// Toggle anti-aliasing (forces immediate re-render)
do_antialiasing = 1 - do_antialiasing;
needs_rerender = 1;
glutPostRedisplay();
break;
}
case 'b':
{
// Toggle use of the BVH for intersection testing
// (forces immediate re-render)
use_bvh = 1 - use_bvh;
printf("use_bvh set to %d\n", use_bvh);
needs_rerender = 1;
glutPostRedisplay();
break;
}
case 'B':
{
// Show BVH nodes
show_bvh = 1 - show_bvh;
if (show_bvh)
draw_bvh_mode = 0;
glutPostRedisplay();
break;
}
case ']':
{
draw_bvh_mode++;
glutPostRedisplay();
break;
}
case '[':
{
draw_bvh_mode--;
if (draw_bvh_mode < 0)
draw_bvh_mode = 0;
glutPostRedisplay();
break;
}
case 'n':
{
// Show vertex normals
show_normals = 1 - show_normals;
glutPostRedisplay();
break;
}
case 'c':
{
// Dump camera parameters
printf("azimuth = %.1f, rot_z = %.1f, distance = %.1f\n",
camAzimuth, camRotZ, camDistance);
break;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
{
// Switch to a predefined viewpoint
int idx = (int)(key - '1');
camAzimuth = viewpoints[idx].azimuth;
camRotZ = viewpoints[idx].rot_z;
camDistance = viewpoints[idx].distance;
// Switch to OpenGL viewing
show_raytraced = 0;
// And since the camera params changed we need to rerender
needs_rerender = 1;
glutPostRedisplay();
break;
}
case 's':
{
// Save rendered image to (ascii) .ppm file 'image.ppm'
save_image();
break;
}
case 'm':
{
// Display mouse coordinates (for debugging only)
printf("x = %d, y = %d\n", x, y);
break;
}
case 'q':
exit(0);
}
}
static void
mouse_func(int button, int state, int x, int y)
{
if (show_raytraced)
return;
// guard against both left and right buttons being pressed at the same time,
// by only responding when a mouse button is pressed while another one
// hasn't been pressed yet
if (state == GLUT_DOWN && mouse_mode == 0)
{
if (button == GLUT_LEFT_BUTTON)
{
mouse_mode = GLUT_LEFT_BUTTON;
saved_camRotZ = camRotZ;
saved_camAzimuth = camAzimuth;
mx = x;
my = y;
}
else if (button == GLUT_RIGHT_BUTTON)
{
mouse_mode = GLUT_RIGHT_BUTTON;
saved_camDistance = camDistance;
my = y;
}
}
else if (state == GLUT_UP && button == mouse_mode)
{
// pressed button released
mouse_mode = 0;
}
}
static void
motion_func(int x, int y)
{
int dx, dy;
if (show_raytraced)
return;
if (mouse_mode == GLUT_LEFT_BUTTON)
{
dx = mx - x;
dy = my - y;
camRotZ = saved_camRotZ + dx * 0.25;
camAzimuth = saved_camAzimuth + dy * 0.25;
if (camAzimuth > 89.99)
camAzimuth = 89.99;
else if (camAzimuth < -89.99)
camAzimuth = -89.99;
needs_rerender = 1;
}
else if (mouse_mode == GLUT_RIGHT_BUTTON)
{
dy = my - y;
camDistance = saved_camDistance - dy * 0.15;
if (camDistance < 0.5)
camDistance = 0.5;
else if (camDistance > 100.0)
camDistance = 100.0;
needs_rerender = 1;
}
}
int
main(int argc, char **argv)
{
glutInit(&argc, argv);
if (--argc != 1)
{
printf("Usage: %s file.scn\n", argv[0]);
exit(-1);
}
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(framebuffer_width, framebuffer_height);
glutInitWindowPosition(20, 100);
glutCreateWindow("Ray tracing");
glutDisplayFunc(&draw_scene);
glutIdleFunc(&draw_scene);
glutReshapeFunc(&resize);
//glutSpecialFunc(&specialKeyPressed);
glutKeyboardFunc(&key_pressed);
glutMouseFunc(&mouse_func);
glutMotionFunc(&motion_func);
read_scene(argv[1]);
init_opengl();
init_noise();
glutMainLoop();
return 1;
}
light 9 -4.0 10.0 0.7
light -10 2.0 15.0 0.3
material 1
sphere 0.65 -0.6 0.4 0.2
sphere -0.6 0.75 0.4 0.4
sphere -0.5 0.15 0.2 0.2
ply_file ../scenes/plateau.ply
ply_file ../scenes/kubus.ply
ply_file ../scenes/tetra.ply
/* Implemention of Perlin's improved noise; See "Improving Noise", SIGGRAPH 2002 */
#include <math.h>
#include <stdio.h>
#include "perlin.h"
static int p[512];
static int permutation[] = {
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,
21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,
230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,
80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,
164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,
118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,
183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,
218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,
145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,
115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,
141,128,195,78,66,215,61,156,180
};
/* Function declarations */
static double fade(double t);
static double lerp(double t, double a, double b);
static double grad(int hash, double x, double y, double z);
void init_noise(void)
{
int i;
for(i = 0; i < 256 ; i++)
p[256+i] = p[i] = permutation[i];
}
// Signed noise
// Result is in [-1,1]
double snoise(double x, double y, double z)
{
int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
Y = (int)floor(y) & 255, /* CONTAINS POINT. */
Z = (int)floor(z) & 255;
x -= floor(x); /* FIND RELATIVE X,Y,Z */
y -= floor(y); /* OF POINT IN CUBE. */
z -= floor(z);
double u = fade(x), /* COMPUTE FADE CURVES */
v = fade(y), /* FOR EACH OF X,Y,Z. */
w = fade(z);
int A = p[X]+Y,
AA = p[A]+Z,
AB = p[A+1]+Z, /* HASH COORDINATES OF */
B = p[X+1]+Y,
BA = p[B]+Z,
BB = p[B+1]+Z; /* THE 8 CUBE CORNERS, */
return lerp(w,lerp(v,lerp(u, grad(p[AA ], x, y, z), /* AND ADD */
grad(p[BA ], x-1, y, z)), /* BLENDED */
lerp(u, grad(p[AB ], x, y-1, z), /* RESULTS */
grad(p[BB ], x-1, y-1, z))), /* FROM 8 */
lerp(v, lerp(u, grad(p[AA+1], x, y, z-1 ),/* CORNERS */
grad(p[BA+1], x-1, y, z-1)), /* OF CUBE */
lerp(u, grad(p[AB+1], x, y-1, z-1),
grad(p[BB+1], x-1, y-1, z-1))));
}
// Result is in [0,1]
double noise(double x, double y, double z)
{
return 0.5 * (1.0 + snoise(x, y, z));
}
static double
fade(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
static double
lerp(double t, double a, double b)
{
return a + t * (b - a);
}
static double
grad(int hash, double x, double y, double z)
{
int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
v = h < 4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
#ifndef PERLIN_H
#define PERLIN_H
void init_noise(void);
double noise(double x, double y, double z);
double snoise(double x, double y, double z);
#endif
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include "rply.h"
#include "plymodel.h"
#include "v3math.h"
static int vertex_cb(p_ply_argument argument);
static int normal_cb(p_ply_argument argument);
static int face_cb(p_ply_argument argument);
static void dump(void);
static int vertex_element_idx, normal_element_idx, face_element_idx;
void
read_ply_model(const char *fname)
{
int i, j, k;
p_ply ply = ply_open(fname, NULL);
// XXX handle better
if (!ply) return;
if (!ply_read_header(ply)) return;
ply_num_vertices = ply_set_read_cb(ply, "vertex", "x", vertex_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "y", vertex_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "z", vertex_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "nx", normal_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "ny", normal_cb, NULL, 0);
ply_set_read_cb(ply, "vertex", "nz", normal_cb, NULL, 0);
int ply_num_faces = ply_set_read_cb(ply, "face", "vertex_indices", face_cb, NULL, 0);
fprintf(stderr, "Reading %s\n", fname);
ply_vertices = malloc(ply_num_vertices * sizeof(vec3));
ply_normals = malloc(ply_num_vertices * sizeof(vec3));
// XXX this will overallocate if some of the faces aren't triangles
ply_triangles = malloc(ply_num_faces * sizeof(triangle));
// will be updated by the callbacks
ply_num_vertices = 0;
ply_num_normals = 0;
ply_num_triangles = 0;
vertex_element_idx = 0;
normal_element_idx = 0;
face_element_idx = 0;
// XXX
if (!ply_read(ply))
{
fprintf(stderr, "\nFATAL: Could not read .ply file %s!\n", fname);
exit(-1);
}
fprintf(stderr, "... %d vertices, %d normals, %d triangles\n",
ply_num_vertices, ply_num_normals, ply_num_triangles);
// Compute triangle normals
fprintf(stderr, "... Computing triangle normals ... ");
for (int t = 0; t < ply_num_triangles; t++)
{
i = ply_triangles[t].v[0];
j = ply_triangles[t].v[1];
k = ply_triangles[t].v[2];
// Calculate normal and store it with the triangle
ply_triangles[t].n = v3_normalize(
v3_crossprod(
v3_subtract(ply_vertices[j], ply_vertices[i]),
v3_subtract(ply_vertices[k], ply_vertices[i])));
}
if (ply_num_normals == ply_num_vertices)
{
// Simply copy triangle vertex normals
for (int t = 0; t < ply_num_triangles; t++)
{
for (i = 0; i < 3; i++)
ply_triangles[t].vn[i] = ply_normals[ply_triangles[t].v[i]];
}
}
else
{
// Incorrect number of vertex normals provided.
// Set triangle's vertex normals to triangle normal, i.e.
// flat shading
for (int t = 0; t < ply_num_triangles; t++)
{
for (i = 0; i < 3; i++)
ply_triangles[t].vn[i] = ply_triangles[t].n;
}
}
fprintf(stderr, "done\n");
// all done...
ply_close(ply);
}
int
vertex_cb(p_ply_argument argument)
{
double v = ply_get_argument_value(argument);
switch (vertex_element_idx)
{
case 0:
ply_vertices[ply_num_vertices].x = v;
break;
case 1:
ply_vertices[ply_num_vertices].y = v;
break;
case 2:
ply_vertices[ply_num_vertices].z = v;
break;
}
if (vertex_element_idx == 2)
{
ply_num_vertices++;
vertex_element_idx = 0;
}
else
vertex_element_idx++;
return 1;
}
int
normal_cb(p_ply_argument argument)
{
double v = ply_get_argument_value(argument);
switch (normal_element_idx)
{
case 0:
ply_normals[ply_num_normals].x = v;
break;
case 1:
ply_normals[ply_num_normals].y = v;
break;
case 2:
ply_normals[ply_num_normals].z = v;
break;
}
if (normal_element_idx == 2)
{
ply_num_normals++;
normal_element_idx = 0;
}
else
normal_element_idx++;
return 1;
}
int
face_cb(p_ply_argument argument)
{
int32 length, value_index;
ply_get_argument_property(argument, NULL, &length, &value_index);
// Skip any polygon that isn't a triangle
if (length == 3 && value_index >= 0)
{
int idx = (int)ply_get_argument_value(argument);
ply_triangles[ply_num_triangles].v[face_element_idx] = idx;
if (face_element_idx == 2)
{
ply_num_triangles++;
face_element_idx = 0;
}
else
face_element_idx++;
}
return 1;
}
static void
dump(void)
{
int i;
for (i = 0; i < ply_num_vertices; i++)
{
if (ply_num_normals == ply_num_vertices)
{
printf("[%d] %.6f, %.6f, %.6f (%.6f, %.6f, %.6f)\n", i,
ply_vertices[i].x, ply_vertices[i].y, ply_vertices[i].z,
ply_normals[i].x, ply_normals[i].y, ply_normals[i].z);
}
else
{
printf("[%d] %.6f, %.6f, %.6f\n", i, ply_vertices[i].x,
ply_vertices[i].y, ply_vertices[i].z);
}
}
for (i = 0; i < ply_num_triangles; i++)
{
printf("[%d] %d %d %d\n", i, ply_triangles[i].v[0],
ply_triangles[i].v[1], ply_triangles[i].v[2]);
}
}
#ifndef PLYMODEL_H
#define PLYMODEL_H
#include "types.h"
int ply_num_triangles, ply_num_vertices, ply_num_normals;
triangle *ply_triangles;
vec3 *ply_vertices;
vec3 *ply_normals;
void read_ply_model(const char *fname);
#endif
#include <math.h>
#include "quat.h"
#include "constants.h"
// Create a quaternion that represents a rotation of
// angle degrees around the given axis.
quat
quat_create_rotation(vec3 axis, float angle)
{
float sin_a;
quat res;
axis = v3_normalize(axis);
sin_a = sin(0.5 * (angle/180.0*M_PI));
res.x = axis.x * sin_a;
res.y = axis.y * sin_a;
res.z = axis.z * sin_a;
res.w = cos(0.5 * (angle/180.0*M_PI));
return quat_normalize(res);
}
// Create a quaternion representation of point p
quat
quat_create_point(vec3 p)
{
quat res;
res.x = p.x;
res.y = p.y;
res.z = p.z;
res.w = 0.0;
return res;
}
// Calculate quaternion magnitude
float
quat_magnitude(quat q)
{
return sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
}
// Normalize a quaternion
quat
quat_normalize(quat q)
{
quat res = q;
float inv_mag = 1.0 / quat_magnitude(q);
res.x *= inv_mag;
res.y *= inv_mag;
res.z *= inv_mag;
res.w *= inv_mag;
return res;
}
quat
quat_conjugate(quat q)
{
quat res = q;
res.x = -q.x;
res.y = -q.y;
res.z = -q.z;
return res;
}
quat
quat_inverse(quat q)
{
float invlen = 1.0 / quat_magnitude(q);
quat res = quat_conjugate(q);
invlen = invlen * invlen;
res.x *= invlen;
res.y *= invlen;
res.z *= invlen;
res.w *= invlen;
return res;
}
quat
quat_multiply(quat q, quat r)
{
quat res;
res.w = q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z;
res.x = q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y;
res.y = q.w*r.y + q.y*r.w + q.z*r.x - q.x*r.z;
res.z = q.w*r.z + q.z*r.w + q.x*r.y - q.y*r.x;
return res;
}
#ifndef QUAT_H
#define QUAT_H
#include "v3math.h"
// A quaternion, used to represent rotations around arbitrary angles
typedef struct
{
float x, y, z, w;
}
quat;
// Create a quaternion that represents a rotation of
// angle degrees around the given axis.
quat quat_create_rotation(vec3 axis, float angle);
// Create a quaternion representation of point p
quat quat_create_point(vec3 p);
// Calculate quaternion magnitude
float quat_magnitude(quat q);
// Normalize a quaternion
quat quat_normalize(quat q);
// Calculate the conjugate (i.e. (-v, w) for quaternion (v, w))
quat quat_conjugate(quat q);
// Calculate the inverse of a quaterion
quat quat_inverse(quat q);
// Transform
vec3 quat_transform_vector(quat q, vec3 v);
quat quat_multiply(quat q, quat r);
#endif
/* ----------------------------------------------------------------------
* RPly library, read/write PLY files
* Diego Nehab, Princeton University
* http://www.cs.princeton.edu/~diego/professional/rply
*
* This library is distributed under the MIT License. See notice
* at the end of this file.
* ---------------------------------------------------------------------- */
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <float.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stddef.h>
#include "rply.h"
/* ----------------------------------------------------------------------
* Constants
* ---------------------------------------------------------------------- */
#define WORDSIZE 256
#define LINESIZE 1024
#define BUFFERSIZE (8*1024)
typedef enum e_ply_io_mode_ {
PLY_READ,
PLY_WRITE
} e_ply_io_mode;
static const char *const ply_storage_mode_list[] = {
"binary_big_endian", "binary_little_endian", "ascii", NULL
}; /* order matches e_ply_storage_mode enum */
static const char *const ply_type_list[] = {
"int8", "uint8", "int16", "uint16",
"int32", "uint32", "float32", "float64",
"char", "uchar", "short", "ushort",
"int", "uint", "float", "double",
"list", NULL
}; /* order matches e_ply_type enum */
/* ----------------------------------------------------------------------
* Property reading callback argument
*
* element: name of element being processed
* property: name of property being processed
* nelements: number of elements of this kind in file
* instance_index: index current element of this kind being processed
* length: number of values in current list (or 1 for scalars)
* value_index: index of current value int this list (or 0 for scalars)
* value: value of property
* pdata/idata: user data defined with ply_set_cb
*
* Returns handle to ply file if succesful, NULL otherwise.
* ---------------------------------------------------------------------- */
typedef struct t_ply_argument_ {
p_ply_element element;
int32 instance_index;
p_ply_property property;
int32 length, value_index;
double value;
void *pdata;
int32 idata;
} t_ply_argument;
/* ----------------------------------------------------------------------
* Property information
*
* name: name of this property
* type: type of this property (list or type of scalar value)
* length_type, value_type: type of list property count and values
* read_cb: function to be called when this property is called
*
* Returns 1 if should continue processing file, 0 if should abort.
* ---------------------------------------------------------------------- */
typedef struct t_ply_property_ {
char name[WORDSIZE];
e_ply_type type, value_type, length_type;
p_ply_read_cb read_cb;
void *pdata;
int32 idata;
} t_ply_property;
/* ----------------------------------------------------------------------
* Element information
*
* name: name of this property
* ninstances: number of elements of this type in file
* property: property descriptions for this element
* nproperty: number of properties in this element
*
* Returns 1 if should continue processing file, 0 if should abort.
* ---------------------------------------------------------------------- */
typedef struct t_ply_element_ {
char name[WORDSIZE];
int32 ninstances;
p_ply_property property;
int32 nproperties;
} t_ply_element;
/* ----------------------------------------------------------------------
* Input/output driver
*
* Depending on file mode, different functions are used to read/write
* property fields. The drivers make it transparent to read/write in ascii,
* big endian or little endian cases.
* ---------------------------------------------------------------------- */
typedef int (*p_ply_ihandler)(p_ply ply, double *value);
typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size);
typedef struct t_ply_idriver_ {
p_ply_ihandler ihandler[16];
p_ply_ichunk ichunk;
const char *name;
} t_ply_idriver;
typedef t_ply_idriver *p_ply_idriver;
typedef int (*p_ply_ohandler)(p_ply ply, double value);
typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size);
typedef struct t_ply_odriver_ {
p_ply_ohandler ohandler[16];
p_ply_ochunk ochunk;
const char *name;
} t_ply_odriver;
typedef t_ply_odriver *p_ply_odriver;
/* ----------------------------------------------------------------------
* Ply file handle.
*
* io_mode: read or write (from e_ply_io_mode)
* storage_mode: mode of file associated with handle (from e_ply_storage_mode)
* element: elements description for this file
* nelement: number of different elements in file
* comment: comments for this file
* ncomments: number of comments in file
* obj_info: obj_info items for this file
* nobj_infos: number of obj_info items in file
* fp: file pointer associated with ply file
* c: last character read from ply file
* buffer: last word/chunck of data read from ply file
* buffer_first, buffer_last: interval of untouched good data in buffer
* buffer_token: start of parsed token (line or word) in buffer
* idriver, odriver: input driver used to get property fields from file
* argument: storage space for callback arguments
* welement, wproperty: element/property type being written
* winstance_index: index of instance of current element being written
* wvalue_index: index of list property value being written
* wlength: number of values in list property being written
* error_cb: callback for error messages
* ---------------------------------------------------------------------- */
typedef struct t_ply_ {
e_ply_io_mode io_mode;
e_ply_storage_mode storage_mode;
p_ply_element element;
int32 nelements;
char *comment;
int32 ncomments;
char *obj_info;
int32 nobj_infos;
FILE *fp;
int c;
char buffer[BUFFERSIZE];
size_t buffer_first, buffer_token, buffer_last;
p_ply_idriver idriver;
p_ply_odriver odriver;
t_ply_argument argument;
int32 welement, wproperty;
int32 winstance_index, wvalue_index, wlength;
p_ply_error_cb error_cb;
} t_ply;
/* ----------------------------------------------------------------------
* I/O functions and drivers
* ---------------------------------------------------------------------- */
static t_ply_idriver ply_idriver_ascii;
static t_ply_idriver ply_idriver_binary;
static t_ply_idriver ply_idriver_binary_reverse;
static t_ply_odriver ply_odriver_ascii;
static t_ply_odriver ply_odriver_binary;
static t_ply_odriver ply_odriver_binary_reverse;
static int ply_read_word(p_ply ply);
static int ply_check_word(p_ply ply);
static int ply_read_line(p_ply ply);
static int ply_check_line(p_ply ply);
static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size);
static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size);
static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
static void ply_reverse(void *anydata, size_t size);
/* ----------------------------------------------------------------------
* String functions
* ---------------------------------------------------------------------- */
static int ply_find_string(const char *item, const char* const list[]);
static p_ply_element ply_find_element(p_ply ply, const char *name);
static p_ply_property ply_find_property(p_ply_element element,
const char *name);
/* ----------------------------------------------------------------------
* Header parsing
* ---------------------------------------------------------------------- */
static int ply_read_header_format(p_ply ply);
static int ply_read_header_comment(p_ply ply);
static int ply_read_header_obj_info(p_ply ply);
static int ply_read_header_property(p_ply ply);
static int ply_read_header_element(p_ply ply);
/* ----------------------------------------------------------------------
* Error handling
* ---------------------------------------------------------------------- */
static void ply_error_cb(const char *message);
static void ply_error(p_ply ply, const char *fmt, ...);
/* ----------------------------------------------------------------------
* Memory allocation and initialization
* ---------------------------------------------------------------------- */
static void ply_init(p_ply ply);
static void ply_element_init(p_ply_element element);
static void ply_property_init(p_ply_property property);
static p_ply ply_alloc(void);
static p_ply_element ply_grow_element(p_ply ply);
static p_ply_property ply_grow_property(p_ply ply, p_ply_element element);
static void *ply_grow_array(p_ply ply, void **pointer, int32 *nmemb, int32 size);
/* ----------------------------------------------------------------------
* Special functions
* ---------------------------------------------------------------------- */
static e_ply_storage_mode ply_arch_endian(void);
static int ply_type_check(void);
/* ----------------------------------------------------------------------
* Auxiliary read functions
* ---------------------------------------------------------------------- */
static int ply_read_element(p_ply ply, p_ply_element element,
p_ply_argument argument);
static int ply_read_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument);
static int ply_read_list_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument);
static int ply_read_scalar_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument);
/* ----------------------------------------------------------------------
* Buffer support functions
* ---------------------------------------------------------------------- */
/* pointers to tokenized word and line in buffer */
#define BWORD(p) (p->buffer + p->buffer_token)
#define BLINE(p) (p->buffer + p->buffer_token)
/* pointer to start of untouched bytes in buffer */
#define BFIRST(p) (p->buffer + p->buffer_first)
/* number of bytes untouched in buffer */
#define BSIZE(p) (p->buffer_last - p->buffer_first)
/* consumes data from buffer */
#define BSKIP(p, s) (p->buffer_first += s)
/* refills the buffer */
static int BREFILL(p_ply ply) {
/* move untouched data to beginning of buffer */
size_t size = BSIZE(ply);
memmove(ply->buffer, BFIRST(ply), size);
ply->buffer_last = size;
ply->buffer_first = ply->buffer_token = 0;
/* fill remaining with new data */
size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp);
/* place sentinel so we can use str* functions with buffer */
ply->buffer[BUFFERSIZE-1] = '\0';
/* check if read failed */
if (size <= 0) return 0;
/* increase size to account for new data */
ply->buffer_last += size;
return 1;
}
/* ----------------------------------------------------------------------
* Exported functions
* ---------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
* Read support functions
* ---------------------------------------------------------------------- */
p_ply ply_open(const char *name, p_ply_error_cb error_cb) {
char magic[5] = " ";
FILE *fp = NULL;
p_ply ply = NULL;
if (error_cb == NULL) error_cb = ply_error_cb;
if (!ply_type_check()) {
error_cb("Incompatible type system");
return NULL;
}
assert(name);
fp = fopen(name, "rb");
if (!fp) {
error_cb("Unable to open file");
return NULL;
}
if (fread(magic, 1, 4, fp) < 4) {
error_cb("Error reading from file");
fclose(fp);
return NULL;
}
if (strcmp(magic, "ply\n")) {
fclose(fp);
error_cb("Not a PLY file. Expected magic number 'ply\\n'");
return NULL;
}
ply = ply_alloc();
if (!ply) {
error_cb("Out of memory");
fclose(fp);
return NULL;
}
ply->fp = fp;
ply->io_mode = PLY_READ;
ply->error_cb = error_cb;
return ply;
}
int ply_read_header(p_ply ply) {
assert(ply && ply->fp && ply->io_mode == PLY_READ);
if (!ply_read_word(ply)) return 0;
/* parse file format */
if (!ply_read_header_format(ply)) {
ply_error(ply, "Invalid file format");
return 0;
}
/* parse elements, comments or obj_infos until the end of header */
while (strcmp(BWORD(ply), "end_header")) {
if (!ply_read_header_comment(ply) &&
!ply_read_header_element(ply) &&
!ply_read_header_obj_info(ply)) {
ply_error(ply, "Unexpected token '%s'", BWORD(ply));
return 0;
}
}
return 1;
}
int32 ply_set_read_cb(p_ply ply, const char *element_name,
const char* property_name, p_ply_read_cb read_cb,
void *pdata, int32 idata) {
p_ply_element element = NULL;
p_ply_property property = NULL;
assert(ply && element_name && property_name);
element = ply_find_element(ply, element_name);
if (!element) return 0;
property = ply_find_property(element, property_name);
if (!property) return 0;
property->read_cb = read_cb;
property->pdata = pdata;
property->idata = idata;
return (int) element->ninstances;
}
int ply_read(p_ply ply) {
int32 i;
p_ply_argument argument;
assert(ply && ply->fp && ply->io_mode == PLY_READ);
argument = &ply->argument;
/* for each element type */
for (i = 0; i < ply->nelements; i++) {
p_ply_element element = &ply->element[i];
argument->element = element;
if (!ply_read_element(ply, element, argument))
return 0;
}
return 1;
}
/* ----------------------------------------------------------------------
* Write support functions
* ---------------------------------------------------------------------- */
p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
p_ply_error_cb error_cb) {
FILE *fp = NULL;
p_ply ply = NULL;
if (error_cb == NULL) error_cb = ply_error_cb;
if (!ply_type_check()) {
error_cb("Incompatible type system");
return NULL;
}
assert(name && storage_mode <= PLY_DEFAULT);
fp = fopen(name, "wb");
if (!fp) {
error_cb("Unable to create file");
return NULL;
}
ply = ply_alloc();
if (!ply) {
fclose(fp);
error_cb("Out of memory");
return NULL;
}
ply->io_mode = PLY_WRITE;
if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian();
if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii;
else if (storage_mode == ply_arch_endian())
ply->odriver = &ply_odriver_binary;
else ply->odriver = &ply_odriver_binary_reverse;
ply->storage_mode = storage_mode;
ply->fp = fp;
ply->error_cb = error_cb;
return ply;
}
int ply_add_element(p_ply ply, const char *name, int32 ninstances) {
p_ply_element element = NULL;
assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
assert(name && strlen(name) < WORDSIZE && ninstances >= 0);
if (strlen(name) >= WORDSIZE || ninstances < 0) {
ply_error(ply, "Invalid arguments");
return 0;
}
element = ply_grow_element(ply);
if (!element) return 0;
strcpy(element->name, name);
element->ninstances = ninstances;
return 1;
}
int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) {
p_ply_element element = NULL;
p_ply_property property = NULL;
assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
assert(name && strlen(name) < WORDSIZE);
assert(type < PLY_LIST);
if (strlen(name) >= WORDSIZE || type >= PLY_LIST) {
ply_error(ply, "Invalid arguments");
return 0;
}
element = &ply->element[ply->nelements-1];
property = ply_grow_property(ply, element);
if (!property) return 0;
strcpy(property->name, name);
property->type = type;
return 1;
}
int ply_add_list_property(p_ply ply, const char *name,
e_ply_type length_type, e_ply_type value_type) {
p_ply_element element = NULL;
p_ply_property property = NULL;
assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
assert(name && strlen(name) < WORDSIZE);
if (strlen(name) >= WORDSIZE) {
ply_error(ply, "Invalid arguments");
return 0;
}
assert(length_type < PLY_LIST);
assert(value_type < PLY_LIST);
if (length_type >= PLY_LIST || value_type >= PLY_LIST) {
ply_error(ply, "Invalid arguments");
return 0;
}
element = &ply->element[ply->nelements-1];
property = ply_grow_property(ply, element);
if (!property) return 0;
strcpy(property->name, name);
property->type = PLY_LIST;
property->length_type = length_type;
property->value_type = value_type;
return 1;
}
int ply_add_property(p_ply ply, const char *name, e_ply_type type,
e_ply_type length_type, e_ply_type value_type) {
if (type == PLY_LIST)
return ply_add_list_property(ply, name, length_type, value_type);
else
return ply_add_scalar_property(ply, name, type);
}
int ply_add_comment(p_ply ply, const char *comment) {
char *new_comment = NULL;
assert(ply && comment && strlen(comment) < LINESIZE);
if (!comment || strlen(comment) >= LINESIZE) {
ply_error(ply, "Invalid arguments");
return 0;
}
new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment,
&ply->ncomments, LINESIZE);
if (!new_comment) return 0;
strcpy(new_comment, comment);
return 1;
}
int ply_add_obj_info(p_ply ply, const char *obj_info) {
char *new_obj_info = NULL;
assert(ply && obj_info && strlen(obj_info) < LINESIZE);
if (!obj_info || strlen(obj_info) >= LINESIZE) {
ply_error(ply, "Invalid arguments");
return 0;
}
new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info,
&ply->nobj_infos, LINESIZE);
if (!new_obj_info) return 0;
strcpy(new_obj_info, obj_info);
return 1;
}
int ply_write_header(p_ply ply) {
int32 i, j;
assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
assert(ply->element || ply->nelements == 0);
assert(!ply->element || ply->nelements > 0);
if (fprintf(ply->fp, "ply\nformat %s 1.0\n",
ply_storage_mode_list[ply->storage_mode]) <= 0) goto error;
for (i = 0; i < ply->ncomments; i++)
if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0)
goto error;
for (i = 0; i < ply->nobj_infos; i++)
if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0)
goto error;
for (i = 0; i < ply->nelements; i++) {
p_ply_element element = &ply->element[i];
assert(element->property || element->nproperties == 0);
assert(!element->property || element->nproperties > 0);
if (fprintf(ply->fp, "element %s %ld\n", element->name,
element->ninstances) <= 0) goto error;
for (j = 0; j < element->nproperties; j++) {
p_ply_property property = &element->property[j];
if (property->type == PLY_LIST) {
if (fprintf(ply->fp, "property list %s %s %s\n",
ply_type_list[property->length_type],
ply_type_list[property->value_type],
property->name) <= 0) goto error;
} else {
if (fprintf(ply->fp, "property %s %s\n",
ply_type_list[property->type],
property->name) <= 0) goto error;
}
}
}
return fprintf(ply->fp, "end_header\n") > 0;
error:
ply_error(ply, "Error writing to file");
return 0;
}
int ply_write(p_ply ply, double value) {
p_ply_element element = NULL;
p_ply_property property = NULL;
int type = -1;
int breakafter = 0;
if (ply->welement > ply->nelements) return 0;
element = &ply->element[ply->welement];
if (ply->wproperty > element->nproperties) return 0;
property = &element->property[ply->wproperty];
if (property->type == PLY_LIST) {
if (ply->wvalue_index == 0) {
type = property->length_type;
ply->wlength = (int32) value;
} else type = property->value_type;
} else {
type = property->type;
ply->wlength = 0;
}
if (!ply->odriver->ohandler[type](ply, value)) {
ply_error(ply, "Failed writing %s of %s %d (%s: %s)",
property->name, element->name,
ply->winstance_index,
ply->odriver->name, ply_type_list[type]);
return 0;
}
ply->wvalue_index++;
if (ply->wvalue_index > ply->wlength) {
ply->wvalue_index = 0;
ply->wproperty++;
}
if (ply->wproperty >= element->nproperties) {
ply->wproperty = 0;
ply->winstance_index++;
if (ply->storage_mode == PLY_ASCII) breakafter = 1;
}
if (ply->winstance_index >= element->ninstances) {
ply->winstance_index = 0;
ply->welement++;
}
return !breakafter || putc('\n', ply->fp) > 0;
}
int ply_close(p_ply ply) {
int32 i;
assert(ply && ply->fp);
assert(ply->element || ply->nelements == 0);
assert(!ply->element || ply->nelements > 0);
/* write last chunk to file */
if (ply->io_mode == PLY_WRITE &&
fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) {
ply_error(ply, "Error closing up");
return 0;
}
fclose(ply->fp);
/* free all memory used by handle */
if (ply->element) {
for (i = 0; i < ply->nelements; i++) {
p_ply_element element = &ply->element[i];
if (element->property) free(element->property);
}
free(ply->element);
}
if (ply->obj_info) free(ply->obj_info);
if (ply->comment) free(ply->comment);
free(ply);
return 1;
}
/* ----------------------------------------------------------------------
* Query support functions
* ---------------------------------------------------------------------- */
p_ply_element ply_get_next_element(p_ply ply,
p_ply_element last) {
assert(ply);
if (!last) return ply->element;
last++;
if (last < ply->element + ply->nelements) return last;
else return NULL;
}
int ply_get_element_info(p_ply_element element, const char** name,
int32 *ninstances) {
assert(element);
if (name) *name = element->name;
if (ninstances) *ninstances = (int32) element->ninstances;
return 1;
}
p_ply_property ply_get_next_property(p_ply_element element,
p_ply_property last) {
assert(element);
if (!last) return element->property;
last++;
if (last < element->property + element->nproperties) return last;
else return NULL;
}
int ply_get_property_info(p_ply_property property, const char** name,
e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type) {
assert(property);
if (name) *name = property->name;
if (type) *type = property->type;
if (length_type) *length_type = property->length_type;
if (value_type) *value_type = property->value_type;
return 1;
}
const char *ply_get_next_comment(p_ply ply, const char *last) {
assert(ply);
if (!last) return ply->comment;
last += LINESIZE;
if (last < ply->comment + LINESIZE*ply->ncomments) return last;
else return NULL;
}
const char *ply_get_next_obj_info(p_ply ply, const char *last) {
assert(ply);
if (!last) return ply->obj_info;
last += LINESIZE;
if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last;
else return NULL;
}
/* ----------------------------------------------------------------------
* Callback argument support functions
* ---------------------------------------------------------------------- */
int ply_get_argument_element(p_ply_argument argument,
p_ply_element *element, int32 *instance_index) {
assert(argument);
if (!argument) return 0;
if (element) *element = argument->element;
if (instance_index) *instance_index = argument->instance_index;
return 1;
}
int ply_get_argument_property(p_ply_argument argument,
p_ply_property *property, int32 *length, int32 *value_index) {
assert(argument);
if (!argument) return 0;
if (property) *property = argument->property;
if (length) *length = argument->length;
if (value_index) *value_index = argument->value_index;
return 1;
}
int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
int32 *idata) {
assert(argument);
if (!argument) return 0;
if (pdata) *pdata = argument->pdata;
if (idata) *idata = argument->idata;
return 1;
}
double ply_get_argument_value(p_ply_argument argument) {
assert(argument);
if (!argument) return 0.0;
return argument->value;
}
/* ----------------------------------------------------------------------
* Internal functions
* ---------------------------------------------------------------------- */
static int ply_read_list_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument) {
int l;
p_ply_read_cb read_cb = property->read_cb;
p_ply_ihandler *driver = ply->idriver->ihandler;
/* get list length */
p_ply_ihandler handler = driver[property->length_type];
double length;
if (!handler(ply, &length)) {
ply_error(ply, "Error reading '%s' of '%s' number %d",
property->name, element->name, argument->instance_index);
return 0;
}
/* invoke callback to pass length in value field */
argument->length = (int32) length;
argument->value_index = -1;
argument->value = length;
if (read_cb && !read_cb(argument)) {
ply_error(ply, "Aborted by user");
return 0;
}
/* read list values */
handler = driver[property->value_type];
/* for each value in list */
for (l = 0; l < (int32) length; l++) {
/* read value from file */
argument->value_index = l;
if (!handler(ply, &argument->value)) {
ply_error(ply, "Error reading value number %d of '%s' of "
"'%s' number %d", l+1, property->name,
element->name, argument->instance_index);
return 0;
}
/* invoke callback to pass value */
if (read_cb && !read_cb(argument)) {
ply_error(ply, "Aborted by user");
return 0;
}
}
return 1;
}
static int ply_read_scalar_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument) {
p_ply_read_cb read_cb = property->read_cb;
p_ply_ihandler *driver = ply->idriver->ihandler;
p_ply_ihandler handler = driver[property->type];
argument->length = 1;
argument->value_index = 0;
if (!handler(ply, &argument->value)) {
ply_error(ply, "Error reading '%s' of '%s' number %d",
property->name, element->name, argument->instance_index);
return 0;
}
if (read_cb && !read_cb(argument)) {
ply_error(ply, "Aborted by user");
return 0;
}
return 1;
}
static int ply_read_property(p_ply ply, p_ply_element element,
p_ply_property property, p_ply_argument argument) {
if (property->type == PLY_LIST)
return ply_read_list_property(ply, element, property, argument);
else
return ply_read_scalar_property(ply, element, property, argument);
}
static int ply_read_element(p_ply ply, p_ply_element element,
p_ply_argument argument) {
int32 j, k;
/* for each element of this type */
for (j = 0; j < element->ninstances; j++) {
argument->instance_index = j;
/* for each property */
for (k = 0; k < element->nproperties; k++) {
p_ply_property property = &element->property[k];
argument->property = property;
argument->pdata = property->pdata;
argument->idata = property->idata;
if (!ply_read_property(ply, element, property, argument))
return 0;
}
}
return 1;
}
static int ply_find_string(const char *item, const char* const list[]) {
int i;
assert(item && list);
for (i = 0; list[i]; i++)
if (!strcmp(list[i], item)) return i;
return -1;
}
static p_ply_element ply_find_element(p_ply ply, const char *name) {
p_ply_element element;
int i, nelements;
assert(ply && name);
element = ply->element;
nelements = ply->nelements;
assert(element || nelements == 0);
assert(!element || nelements > 0);
for (i = 0; i < nelements; i++)
if (!strcmp(element[i].name, name)) return &element[i];
return NULL;
}
static p_ply_property ply_find_property(p_ply_element element,
const char *name) {
p_ply_property property;
int i, nproperties;
assert(element && name);
property = element->property;
nproperties = element->nproperties;
assert(property || nproperties == 0);
assert(!property || nproperties > 0);
for (i = 0; i < nproperties; i++)
if (!strcmp(property[i].name, name)) return &property[i];
return NULL;
}
static int ply_check_word(p_ply ply) {
if (strlen(BLINE(ply)) >= WORDSIZE) {
ply_error(ply, "Word too long");
return 0;
}
return 1;
}
static int ply_read_word(p_ply ply) {
size_t t = 0;
assert(ply && ply->fp && ply->io_mode == PLY_READ);
/* skip leading blanks */
while (1) {
t = strspn(BFIRST(ply), " \n\r\t");
/* check if all buffer was made of blanks */
if (t >= BSIZE(ply)) {
if (!BREFILL(ply)) {
ply_error(ply, "Unexpected end of file");
return 0;
}
} else break;
}
BSKIP(ply, t);
/* look for a space after the current word */
t = strcspn(BFIRST(ply), " \n\r\t");
/* if we didn't reach the end of the buffer, we are done */
if (t < BSIZE(ply)) {
ply->buffer_token = ply->buffer_first;
BSKIP(ply, t);
*BFIRST(ply) = '\0';
BSKIP(ply, 1);
return ply_check_word(ply);
}
/* otherwise, try to refill buffer */
if (!BREFILL(ply)) {
ply_error(ply, "Unexpected end of file");
return 0;
}
/* keep looking from where we left */
t += strcspn(BFIRST(ply) + t, " \n\r\t");
/* check if the token is too large for our buffer */
if (t >= BSIZE(ply)) {
ply_error(ply, "Token too large");
return 0;
}
/* we are done */
ply->buffer_token = ply->buffer_first;
BSKIP(ply, t);
*BFIRST(ply) = '\0';
BSKIP(ply, 1);
return ply_check_word(ply);
}
static int ply_check_line(p_ply ply) {
if (strlen(BLINE(ply)) >= LINESIZE) {
ply_error(ply, "Line too long");
return 0;
}
return 1;
}
static int ply_read_line(p_ply ply) {
const char *end = NULL;
assert(ply && ply->fp && ply->io_mode == PLY_READ);
/* look for a end of line */
end = strchr(BFIRST(ply), '\n');
/* if we didn't reach the end of the buffer, we are done */
if (end) {
ply->buffer_token = ply->buffer_first;
BSKIP(ply, end - BFIRST(ply));
*BFIRST(ply) = '\0';
BSKIP(ply, 1);
return ply_check_line(ply);
} else {
end = ply->buffer + BSIZE(ply);
/* otherwise, try to refill buffer */
if (!BREFILL(ply)) {
ply_error(ply, "Unexpected end of file");
return 0;
}
}
/* keep looking from where we left */
end = strchr(end, '\n');
/* check if the token is too large for our buffer */
if (!end) {
ply_error(ply, "Token too large");
return 0;
}
/* we are done */
ply->buffer_token = ply->buffer_first;
BSKIP(ply, end - BFIRST(ply));
*BFIRST(ply) = '\0';
BSKIP(ply, 1);
return ply_check_line(ply);
}
static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) {
char *buffer = (char *) anybuffer;
size_t i = 0;
assert(ply && ply->fp && ply->io_mode == PLY_READ);
assert(ply->buffer_first <= ply->buffer_last);
while (i < size) {
if (ply->buffer_first < ply->buffer_last) {
buffer[i] = ply->buffer[ply->buffer_first];
ply->buffer_first++;
i++;
} else {
ply->buffer_first = 0;
ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp);
if (ply->buffer_last <= 0) return 0;
}
}
return 1;
}
static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) {
char *buffer = (char *) anybuffer;
size_t i = 0;
assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
assert(ply->buffer_last <= BUFFERSIZE);
while (i < size) {
if (ply->buffer_last < BUFFERSIZE) {
ply->buffer[ply->buffer_last] = buffer[i];
ply->buffer_last++;
i++;
} else {
ply->buffer_last = 0;
if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE)
return 0;
}
}
return 1;
}
static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
int ret = 0;
ply_reverse(anybuffer, size);
ret = ply_write_chunk(ply, anybuffer, size);
ply_reverse(anybuffer, size);
return ret;
}
static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
if (!ply_read_chunk(ply, anybuffer, size)) return 0;
ply_reverse(anybuffer, size);
return 1;
}
static void ply_reverse(void *anydata, size_t size) {
char *data = (char *) anydata;
char temp;
size_t i;
for (i = 0; i < size/2; i++) {
temp = data[i];
data[i] = data[size-i-1];
data[size-i-1] = temp;
}
}
static void ply_init(p_ply ply) {
ply->c = ' ';
ply->element = NULL;
ply->nelements = 0;
ply->comment = NULL;
ply->ncomments = 0;
ply->obj_info = NULL;
ply->nobj_infos = 0;
ply->idriver = NULL;
ply->odriver = NULL;
ply->buffer[0] = '\0';
ply->buffer_first = ply->buffer_last = ply->buffer_token = 0;
ply->welement = 0;
ply->wproperty = 0;
ply->winstance_index = 0;
ply->wlength = 0;
ply->wvalue_index = 0;
}
static void ply_element_init(p_ply_element element) {
element->name[0] = '\0';
element->ninstances = 0;
element->property = NULL;
element->nproperties = 0;
}
static void ply_property_init(p_ply_property property) {
property->name[0] = '\0';
property->type = -1;
property->length_type = -1;
property->value_type = -1;
property->read_cb = (p_ply_read_cb) NULL;
property->pdata = NULL;
property->idata = 0;
}
static p_ply ply_alloc(void) {
p_ply ply = (p_ply) malloc(sizeof(t_ply));
if (!ply) return NULL;
ply_init(ply);
return ply;
}
static void *ply_grow_array(p_ply ply, void **pointer,
int32 *nmemb, int32 size) {
void *temp = *pointer;
int32 count = *nmemb + 1;
if (!temp) temp = malloc(count*size);
else temp = realloc(temp, count*size);
if (!temp) {
ply_error(ply, "Out of memory");
return NULL;
}
*pointer = temp;
*nmemb = count;
return (char *) temp + (count-1) * size;
}
static p_ply_element ply_grow_element(p_ply ply) {
p_ply_element element = NULL;
assert(ply);
assert(ply->element || ply->nelements == 0);
assert(!ply->element || ply->nelements > 0);
element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element,
&ply->nelements, sizeof(t_ply_element));
if (!element) return NULL;
ply_element_init(element);
return element;
}
static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) {
p_ply_property property = NULL;
assert(ply);
assert(element);
assert(element->property || element->nproperties == 0);
assert(!element->property || element->nproperties > 0);
property = (p_ply_property) ply_grow_array(ply,
(void **) &element->property,
&element->nproperties, sizeof(t_ply_property));
if (!property) return NULL;
ply_property_init(property);
return property;
}
static int ply_read_header_format(p_ply ply) {
assert(ply && ply->fp && ply->io_mode == PLY_READ);
if (strcmp(BWORD(ply), "format")) return 0;
if (!ply_read_word(ply)) return 0;
ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list);
if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0;
if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii;
else if (ply->storage_mode == ply_arch_endian())
ply->idriver = &ply_idriver_binary;
else ply->idriver = &ply_idriver_binary_reverse;
if (!ply_read_word(ply)) return 0;
if (strcmp(BWORD(ply), "1.0")) return 0;
if (!ply_read_word(ply)) return 0;
return 1;
}
static int ply_read_header_comment(p_ply ply) {
assert(ply && ply->fp && ply->io_mode == PLY_READ);
if (strcmp(BWORD(ply), "comment")) return 0;
if (!ply_read_line(ply)) return 0;
if (!ply_add_comment(ply, BLINE(ply))) return 0;
if (!ply_read_word(ply)) return 0;
return 1;
}
static int ply_read_header_obj_info(p_ply ply) {
assert(ply && ply->fp && ply->io_mode == PLY_READ);
if (strcmp(BWORD(ply), "obj_info")) return 0;
if (!ply_read_line(ply)) return 0;
if (!ply_add_obj_info(ply, BLINE(ply))) return 0;
if (!ply_read_word(ply)) return 0;
return 1;
}
static int ply_read_header_property(p_ply ply) {
p_ply_element element = NULL;
p_ply_property property = NULL;
/* make sure it is a property */
if (strcmp(BWORD(ply), "property")) return 0;
element = &ply->element[ply->nelements-1];
property = ply_grow_property(ply, element);
if (!property) return 0;
/* get property type */
if (!ply_read_word(ply)) return 0;
property->type = ply_find_string(BWORD(ply), ply_type_list);
if (property->type == (e_ply_type) (-1)) return 0;
if (property->type == PLY_LIST) {
/* if it's a list, we need the base types */
if (!ply_read_word(ply)) return 0;
property->length_type = ply_find_string(BWORD(ply), ply_type_list);
if (property->length_type == (e_ply_type) (-1)) return 0;
if (!ply_read_word(ply)) return 0;
property->value_type = ply_find_string(BWORD(ply), ply_type_list);
if (property->value_type == (e_ply_type) (-1)) return 0;
}
/* get property name */
if (!ply_read_word(ply)) return 0;
strcpy(property->name, BWORD(ply));
if (!ply_read_word(ply)) return 0;
return 1;
}
static int ply_read_header_element(p_ply ply) {
p_ply_element element = NULL;
int32 dummy;
assert(ply && ply->fp && ply->io_mode == PLY_READ);
if (strcmp(BWORD(ply), "element")) return 0;
/* allocate room for new element */
element = ply_grow_element(ply);
if (!element) return 0;
/* get element name */
if (!ply_read_word(ply)) return 0;
strcpy(element->name, BWORD(ply));
/* get number of elements of this type */
if (!ply_read_word(ply)) return 0;
if (sscanf(BWORD(ply), "%ld", &dummy) != 1) {
ply_error(ply, "Expected number got '%s'", BWORD(ply));
return 0;
}
element->ninstances = dummy;
/* get all properties for this element */
if (!ply_read_word(ply)) return 0;
while (ply_read_header_property(ply) ||
ply_read_header_comment(ply) || ply_read_header_obj_info(ply))
/* do nothing */;
return 1;
}
static void ply_error_cb(const char *message) {
fprintf(stderr, "RPly: %s\n", message);
}
static void ply_error(p_ply ply, const char *fmt, ...) {
char buffer[1024];
va_list ap;
va_start(ap, fmt);
vsprintf(buffer, fmt, ap);
va_end(ap);
ply->error_cb(buffer);
}
static e_ply_storage_mode ply_arch_endian(void) {
uint32 i = 1;
unsigned char *s = (unsigned char *) &i;
if (*s == 1) return PLY_LITTLE_ENDIAN;
else return PLY_BIG_ENDIAN;
}
static int ply_type_check(void) {
assert(sizeof(char) == 1);
assert(sizeof(unsigned char) == 1);
assert(sizeof(int16) == 2);
assert(sizeof(uint16) == 2);
assert(sizeof(int32) == 4);
assert(sizeof(uint32) == 4);
assert(sizeof(float) == 4);
assert(sizeof(double) == 8);
if (sizeof(char) != 1) return 0;
if (sizeof(unsigned char) != 1) return 0;
if (sizeof(int16) != 2) return 0;
if (sizeof(uint16) != 2) return 0;
if (sizeof(int32) != 4) return 0;
if (sizeof(uint32) != 4) return 0;
if (sizeof(float) != 4) return 0;
if (sizeof(double) != 8) return 0;
return 1;
}
/* ----------------------------------------------------------------------
* Output handlers
* ---------------------------------------------------------------------- */
static int oascii_int8(p_ply ply, double value) {
if (value > CHAR_MAX || value < CHAR_MIN) return 0;
return fprintf(ply->fp, "%d ", (char) value) > 0;
}
static int oascii_uint8(p_ply ply, double value) {
if (value > UCHAR_MAX || value < 0) return 0;
return fprintf(ply->fp, "%d ", (unsigned char) value) > 0;
}
static int oascii_int16(p_ply ply, double value) {
if (value > SHRT_MAX || value < SHRT_MIN) return 0;
return fprintf(ply->fp, "%d ", (short) value) > 0;
}
static int oascii_uint16(p_ply ply, double value) {
if (value > USHRT_MAX || value < 0) return 0;
return fprintf(ply->fp, "%d ", (unsigned short) value) > 0;
}
static int oascii_int32(p_ply ply, double value) {
if (value > INT_MAX || value < INT_MIN) return 0;
return fprintf(ply->fp, "%d ", (int) value) > 0;
}
static int oascii_uint32(p_ply ply, double value) {
if (value > UINT_MAX || value < 0) return 0;
return fprintf(ply->fp, "%d ", (unsigned int) value) > 0;
}
static int oascii_float32(p_ply ply, double value) {
if (value < -FLT_MAX || value > FLT_MAX) return 0;
return fprintf(ply->fp, "%g ", (float) value) > 0;
}
static int oascii_float64(p_ply ply, double value) {
if (value < -DBL_MAX || value > DBL_MAX) return 0;
return fprintf(ply->fp, "%g ", value) > 0;
}
static int obinary_int8(p_ply ply, double value) {
char int8 = (char) value;
if (value > CHAR_MAX || value < CHAR_MIN) return 0;
return ply->odriver->ochunk(ply, &int8, sizeof(int8));
}
static int obinary_uint8(p_ply ply, double value) {
unsigned char uint8 = (unsigned char) value;
if (value > UCHAR_MAX || value < 0) return 0;
return ply->odriver->ochunk(ply, &uint8, sizeof(uint8));
}
static int obinary_int16(p_ply ply, double value) {
short value_int16 = (short) value;
if (value > SHRT_MAX || value < SHRT_MIN) return 0;
return ply->odriver->ochunk(ply, &value_int16, sizeof(value_int16));
}
static int obinary_uint16(p_ply ply, double value) {
unsigned short value_uint16 = (unsigned short) value;
if (value > USHRT_MAX || value < 0) return 0;
return ply->odriver->ochunk(ply, &value_uint16, sizeof(value_uint16));
}
static int obinary_int32(p_ply ply, double value) {
int32 value_int32 = (int32) value;
if (value > INT_MAX || value < INT_MIN) return 0;
return ply->odriver->ochunk(ply, &value_int32, sizeof(value_int32));
}
static int obinary_uint32(p_ply ply, double value) {
uint32 value_uint32 = (uint32) value;
if (value > UINT_MAX || value < 0) return 0;
return ply->odriver->ochunk(ply, &value_uint32, sizeof(value_uint32));
}
static int obinary_float32(p_ply ply, double value) {
float float32 = (float) value;
if (value > FLT_MAX || value < -FLT_MAX) return 0;
return ply->odriver->ochunk(ply, &float32, sizeof(float32));
}
static int obinary_float64(p_ply ply, double value) {
return ply->odriver->ochunk(ply, &value, sizeof(value));
}
/* ----------------------------------------------------------------------
* Input handlers
* ---------------------------------------------------------------------- */
static int iascii_int8(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value > CHAR_MAX || *value < CHAR_MIN) return 0;
return 1;
}
static int iascii_uint8(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value > UCHAR_MAX || *value < 0) return 0;
return 1;
}
static int iascii_int16(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value > SHRT_MAX || *value < SHRT_MIN) return 0;
return 1;
}
static int iascii_uint16(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value > USHRT_MAX || *value < 0) return 0;
return 1;
}
static int iascii_int32(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value > INT_MAX || *value < INT_MIN) return 0;
return 1;
}
static int iascii_uint32(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtol(BWORD(ply), &end, 10);
if (*end || *value < 0) return 0;
return 1;
}
static int iascii_float32(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtod(BWORD(ply), &end);
if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0;
return 1;
}
static int iascii_float64(p_ply ply, double *value) {
char *end;
if (!ply_read_word(ply)) return 0;
*value = strtod(BWORD(ply), &end);
if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0;
return 1;
}
static int ibinary_int8(p_ply ply, double *value) {
char int8;
if (!ply->idriver->ichunk(ply, &int8, 1)) return 0;
*value = int8;
return 1;
}
static int ibinary_uint8(p_ply ply, double *value) {
unsigned char uint8;
if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0;
*value = uint8;
return 1;
}
static int ibinary_int16(p_ply ply, double *value) {
short value_int16;
if (!ply->idriver->ichunk(ply, &value_int16, sizeof(value_int16))) return 0;
*value = value_int16;
return 1;
}
static int ibinary_uint16(p_ply ply, double *value) {
unsigned short value_uint16;
if (!ply->idriver->ichunk(ply, &value_uint16, sizeof(value_uint16))) return 0;
*value = value_uint16;
return 1;
}
static int ibinary_int32(p_ply ply, double *value) {
int32 value_int32;
if (!ply->idriver->ichunk(ply, &value_int32, sizeof(value_int32))) return 0;
*value = value_int32;
return 1;
}
static int ibinary_uint32(p_ply ply, double *value) {
uint32 value_uint32;
if (!ply->idriver->ichunk(ply, &value_uint32, sizeof(value_uint32))) return 0;
*value = value_uint32;
return 1;
}
static int ibinary_float32(p_ply ply, double *value) {
float float32;
if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0;
*value = float32;
ply_reverse(&float32, sizeof(float32));
return 1;
}
static int ibinary_float64(p_ply ply, double *value) {
return ply->idriver->ichunk(ply, value, sizeof(double));
}
/* ----------------------------------------------------------------------
* Constants
* ---------------------------------------------------------------------- */
static t_ply_idriver ply_idriver_ascii = {
{ iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
iascii_int32, iascii_uint32, iascii_float32, iascii_float64,
iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
iascii_int32, iascii_uint32, iascii_float32, iascii_float64
}, /* order matches e_ply_type enum */
NULL,
"ascii input"
};
static t_ply_idriver ply_idriver_binary = {
{ ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
}, /* order matches e_ply_type enum */
ply_read_chunk,
"binary input"
};
static t_ply_idriver ply_idriver_binary_reverse = {
{ ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
}, /* order matches e_ply_type enum */
ply_read_chunk_reverse,
"reverse binary input"
};
static t_ply_odriver ply_odriver_ascii = {
{ oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
oascii_int32, oascii_uint32, oascii_float32, oascii_float64,
oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
oascii_int32, oascii_uint32, oascii_float32, oascii_float64
}, /* order matches e_ply_type enum */
NULL,
"ascii output"
};
static t_ply_odriver ply_odriver_binary = {
{ obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
obinary_int32, obinary_uint32, obinary_float32, obinary_float64
}, /* order matches e_ply_type enum */
ply_write_chunk,
"binary output"
};
static t_ply_odriver ply_odriver_binary_reverse = {
{ obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
obinary_int32, obinary_uint32, obinary_float32, obinary_float64
}, /* order matches e_ply_type enum */
ply_write_chunk_reverse,
"reverse binary output"
};
/* ----------------------------------------------------------------------
* Copyright (C) 2003 Diego Nehab. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ---------------------------------------------------------------------- */
#ifndef PLY_H
#define PLY_H
/* ----------------------------------------------------------------------
* RPly library, read/write PLY files
* Diego Nehab, Princeton University
* http://www.cs.princeton.edu/~diego/professional/rply
*
* This library is distributed under the MIT License. See notice
* at the end of this file.
* ---------------------------------------------------------------------- */
#ifdef __cplusplus
extern "C" {
#endif
#define RPLY_VERSION "RPly 1.01"
#define RPLY_COPYRIGHT "Copyright (C) 2003-2005 Diego Nehab"
#define RPLY_AUTHORS "Diego Nehab"
/* ----------------------------------------------------------------------
* Types
* ---------------------------------------------------------------------- */
typedef short int int16;
typedef unsigned short int uint16;
typedef int int32;
typedef unsigned int uint32;
/* structures are opaque */
typedef struct t_ply_ *p_ply;
typedef struct t_ply_element_ *p_ply_element;
typedef struct t_ply_property_ *p_ply_property;
typedef struct t_ply_argument_ *p_ply_argument;
/* ply format mode type */
typedef enum e_ply_storage_mode_ {
PLY_BIG_ENDIAN,
PLY_LITTLE_ENDIAN,
PLY_ASCII,
PLY_DEFAULT /* has to be the last in enum */
} e_ply_storage_mode; /* order matches ply_storage_mode_list */
/* ply data type */
typedef enum e_ply_type {
PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16,
PLY_INT32, PLY_UIN32, PLY_FLOAT32, PLY_FLOAT64,
PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT,
PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE,
PLY_LIST /* has to be the last in enum */
} e_ply_type; /* order matches ply_type_list */
/* ----------------------------------------------------------------------
* Property reading callback prototype
*
* message: error message
* ---------------------------------------------------------------------- */
typedef void (*p_ply_error_cb)(const char *message);
/* ----------------------------------------------------------------------
* Opens a ply file for reading (fails if file is not a ply file)
*
* error_cb: error callback function
* name: file name
*
* Returns 1 if successful, 0 otherwise
* ---------------------------------------------------------------------- */
p_ply ply_open(const char *name, p_ply_error_cb error_cb);
/* ----------------------------------------------------------------------
* Reads and parses the header of a ply file returned by ply_open
*
* ply: handle returned by ply_open
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_read_header(p_ply ply);
/* ----------------------------------------------------------------------
* Property reading callback prototype
*
* argument: parameters for property being processed when callback is called
*
* Returns 1 if should continue processing file, 0 if should abort.
* ---------------------------------------------------------------------- */
typedef int (*p_ply_read_cb)(p_ply_argument argument);
/* ----------------------------------------------------------------------
* Sets up callbacks for property reading after header was parsed
*
* ply: handle returned by ply_open
* element_name: element where property is
* property_name: property to associate element with
* read_cb: function to be called for each property value
* pdata/idata: user data that will be passed to callback
*
* Returns 0 if no element or no property in element, returns the
* number of element instances otherwise.
* ---------------------------------------------------------------------- */
int32 ply_set_read_cb(p_ply ply, const char *element_name,
const char *property_name, p_ply_read_cb read_cb,
void *pdata, int32 idata);
/* ----------------------------------------------------------------------
* Returns information about the element originating a callback
*
* argument: handle to argument
* element: receives a the element handle (if non-null)
* instance_index: receives the index of the current element instance
* (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_element(p_ply_argument argument,
p_ply_element *element, int32 *instance_index);
/* ----------------------------------------------------------------------
* Returns information about the property originating a callback
*
* argument: handle to argument
* property: receives the property handle (if non-null)
* length: receives the number of values in this property (if non-null)
* value_index: receives the index of current property value (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_property(p_ply_argument argument,
p_ply_property *property, int32 *length, int32 *value_index);
/* ----------------------------------------------------------------------
* Returns user data associated with callback
*
* pdata: receives a copy of user custom data pointer (if non-null)
* idata: receives a copy of user custom data integer (if non-null)
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
int32 *idata);
/* ----------------------------------------------------------------------
* Returns the value associated with a callback
*
* argument: handle to argument
*
* Returns the current data item
* ---------------------------------------------------------------------- */
double ply_get_argument_value(p_ply_argument argument);
/* ----------------------------------------------------------------------
* Reads all elements and properties calling the callbacks defined with
* calls to ply_set_read_cb
*
* ply: handle returned by ply_open
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_read(p_ply ply);
/* ----------------------------------------------------------------------
* Iterates over all elements by returning the next element.
* Call with NULL to return handle to first element.
*
* ply: handle returned by ply_open
* last: handle of last element returned (NULL for first element)
*
* Returns element if successfull or NULL if no more elements
* ---------------------------------------------------------------------- */
p_ply_element ply_get_next_element(p_ply ply, p_ply_element last);
/* ----------------------------------------------------------------------
* Iterates over all comments by returning the next comment.
* Call with NULL to return pointer to first comment.
*
* ply: handle returned by ply_open
* last: pointer to last comment returned (NULL for first comment)
*
* Returns comment if successfull or NULL if no more comments
* ---------------------------------------------------------------------- */
const char *ply_get_next_comment(p_ply ply, const char *last);
/* ----------------------------------------------------------------------
* Iterates over all obj_infos by returning the next obj_info.
* Call with NULL to return pointer to first obj_info.
*
* ply: handle returned by ply_open
* last: pointer to last obj_info returned (NULL for first obj_info)
*
* Returns obj_info if successfull or NULL if no more obj_infos
* ---------------------------------------------------------------------- */
const char *ply_get_next_obj_info(p_ply ply, const char *last);
/* ----------------------------------------------------------------------
* Returns information about an element
*
* element: element of interest
* name: receives a pointer to internal copy of element name (if non-null)
* ninstances: receives the number of instances of this element (if non-null)
*
* Returns 1 if successfull or 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_element_info(p_ply_element element, const char** name,
int32 *ninstances);
/* ----------------------------------------------------------------------
* Iterates over all properties by returning the next property.
* Call with NULL to return handle to first property.
*
* element: handle of element with the properties of interest
* last: handle of last property returned (NULL for first property)
*
* Returns element if successfull or NULL if no more properties
* ---------------------------------------------------------------------- */
p_ply_property ply_get_next_property(p_ply_element element,
p_ply_property last);
/* ----------------------------------------------------------------------
* Returns information about a property
*
* property: handle to property of interest
* name: receives a pointer to internal copy of property name (if non-null)
* type: receives the property type (if non-null)
* length_type: for list properties, receives the scalar type of
* the length field (if non-null)
* value_type: for list properties, receives the scalar type of the value
* fields (if non-null)
*
* Returns 1 if successfull or 0 otherwise
* ---------------------------------------------------------------------- */
int ply_get_property_info(p_ply_property property, const char** name,
e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type);
/* ----------------------------------------------------------------------
* Creates new ply file
*
* name: file name
* storage_mode: file format mode
*
* Returns handle to ply file if successfull, NULL otherwise
* ---------------------------------------------------------------------- */
p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
p_ply_error_cb error_cb);
/* ----------------------------------------------------------------------
* Adds a new element to the ply file created by ply_create
*
* ply: handle returned by ply_create
* name: name of new element
* ninstances: number of element of this time in file
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_element(p_ply ply, const char *name, int32 ninstances);
/* ----------------------------------------------------------------------
* Adds a new property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* type: property type
* length_type: scalar type of length field of a list property
* value_type: scalar type of value fields of a list property
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_property(p_ply ply, const char *name, e_ply_type type,
e_ply_type length_type, e_ply_type value_type);
/* ----------------------------------------------------------------------
* Adds a new list property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* length_type: scalar type of length field of a list property
* value_type: scalar type of value fields of a list property
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_list_property(p_ply ply, const char *name,
e_ply_type length_type, e_ply_type value_type);
/* ----------------------------------------------------------------------
* Adds a new property to the last element added by ply_add_element
*
* ply: handle returned by ply_create
* name: name of new property
* type: property type
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type);
/* ----------------------------------------------------------------------
* Adds a new comment item
*
* ply: handle returned by ply_create
* comment: pointer to string with comment text
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_comment(p_ply ply, const char *comment);
/* ----------------------------------------------------------------------
* Adds a new obj_info item
*
* ply: handle returned by ply_create
* comment: pointer to string with obj_info data
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_add_obj_info(p_ply ply, const char *obj_info);
/* ----------------------------------------------------------------------
* Writes the ply file header after all element and properties have been
* defined by calls to ply_add_element and ply_add_property
*
* ply: handle returned by ply_create
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_write_header(p_ply ply);
/* ----------------------------------------------------------------------
* Writes one property value, in the order they should be written to the
* file. For each element type, write all elements of that type in order.
* For each element, write all its properties in order. For scalar
* properties, just write the value. For list properties, write the length
* and then each of the values.
*
* ply: handle returned by ply_create
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_write(p_ply ply, double value);
/* ----------------------------------------------------------------------
* Closes a ply file handle. Releases all memory used by handle
*
* ply: handle to be closed.
*
* Returns 1 if successfull, 0 otherwise
* ---------------------------------------------------------------------- */
int ply_close(p_ply ply);
#ifdef __cplusplus
}
#endif
#endif /* RPLY_H */
/* ----------------------------------------------------------------------
* Copyright (C) 2003-2005 Diego Nehab. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ---------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "scene.h"
#include "plymodel.h"
#include "bvh.h"
int scene_num_triangles, scene_num_vertices;
triangle *scene_triangles=NULL;
vec3 *scene_vertices=NULL;
int scene_num_spheres;
sphere *scene_spheres=NULL;
int scene_num_lights;
light *scene_lights=NULL;
float scene_ambient_light = 0.05;
vec3 scene_background_color = { 1.0, 1.0, 1.0 };
vec3 scene_camera_position, scene_camera_lookat;
void
read_scene(const char *fname)
{
FILE *f;
char line[256];
int i;
float x, y, z, r;
char s[256];
int material;
f = fopen(fname, "rt");
if (!f)
{
fprintf(stderr, "\nFATAL: Could not open scene file %s!\n", fname);
exit(-1);
}
printf("Reading scene %s\n", fname);
scene_num_triangles = scene_num_vertices = 0;
scene_num_spheres = 0;
material = 0;
// Bare bones, but usable, parsing
fgets(line, 255, f);
while (!feof(f))
{
if (line[0] == '/' && line[1] == '/')
{
// Comment, skip
}
else if (sscanf(line, "material %d\n", &material) == 1)
{
// Nothing to do, value already stored...
}
else if (sscanf(line, "light %f %f %f %f\n", &x, &y, &z, &r) == 4)
{
scene_lights = realloc(scene_lights, (scene_num_lights+1)*sizeof(light));
scene_lights[scene_num_lights].position.x = x;
scene_lights[scene_num_lights].position.y = y;
scene_lights[scene_num_lights].position.z = z;
scene_lights[scene_num_lights].intensity = r;
scene_num_lights++;
}
else if (sscanf(line, "sphere %f %f %f %f\n", &x, &y, &z, &r) == 4)
{
scene_spheres = realloc(scene_spheres, (scene_num_spheres+1)*sizeof(sphere));
scene_spheres[scene_num_spheres].center.x = x;
scene_spheres[scene_num_spheres].center.y = y;
scene_spheres[scene_num_spheres].center.z = z;
scene_spheres[scene_num_spheres].radius = r;
scene_spheres[scene_num_spheres].material = material;
scene_num_spheres++;
}
else if (sscanf(line, "ply_file %s\n", s) == 1)
{
// Read triangle model from a .ply file, add the triangles
// in the file to the global list of triangles in the scene
printf("[%s]\n", s);
read_ply_model(s);
// Allocate memory to hold the new vertices and triangles
scene_vertices = realloc(scene_vertices,
(scene_num_vertices + ply_num_vertices)*sizeof(vec3));
scene_triangles = realloc(scene_triangles,
(scene_num_triangles + ply_num_triangles)*sizeof(triangle));
// Copy the vertices
memcpy(scene_vertices + scene_num_vertices,
ply_vertices, ply_num_vertices*sizeof(vec3));
// Copy the triangles, AND RENUMBER THEIR VERTEX INDICES
// (as we're going to add the model triangles and vertices
// to a scene-wide list of triangles and vertices)
for (i = 0; i < ply_num_triangles; i++)
{
scene_triangles[scene_num_triangles+i] = ply_triangles[i];
// Renumber the vertex indices
scene_triangles[scene_num_triangles+i].v[0] += scene_num_vertices;
scene_triangles[scene_num_triangles+i].v[1] += scene_num_vertices;
scene_triangles[scene_num_triangles+i].v[2] += scene_num_vertices;
// Assign the material
scene_triangles[scene_num_triangles+i].material = material;
}
// Update the total numer of vertices in the scene
scene_num_vertices += ply_num_vertices;
scene_num_triangles += ply_num_triangles;
}
else if (line[0] != '\n')
{
fprintf(stderr, "Ignoring unknown line '%s'\n", line);
}
fgets(line, 255, f);
}
fclose(f);
// Build the BVH for the triangles read
bvh_build();
}
#ifndef SCENE_H
#define SCENE_H
#include "types.h"
extern int scene_num_triangles, scene_num_vertices;
extern triangle *scene_triangles;
extern vec3 *scene_vertices;
extern int scene_num_spheres;
extern sphere *scene_spheres;
extern int scene_num_lights;
extern light *scene_lights;
extern float scene_ambient_light;
extern vec3 scene_background_color;
extern vec3 scene_camera_position;
extern vec3 scene_camera_lookat;
void read_scene(const char *fname);
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "shaders.h"
#include "perlin.h"
#include "v3math.h"
#include "intersection.h"
#include "scene.h"
#include "quat.h"
#include "constants.h"
// Always return the same color. This shader does no real computations
// based on normal, light position, etc. As such, it merely creates
// a "silhouette" of an object.
vec3
shade_constant(intersection_point ip)
{
return v3_create(1, 0, 0);
}
vec3
shade_matte(intersection_point ip)
{
vec3 l_dir;
float f;
float res;
res = scene_ambient_light;
for (int l = 0; l < scene_num_lights; l++)
{
l_dir = v3_subtract(scene_lights[l].position, ip.p);
// Shoot a shadow ray. If the point to shade is in shadow w.r.t.
// this light then there is no contribution from the light
if (shadow_check(v3_add(ip.p, v3_multiply(l_dir, 0.0001)), l_dir))
continue;
// Compute and add the contribution from this light
l_dir = v3_normalize(l_dir);
f = v3_dotprod(ip.n, l_dir);
if (f > 0.0)
res += f * scene_lights[l].intensity;
}
if (res > 1.0)
res = 1.0;
return v3_create(res, res, res);
}
// Returns the shaded color for the given point to shade.
// Calls the relevant shading function based on the material index.
vec3
shade(intersection_point ip)
{
switch (ip.material)
{
case 0:
return shade_constant(ip);
case 1:
return shade_matte(ip);
default:
return shade_constant(ip);
}
}
// Determine the surface color for the first object intersected by
// the given ray, or return the scene background color when no
// intersection is found
vec3
ray_color(int level, vec3 ray_origin, vec3 ray_direction)
{
intersection_point ip;
// If this ray has been reflected too many times, simply
// return the background color.
if (level >= 3)
return scene_background_color;
// Check if the ray intersects anything in the scene
if (find_first_intersection(&ip, ray_origin, ray_direction))
{
// Shade the found intersection point
ip.ray_level = level;
return shade(ip);
}
// Nothing was hit, return background color
return scene_background_color;
}
#ifndef SHADERS_H
#define SHADERS_H
#include "types.h"
// Determine the surface color for the first object intersected by
// the given ray, or return the scene background color when no
// intersection is found
vec3 ray_color(int level, vec3 ray_origin, vec3 ray_direction);
#endif
light 9 -4.0 10.0 0.7
light -10 2.0 15.0 0.3
material 0
sphere 0.65 -0.6 0.4 0.2
sphere -0.6 0.75 0.4 0.4
sphere -0.5 0.15 0.2 0.2
ply_file ../scenes/plateau.ply
ply_file ../scenes/kubus.ply
ply_file ../scenes/tetra.ply
light 9 -4.0 10.0 0.7
light -10 2.0 15.0 0.3
material 1
ply_file ../scenes/plateau.ply
ply_file ../scenes/kubus.ply
ply_file ../scenes/tetra.ply
ply_file ../scenes/cone.ply
#ifndef TYPES_H
#define TYPES_H
#include "v3math.h"
typedef unsigned char byte;
typedef struct
{
// indices of vertices
int v[3];
// the triangle's normal
vec3 n;
// the triangle's vertex normals
vec3 vn[3];
// the material index for this triangle
int material;
}
triangle;
typedef struct
{
vec3 center;
float radius;
int material;
}
sphere;
typedef struct
{
// The ray t-value at which intersection occurred, used to find
// the closest hit
float t;
// intersection point
vec3 p;
// surface normal at p
vec3 n;
// direction from which the ray responsible for this intersection
// came. points away from the surface!
vec3 i;
// the intersected surface's material index
int material;
// level of the ray that caused this intersection;
// i.e. level 0 is a camera ray, level 1 is is a ray that has
// been reflected once, etc
int ray_level;
}
intersection_point;
typedef struct
{
vec3 position;
float intensity; // 0 - 1
}
light;
typedef struct
{
float azimuth;
float rot_z;
float distance;
}
viewpoint;
#endif
#ifndef V3MATH_H
#define V3MATH_H
#include <math.h>
typedef struct
{
float x, y, z;
}
vec3;
// Create a new 3-vector of floats
static inline vec3
v3_create(float x, float y, float z)
{
vec3 res;
res.x = x;
res.y = y;
res.z = z;
return res;
}
// Return -a
static inline vec3
v3_negate(vec3 a)
{
vec3 res;
res.x = - a.x;
res.y = - a.y;
res.z = - a.z;
return res;
}
// Return a + b
static inline vec3
v3_add(vec3 a,vec3 b)
{
vec3 res;
res.x = a.x+b.x;
res.y = a.y+b.y;
res.z = a.z+b.z;
return res;
}
// Return a - b
static inline vec3
v3_subtract(vec3 a, vec3 b)
{
vec3 res;
res.x = a.x-b.x;
res.y = a.y-b.y;
res.z = a.z-b.z;
return res;
}
// Return a / |a|
static inline vec3
v3_normalize(vec3 a)
{
vec3 res;
double l = sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
res = a;
res.x /= l;
res.y /= l;
res.z /= l;
return res;
}
// Return a ^ b
static inline vec3
v3_crossprod(vec3 a, vec3 b)
{
vec3 res;
res.x = a.y*b.z - a.z*b.y;
res.y = a.z*b.x - a.x*b.z;
res.z = a.x*b.y - a.y*b.x;
return res;
}
// Return a * b
static inline float
v3_dotprod(vec3 a, vec3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
// Return a*s
static inline vec3
v3_multiply(vec3 a, float s)
{
vec3 res;
res.x = a.x*s;
res.y = a.y*s;
res.z = a.z*s;
return res;
}
// Return |a|
static inline float
v3_length(vec3 a)
{
return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
}
// Return the i-th component of a, i.e. for i=0
// this returns a.x
static inline float
v3_component(vec3 a, int i)
{
if (i == 0)
return a.x;
else if (i == 1)
return a.y;
else
return a.z;
}
// Set the i-th component of a to v
static inline vec3
v3_set_component(vec3 a, int i, float v)
{
vec3 res = a;
if (i == 0)
res.x = v;
else if (i == 1)
res.y = v;
else
res.z = v;
return res;
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment