Skip to content

Instantly share code, notes, and snippets.

@harieamjari
Created March 31, 2023 20:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save harieamjari/6dfda39ca22852363aae51d5b773e3fd to your computer and use it in GitHub Desktop.
Save harieamjari/6dfda39ca22852363aae51d5b773e3fd to your computer and use it in GitHub Desktop.
render obj file
import png
from ctypes import *
"""
struct vec3D {
double x, y, z;
};
/* A triangle is composed of 3D vertices */
struct triangle3D {
struct vec3D *vertex[3];
};
/* An object is composed of triangles */
struct object3D {
int vertices;
struct vec3D **vertex;
int faces;
struct triangle3D *face;
};
"""
class vec3D(Structure):
_fields_ = [("x", c_double), ("y", c_double), ("z", c_double)]
class triangle3D(Structure):
_fields_ = [("nda", c_int),("ndb", c_int),("ndz", c_int), ("vertex", POINTER(vec3D)*3)]
class object3D(Structure):
_fields_ = [("vertices", c_int),
("vertex", POINTER(POINTER(vec3D))),
("faces", c_int),
("face", POINTER(triangle3D))]
"""
As a C programmer, I feel like I would have saved time writing a whole parser
for the obj format in C than trying to demystify Python's syntax.
"""
def load_obj(obj_name):
with open(obj_name, 'r') as file:
countv = 0;
countf = 0;
for line in file:
words = [word for word in line.split()]
if not words:
continue
if words[0] == "v":
countv+=1
continue
if words[0] == "f":
countf+=1
continue
file.seek(0)
#an array of pointer vertex
vertex = (POINTER(vec3D)*countv)()
#an array of face
face = (triangle3D*countf)()
vertices = countv; faces = countf;
countv = 0; countf = 0;
for line in file:
words = [word for word in line.split()]
if not words:
continue
if words[0] == "v":
x = words[1]; y = words[2]; z = words[3];
vertex[countv] = pointer(vec3D(float(x),float(y),float(z)))
#print("x y z ",float(x),float(y),float(z))
countv+=1
continue
if words[0] == "f":
v1 = words[1].split('/'); v1 = int(v1[0])
v2 = words[2].split('/'); v2 = int(v2[0])
v3 = words[3].split('/'); v3 = int(v3[0])
vert3 = (POINTER(vec3D)*3)()
vert3[0].contents = vertex[v1-1].contents
vert3[1].contents = vertex[v2-1].contents
vert3[2].contents = vertex[v3-1].contents
#print("vertices: ", vertex[v1-1].contents, vertex[v2-1].contents, vertex[v3-1].x)
face[countf] = triangle3D(v1-1,v2-1,v3-1,vert3);
#print(type(vert3), vert3)
#face[countf] = triangle3D(pointer(pointer(vertex[v1-1])))
countf+=1
continue
print("vertex ", countv)
print("faces ", countf)
obj_loaded = object3D(vertices, vertex, faces, face)
return obj_loaded
dll = CDLL("./render.so")
print(type(dll))
img_width = 256; img_height = 256; img_channels = 4; #rgba
img_buf = create_string_buffer(img_height*img_width * img_channels);
dll.render.argtypes = [c_void_p, c_int, c_int, c_int, POINTER(object3D)];
obj_loaded = load_obj("prat.obj")
for i in range(256):
dll.render(byref(img_buf), img_width, img_height, i, byref(obj_loaded));
f = open("box-"+str(i)+".png", 'wb');
w = png.Writer(width=img_width, height=img_height,greyscale=False, alpha=True, bitdepth=8);
w.write_array(f, img_buf);
f.close();
print("box-"+str(i)+".png done");
#include <assert.h> // so I wont have to do error handling
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
//#define DEBUG
/* this may be a vertex or a vector */
struct vec3D {
double x, y, z;
};
/* A triangle is composed of 3D vertices */
struct triangle3D {
// struct vec3D vertex[3];
int nda, ndb, ndc;
struct vec3D *vertex[3];
};
/* An object is composed of triangles */
struct object3D {
int vertices;
struct vec3D **vertex;
int faces;
struct triangle3D *face;
};
/* A scene is composed of objects */
struct scene3D {
int objects;
struct object3D *object;
};
static double dot_product(const struct vec3D a, const struct vec3D b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
static double magnitude(const struct vec3D a) {
return sqrt(dot_product(a, a));
}
static struct triangle3D *allocate_triangle(size_t n) {
struct triangle3D *ret = malloc(sizeof(struct triangle3D) * n);
for (size_t i = 0; i < n; i++) {
ret[i].vertex[0] = malloc(sizeof(struct vec3D));
ret[i].vertex[1] = malloc(sizeof(struct vec3D));
ret[i].vertex[2] = malloc(sizeof(struct vec3D));
}
return ret;
};
static void free_object(struct object3D object) {
for (int i = 0; i < object.vertices; i++) {
free(object.vertex[i]);
}
free(object.vertex);
free(object.face);
}
static struct object3D copy_object(struct object3D ctx) {
int nb_vertex = ctx.vertices;
struct vec3D **vertex = malloc(sizeof(struct vec3D *) * nb_vertex);
for (int i = 0; i < nb_vertex; i++) {
vertex[i] = malloc(sizeof(struct vec3D));
vertex[i]->x = ctx.vertex[i]->x;
vertex[i]->y = ctx.vertex[i]->y;
vertex[i]->z = ctx.vertex[i]->z;
}
int nb_faces = ctx.faces;
struct triangle3D *face = malloc(sizeof(struct triangle3D) * nb_faces);
for (int i = 0; i < nb_faces; i++) {
int nda = ctx.face[i].nda, ndb = ctx.face[i].ndb, ndc = ctx.face[i].ndc;
face[i].nda = nda;
face[i].ndb = ndb;
face[i].ndc = ndc;
face[i].vertex[0] = vertex[nda];
face[i].vertex[1] = vertex[ndb];
face[i].vertex[2] = vertex[ndc];
}
return (struct object3D){.vertices = ctx.vertices,
.vertex = vertex,
.faces = ctx.faces,
.face = face};
}
static struct object3D translate(const struct object3D ctx, const double dx,
const double dy, const double dz) {
struct vec3D **vertex = ctx.vertex;
for (int i = 0; i < ctx.vertices; i++) {
vertex[i]->x += dx;
vertex[i]->y += dy;
vertex[i]->z += dz;
}
return (struct object3D){.vertices = ctx.vertices,
.vertex = ctx.vertex,
.faces = ctx.faces,
.face = ctx.face};
}
static struct object3D dilate(const struct object3D ctx, const double dx,
const double dy, const double dz) {
for (int i = 0; i < ctx.vertices; i++) {
ctx.vertex[i]->x *= dx;
ctx.vertex[i]->y *= dy;
ctx.vertex[i]->z *= dz;
}
return (struct object3D){.vertices = ctx.vertices,
.vertex = ctx.vertex,
.faces = ctx.faces,
.face = ctx.face};
}
static struct object3D rotate(const struct object3D ctx, const double dx,
const double dy, const double dz) {
struct vec3D **vertex = ctx.vertex;
for (int i = 0; i < ctx.vertices; i++) {
double vix, viy, viz;
vix = cos(dz) * cos(dy) * vertex[i]->x +
(cos(dz) * sin(dy) * sin(dx) - sin(dz) * cos(dx)) * vertex[i]->y +
(cos(dz) * sin(dy) * cos(dx) + sin(dz) * sin(dx)) * vertex[i]->z;
viy = sin(dz) * cos(dy) * vertex[i]->x +
(sin(dz) * sin(dy) * sin(dx) + cos(dz) * cos(dx)) * vertex[i]->y +
(sin(dz) * sin(dy) * cos(dx) - cos(dz) * sin(dx)) * vertex[i]->z;
viz = -sin(dy) * vertex[i]->x + cos(dy) * sin(dx) * vertex[i]->y +
cos(dy) * cos(dx) * vertex[i]->z;
vertex[i]->x = vix;
vertex[i]->y = viy;
vertex[i]->z = viz;
}
return (struct object3D){.vertices = ctx.vertices,
.vertex = ctx.vertex,
.faces = ctx.faces,
.face = ctx.face};
}
static struct vec3D cross_product(const struct vec3D a, const struct vec3D b) {
/* designated initializers */
return (struct vec3D){.x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x};
}
static struct vec3D normalize(const struct vec3D ctx) {
double mg = magnitude(ctx);
return (struct vec3D){.x = ctx.x / mg, .y = ctx.y / mg, .z = ctx.z / mg};
}
void render(void *img_buf, int width, int height, int ndx,
struct object3D *obj) {
double Pz = 150.0;
// struct vec3D v1 = {20.0, 50.0, -20.0};
// struct vec3D v2 = {-20.0, 50.0, -20.0};
// struct vec3D v3 = {-20.0, -50.0, -20.0};
// struct vec3D v4 = {20.0, -50.0, -20.0};
//
// struct vec3D v5 = {20.0, 50.0, 20.0};
// struct vec3D v6 = {-20.0, 50.0, 20.0};
// struct vec3D v7 = {-20.0, -50.0, 20.0};
// struct vec3D v8 = {20.0, -50.0, 20.0};
// struct vec3D **vertex = malloc(8*sizeof(struct vec3D *));
// vertex[0] = &v1;
// vertex[1] = &v2;
// vertex[2] = &v3;
// vertex[3] = &v4;
// vertex[4] = &v5;
// vertex[5] = &v6;
// vertex[6] = &v7;
// vertex[7] = &v8;
//
//
// struct triangle3D face[8] = {
// [0].vertex = {[0] = &v3, [1] = &v2, [2] = &v1},
// [1].vertex = {[0] = &v1, [1] = &v4, [2] = &v3},
// [2].vertex = {[0] = &v5, [1] = &v6, [2] = &v7},
// [3].vertex = {[0] = &v7, [1] = &v8, [2] = &v5},
// [4].vertex = {[0] = &v4, [1] = &v1, [2] = &v5},
// [5].vertex = {[0] = &v5, [1] = &v8, [2] = &v4},
// [6].vertex = {[0] = &v7, [1] = &v6, [2] = &v2},
// [7].vertex = {[0] = &v2, [1] = &v3, [2] = &v7},
// };
struct object3D box = copy_object(
*obj); // {.vertices=8, .vertex=vertex, .faces = 8, .face = face};
#ifdef DEBUG
printf("vertices %d faces %d\n", box.vertices, box.faces);
for (int i = 0; i < box.vertices; i++)
printf("v%d %f %f %f\n", i, box.vertex[i]->x, box.vertex[i]->y,
box.vertex[i]->z);
for (int i = 0; i < box.faces; i++) {
printf("f%d %f %f %f", i, box.face[i].vertex[0]->x,
box.face[i].vertex[0]->y, box.face[i].vertex[0]->z);
printf(" %f %f %f", box.face[i].vertex[1]->x, box.face[i].vertex[1]->y,
box.face[i].vertex[1]->z);
printf(" %f %f %f\n", box.face[i].vertex[2]->x, box.face[i].vertex[2]->y,
box.face[i].vertex[2]->z);
}
printf("b %f %f %f\n", box.vertex[1]->x, box.vertex[1]->y, box.vertex[1]->z);
printf("b %f %f %f\n", box.vertex[2]->x, box.vertex[2]->y, box.vertex[2]->z);
#endif
/* RANDOM SPEED */
// dilate(box, 20, 50, 20);
struct object3D rotbox =
rotate(box, (double)ndx * 3.0 / 180.0, M_PI * (double)ndx * 4.0 / 180.0,
(double)ndx * 7.0 / 180.0);
struct object3D trbox = translate(box, 0.0, 0.0, 40.0);
// free_object(rotbox);
#ifdef DEBUG
printf("%f %f %f\n", box.vertex[0]->x, box.vertex[0]->y, box.vertex[0]->z);
printf("%f %f %f\n", box.vertex[1]->x, box.vertex[1]->y, box.vertex[1]->z);
printf("%f %f %f\n", box.vertex[2]->x, box.vertex[2]->y, box.vertex[2]->z);
#endif
struct vec3D light_source = {
.x = 0.0, .y = 0.0, .z = -1.0}; /* magnitude = 1 */
for (int y = 0; y < height; y++) {
uint8_t *line = img_buf + (4 * (width * y));
for (int x = 0; x < width; x++) {
double Px = x - (width / 2), Py = (height / 2) - y;
struct vec3D ray = {Px, Py, Pz};
double _magnitude = magnitude(ray);
struct vec3D normalized_ray = {ray.x / _magnitude, ray.y / _magnitude,
ray.z / _magnitude};
struct vec3D BmA;
struct vec3D CmA;
struct vec3D pvec;
double illumination, um, vm;
double det, t = INFINITY; /* portable? */
int intersect = 0;
/* check if all triangle intersects */
for (int triangle = 0; triangle < trbox.faces; triangle++) {
BmA = (struct vec3D){.x = trbox.face[triangle].vertex[1]->x -
trbox.face[triangle].vertex[0]->x,
.y = trbox.face[triangle].vertex[1]->y -
trbox.face[triangle].vertex[0]->y,
.z = trbox.face[triangle].vertex[1]->z -
trbox.face[triangle].vertex[0]->z};
CmA = (struct vec3D){.x = trbox.face[triangle].vertex[2]->x -
trbox.face[triangle].vertex[0]->x,
.y = trbox.face[triangle].vertex[2]->y -
trbox.face[triangle].vertex[0]->y,
.z = trbox.face[triangle].vertex[2]->z -
trbox.face[triangle].vertex[0]->z};
pvec = (struct vec3D)cross_product(ray, CmA);
double illum =
dot_product(normalize(cross_product(BmA, CmA)), light_source);
det = dot_product(BmA, pvec);
double u =
dot_product(
pvec, (struct vec3D){.x = -trbox.face[triangle].vertex[0]->x,
.y = -trbox.face[triangle].vertex[0]->y,
.z = -trbox.face[triangle].vertex[0]->z}) /
det;
/* return background color */
if (u < 0.0 || u > 1.0) {
line[(x * 4) + 0] = 0xA0;
line[(x * 4) + 1] = 0xA0;
line[(x * 4) + 2] = 0xA0;
line[(x * 4) + 3] = 0xFF;
continue;
}
double v =
dot_product(
cross_product(
(struct vec3D){.x = -trbox.face[triangle].vertex[0]->x,
.y = -trbox.face[triangle].vertex[0]->y,
.z = -trbox.face[triangle].vertex[0]->z},
BmA),
ray) /
det;
if (v < 0.0 || u + v > 1.0) {
line[(x * 4) + 0] = 0xA0;
line[(x * 4) + 1] = 0xA0;
line[(x * 4) + 2] = 0xA0;
line[(x * 4) + 3] = 0xFF;
continue;
}
double temp =
dot_product(CmA, cross_product(
(struct vec3D){
.x = -trbox.face[triangle].vertex[0]->x,
.y = -trbox.face[triangle].vertex[0]->y,
.z = -trbox.face[triangle].vertex[0]->z},
BmA)) /
det; /*
double mm = magnitude((struct vec3D){normalized_ray.x*temp,
normalized_ray.y*temp, normalized_ray.z*temp});*/
if (t > temp) {
illumination = illum;
t = temp;
intersect = 1;
vm = v;
um = u;
}
}
// free_object(trbox);
if (!intersect) {
line[(x * 4) + 0] = 0xA0;
line[(x * 4) + 1] = 0xA0;
line[(x * 4) + 2] = 0xA0;
line[(x * 4) + 3] = 0xFF;
continue;
}
line[(x * 4) + 0] = (unsigned char)(((illumination + 1.0) / 2.0) * 255.0);
line[(x * 4) + 1] = (unsigned char)(((illumination + 1.0) / 2.0) * 255.0);
line[(x * 4) + 2] = (unsigned char)(((illumination + 1.0) / 2.0) * 255.0);
// line[(x * 4) + 0] = (unsigned char)(255.0*powf(um*((illumination
// + 1.0) / 2.0), 0.2)); line[(x * 4) + 1] = (unsigned
// char)(255.0*powf(vm*((illumination + 1.0) / 2.0), 0.2)); line[(x *
// 4) + 2] = (unsigned char)(255.0*powf((1-um-vm) *((illumination
// + 1.0) / 2.0), 0.2)); printf("u %f v %f\n", um, vm);
line[(x * 4) + 3] = 0xFF;
}
}
free_object(box);
// return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment