dosobj = src/dos/djdpmi.obj src/dos/gfx.obj src/dos/keyb.obj src/dos/main.obj &
src/dos/mouse.obj src/dos/vbe.obj src/dos/vga.obj src/dos/watdpmi.obj
appobj = src/app.obj src/cmesh.obj src/darray.obj src/font.obj src/logger.obj &
- src/meshgen.obj src/meshload.obj src/options.obj src/rbtree.obj src/rt.obj &
+ src/meshgen.obj src/meshload.obj src/options.obj src/rbtree.obj src/geom.obj &
src/rend.obj src/rtk.obj src/scene.obj src/scr_mod.obj src/scr_rend.obj &
+ src/texture.obj src/material.obj &
src/util.obj src/util_s.obj src/cpuid.obj src/cpuid_s.obj
gawobj = src/gaw/gaw_sw.obj src/gaw/gawswtnl.obj src/gaw/polyclip.obj src/gaw/polyfill.obj
dosobj = src\dos\djdpmi.obj src\dos\gfx.obj src\dos\keyb.obj src\dos\main.obj &
src\dos\mouse.obj src\dos\vbe.obj src\dos\vga.obj src\dos\watdpmi.obj
appobj = src\app.obj src\cmesh.obj src\darray.obj src\font.obj src\logger.obj &
- src\meshgen.obj src\meshload.obj src\options.obj src\rbtree.obj src\rt.obj &
+ src\meshgen.obj src\meshload.obj src\options.obj src\rbtree.obj src\geom.obj &
src\rend.obj src\rtk.obj src\scene.obj src\scr_mod.obj src\scr_rend.obj &
+ src\texture.obj src\material.obj &
src\util.obj src\util_s.obj src\cpuid.obj src\cpuid_s.obj
gawobj = src\gaw\gaw_sw.obj src\gaw\gawswtnl.obj src\gaw\polyclip.obj src\gaw\polyfill.obj
static CGM_INLINE void cgm_vcons(cgm_vec3 *v, float x, float y, float z);
static CGM_INLINE cgm_vec3 cgm_vvec(float x, float y, float z);
+static CGM_INLINE void cgm_vneg(cgm_vec3 *v);
static CGM_INLINE void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b);
static CGM_INLINE void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a+b*s */
static CGM_INLINE void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b);
return v;
}
+static CGM_INLINE void cgm_vneg(cgm_vec3 *v)
+{
+ v->x = -v->x;
+ v->y = -v->y;
+ v->z = -v->z;
+}
+
static CGM_INLINE void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b)
{
a->x += b->x;
switch(obj->type) {
case OBJ_SPHERE:
- res = ray_sphere(ray, (const struct sphere*)obj, hit);
+ res = ray_sphere(&localray, (const struct sphere*)obj, hit);
break;
default:
return res;
}
+#define EPSILON 1e-5
+
int ray_sphere(const cgm_ray *ray, const struct sphere *sph, struct csghit *hit)
{
int i;
struct rayhit *rhptr;
a = cgm_vdot(&ray->dir, &ray->dir);
- b = 2.0f * ray->dir.x * (ray->origin.x - sph->pos.x) +
- 2.0f * ray->dir.y * (ray->origin.y - sph->pos.y) +
- 2.0f * ray->dir.z * (ray->origin.z - sph->pos.z);
- c = cgm_vdot(&sph->pos, &sph->pos) + cgm_vdot(&ray->origin, &ray->origin) +
- cgm_vdot(&sph->pos, &ray->origin) * -2.0f - sph->rad * sph->rad;
+ b = 2.0f * ray->dir.x * ray->origin.x +
+ 2.0f * ray->dir.y * ray->origin.y +
+ 2.0f * ray->dir.z * ray->origin.z;
+ c = cgm_vdot(&ray->origin, &ray->origin) - sph->rad * sph->rad;
if((d = b * b - 4.0 * a * c) < 0.0) return 0;
t1 = (-b + sqrt_d) / (2.0 * a);
t2 = (-b - sqrt_d) / (2.0 * a);
- if((t1 < 1e-6f && t2 < 1e-6f) || (t1 > 1.0f && t2 > 1.0f)) {
+ if((t1 < EPSILON && t2 < EPSILON) || (t1 > 1.0f && t2 > 1.0f)) {
return 0;
}
if(hit) {
- if(t1 < 1e-6f || t1 > 1.0f) {
+ if(t1 < EPSILON || t1 > 1.0f) {
t1 = t2;
- } else if(t2 < 1e-6f || t2 > 1.0f) {
+ } else if(t2 < EPSILON || t2 > 1.0f) {
t2 = t1;
}
if(t2 < t1) {
--- /dev/null
+/*
+RetroRay - integrated standalone vintage modeller/renderer
+Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include "material.h"
+
+void mtl_init(struct material *mtl)
+{
+ static int mtlidx;
+ char namebuf[64];
+
+ cgm_vcons(&mtl->kd, 0.7, 0.7, 0.7);
+ cgm_vcons(&mtl->ks, 0.5, 0.5, 0.5);
+ cgm_vcons(&mtl->ke, 0, 0, 0);
+ mtl->shin = 50.0f;
+ mtl->refl = mtl->trans = 0.0f;
+ mtl->ior = 1.3333333;
+ mtl->texmap = 0;
+
+ sprintf(namebuf, "material%03d", mtlidx++);
+ mtl_set_name(mtl, namebuf);
+}
+
+void mtl_destroy(struct material *mtl)
+{
+ if(!mtl) return;
+ free(mtl->name);
+}
+
+void mtl_set_name(struct material *mtl, const char *name)
+{
+ char *tmp = strdup(name);
+ if(!tmp) return;
+ free(mtl->name);
+ mtl->name = tmp;
+}
--- /dev/null
+/*
+RetroRay - integrated standalone vintage modeller/renderer
+Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef MATERIAL_H_
+#define MATERIAL_H_
+
+#include "cgmath/cgmath.h"
+#include "texture.h"
+
+struct material {
+ char *name;
+ cgm_vec3 kd, ks, ke;
+ float shin;
+ float refl, trans, ior;
+
+ struct texture *texmap;
+};
+
+void mtl_init(struct material *mtl);
+void mtl_destroy(struct material *mtl);
+
+void mtl_set_name(struct material *mtl, const char *name);
+
+#endif /* MATERIAL_H_ */
#include "gfxutil.h"
#include "scene.h"
+static int calc_light(const struct rayhit *hit, const struct light *lt,
+ const cgm_vec3 *vdir, cgm_vec3 *dcol, cgm_vec3 *scol);
+
struct img_pixmap renderbuf;
int max_ray_depth;
+cgm_vec3 ambient;
static int rx, ry, rwidth, rheight;
static int roffs;
static int xstep, ystep;
+static int pan_x, pan_y;
+
+static struct light def_light = {0, {0, 0, 0}, {1, 1, 1}};
+
int rend_init(void)
{
img_init(&renderbuf);
+ cgm_vcons(&ambient, 0.05, 0.05, 0.05);
+
rx = ry = rwidth = rheight = roffs = 0;
+ pan_x = pan_y = 0;
max_ray_depth = 6;
return 0;
}
}
+void rend_pan(int xoffs, int yoffs)
+{
+ pan_x = xoffs;
+ pan_y = yoffs;
+}
+
void rend_begin(int x, int y, int w, int h)
{
int i;
cgm_ray ray;
dest = (uint32_t*)renderbuf.pixels + roffs;
- if(fb) fb += roffs;
+ if(fb) {
+ fb += roffs;
+ }
if(xstep < 1) xstep = 1;
if(ystep < 1) ystep = 1;
+ if(scn_num_lights(scn) == 0) {
+ primray(&ray, renderbuf.width / 2, renderbuf.height / 2);
+ def_light.pos = ray.origin;
+ }
+
for(i=0; i<rheight; i+=ystep) {
h = ystep;
if(i + h > rheight) h = rheight - i;
for(j=0; j<rwidth; j+=xstep) {
- primray(&ray, rx + j, ry + i);
+ primray(&ray, rx + j + pan_x, ry + i + pan_y);
ray_trace(&ray, max_ray_depth, &color);
+ if(color.x > 1.0f) color.x = 1.0f;
+ if(color.y > 1.0f) color.y = 1.0f;
+ if(color.z > 1.0f) color.z = 1.0f;
r = cround64(color.x * 255.0f);
g = cround64(color.y * 255.0f);
b = cround64(color.z * 255.0f);
-
- if(r > 255) r = 255;
- if(g > 255) g = 255;
- if(b > 255) b = 255;
-
pcol = PACK_RGB32(r, g, b);
offs = i * renderbuf.width + j;
cgm_vec3 shade(const cgm_ray *ray, const struct rayhit *hit, int maxiter)
{
- return cgm_vvec(1, 0, 0);
+ int i, num_lights;
+ cgm_vec3 color, dcol, scol, texel, vdir;
+ struct material *mtl;
+ struct light *lt;
+
+ dcol = ambient;
+ cgm_vcons(&scol, 0, 0, 0);
+
+ mtl = hit->obj->mtl;
+
+ vdir = ray->dir;
+ cgm_vneg(&vdir);
+ cgm_vnormalize(&vdir);
+
+ if(!(num_lights = scn_num_lights(scn))) {
+ calc_light(hit, &def_light, &vdir, &dcol, &scol);
+ }
+ for(i=0; i<num_lights; i++) {
+ lt = scn->lights[i];
+ calc_light(hit, lt, &vdir, &dcol, &scol);
+ }
+
+ if(mtl->texmap) {
+ texel = mtl->texmap->lookup(mtl->texmap, hit);
+ cgm_vmul(&dcol, &texel);
+ }
+
+ color = dcol;
+ cgm_vadd(&color, &scol);
+ return color;
+}
+
+static int calc_light(const struct rayhit *hit, const struct light *lt,
+ const cgm_vec3 *vdir, cgm_vec3 *dcol, cgm_vec3 *scol)
+{
+ float ndotl, ndoth, spec;
+ cgm_vec3 ldir, hdir;
+ cgm_ray ray;
+ struct material *mtl = hit->obj->mtl;
+
+ ldir = lt->pos;
+ cgm_vsub(&ldir, &hit->pos);
+
+ ray.origin = hit->pos;
+ ray.dir = ldir;
+
+ if(scn_intersect(scn, &ray, 0)) {
+ return 0; /* in shadow */
+ }
+
+ cgm_vnormalize(&ldir);
+
+ hdir = *vdir;
+ cgm_vadd(&hdir, &ldir);
+ cgm_vnormalize(&hdir);
+
+ ndotl = cgm_vdot(&hit->norm, &ldir);
+ if(ndotl < 0.0f) ndotl = 0.0f;
+ ndoth = cgm_vdot(&hit->norm, &hdir);
+ if(ndoth < 0.0f) ndoth = 0.0f;
+
+ spec = pow(ndoth, mtl->shin);
+
+ dcol->x += mtl->kd.x * ndotl * lt->color.x;
+ dcol->y += mtl->kd.y * ndotl * lt->color.y;
+ dcol->z += mtl->kd.z * ndotl * lt->color.z;
+
+ scol->x += mtl->ks.x * spec * lt->color.x;
+ scol->y += mtl->ks.y * spec * lt->color.y;
+ scol->z += mtl->ks.z * spec * lt->color.z;
+
+ return 1;
}
extern struct img_pixmap renderbuf;
extern int max_ray_depth;
+extern cgm_vec3 ambient;
struct scene;
int rend_init(void);
void rend_size(int xsz, int ysz);
+void rend_pan(int xoffs, int yoffs);
void rend_begin(int x, int y, int w, int h);
int render(uint32_t *fb);
#include "darray.h"
#include "logger.h"
+static struct material *default_material(void);
+
struct scene *create_scene(void)
{
struct scene *scn;
return 0;
}
scn->objects = darr_alloc(0, sizeof *scn->objects);
-
+ scn->lights = darr_alloc(0, sizeof *scn->lights);
+ scn->mtl = darr_alloc(0, sizeof *scn->mtl);
return scn;
}
free_object(scn->objects[i]);
}
darr_free(scn->objects);
+
+ for(i=0; i<darr_size(scn->lights); i++) {
+ free_light(scn->lights[i]);
+ }
+ darr_free(scn->lights);
+
+ for(i=0; i<darr_size(scn->mtl); i++) {
+ mtl_destroy(scn->mtl[i]);
+ free(scn->mtl[i]);
+ }
+ darr_free(scn->mtl);
+
free(scn);
}
return -1;
}
+
+
+int scn_add_material(struct scene *scn, struct material *mtl)
+{
+ darr_push(scn->mtl, &mtl);
+ return 0;
+}
+
+int scn_rm_material(struct scene *scn, struct material *mtl)
+{
+ int idx, num_mtl;
+
+ if((idx = scn_material_index(scn, mtl)) == -1) {
+ return -1;
+ }
+
+ num_mtl = darr_size(scn->mtl);
+
+ if(idx < num_mtl - 1) {
+ scn->mtl[idx] = scn->mtl[num_mtl - 1];
+ }
+ darr_pop(scn->mtl);
+ return 0;
+}
+
+int scn_num_materials(const struct scene *scn)
+{
+ return darr_size(scn->mtl);
+}
+
+int scn_material_index(const struct scene *scn, const struct material *mtl)
+{
+ int i, num_mtl;
+
+ num_mtl = darr_size(scn->mtl);
+ for(i=0; i<num_mtl; i++) {
+ if(scn->mtl[i] == mtl) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+struct material *scn_find_material(const struct scene *scn, const char *mname)
+{
+ int i, num_mtl;
+
+ num_mtl = darr_size(scn->mtl);
+ for(i=0; i<num_mtl; i++) {
+ if(strcmp(scn->mtl[i]->name, mname) == 0) {
+ return scn->mtl[i];
+ }
+ }
+ return 0;
+}
+
+/* manage lights */
+
+int scn_add_light(struct scene *scn, struct light *light)
+{
+ darr_push(scn->lights, &light);
+ return 0;
+}
+
+int scn_rm_light(struct scene *scn, struct light *light)
+{
+ int idx, num_lights;
+
+ if((idx = scn_light_index(scn, light)) == -1) {
+ return -1;
+ }
+
+ num_lights = darr_size(scn->lights);
+
+ if(idx < num_lights - 1) {
+ scn->lights[idx] = scn->lights[num_lights - 1];
+ }
+ darr_pop(scn->lights);
+ return 0;
+}
+
+int scn_num_lights(const struct scene *scn)
+{
+ return darr_size(scn->lights);
+}
+
+int scn_light_index(const struct scene *scn, const struct light *light)
+{
+ int i, num_lights;
+
+ num_lights = darr_size(scn->lights);
+ for(i=0; i<num_lights; i++) {
+ if(scn->lights[i] == light) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+struct light *scn_find_light(const struct scene *scn, const char *mname)
+{
+ int i, num_lights;
+
+ num_lights = darr_size(scn->lights);
+ for(i=0; i<num_lights; i++) {
+ if(strcmp(scn->lights[i]->name, mname) == 0) {
+ return scn->lights[i];
+ }
+ }
+ return 0;
+}
+
+
int scn_intersect(const struct scene *scn, const cgm_ray *ray, struct rayhit *hit)
{
int i, numobj;
return 0;
}
+
+/* --- object functions --- */
+
struct object *create_object(int type)
{
struct object *obj;
cgm_midentity(obj->xform);
cgm_midentity(obj->inv_xform);
obj->xform_valid = 1;
+ obj->mtl = default_material();
set_object_name(obj, buf);
objid++;
obj->xform_valid = 1;
}
+
+/* --- lights --- */
+struct light *create_light(void)
+{
+ struct light *lt;
+ static int ltidx;
+ char buf[64];
+
+ if(!(lt = malloc(sizeof *lt))) {
+ return 0;
+ }
+ cgm_vcons(<->pos, 0, 0, 0);
+
+ set_light_color(lt, 1, 1, 1);
+ set_light_energy(lt, 1);
+
+ sprintf(buf, "light%03d", ltidx++);
+ set_light_name(lt, buf);
+
+ return lt;
+}
+
+void free_light(struct light *lt)
+{
+ if(!lt) return;
+ free(lt->name);
+ free(lt);
+}
+
+int set_light_name(struct light *lt, const char *name)
+{
+ char *tmp = strdup(name);
+ if(!tmp) return -1;
+ free(lt->name);
+ lt->name = tmp;
+ return 0;
+}
+
+void set_light_color(struct light *lt, float r, float g, float b)
+{
+ cgm_vcons(<->orig_color, r, g, b);
+ lt->color = lt->orig_color;
+ cgm_vscale(<->color, lt->energy);
+}
+
+void set_light_energy(struct light *lt, float e)
+{
+ lt->energy = e;
+ lt->color = lt->orig_color;
+ cgm_vscale(<->color, e);
+}
+
+static struct material *default_material(void)
+{
+ static struct material defmtl;
+
+ if(!defmtl.name) {
+ mtl_init(&defmtl);
+ mtl_set_name(&defmtl, "default_mtl");
+ }
+ return &defmtl;
+}
#define SCENE_H_
#include "cgmath/cgmath.h"
+#include "material.h"
enum {
OBJ_NULL,
cgm_vec3 pos, scale, pivot; \
cgm_quat rot; \
float xform[16], inv_xform[16]; \
- int xform_valid
+ int xform_valid; \
+ struct material *mtl
struct object {
OBJ_COMMON_ATTR;
struct object *subobj; /* darr */
};
+struct light {
+ char *name;
+ cgm_vec3 pos;
+ cgm_vec3 color, orig_color;
+ float energy;
+};
+
struct scene {
struct object **objects; /* darr */
+ struct light **lights;
+ struct material **mtl; /* darr */
};
struct rayhit; /* declared in rt.h */
int scn_num_objects(const struct scene *scn);
int scn_object_index(const struct scene *scn, const struct object *obj);
+int scn_add_material(struct scene *scn, struct material *mtl);
+int scn_rm_material(struct scene *scn, struct material *mtl);
+int scn_num_materials(const struct scene *scn);
+int scn_material_index(const struct scene *scn, const struct material *mtl);
+struct material *scn_find_material(const struct scene *scn, const char *mname);
+
+int scn_add_light(struct scene *scn, struct light *mtl);
+int scn_rm_light(struct scene *scn, struct light *mtl);
+int scn_num_lights(const struct scene *scn);
+int scn_light_index(const struct scene *scn, const struct light *mtl);
+struct light *scn_find_light(const struct scene *scn, const char *mname);
+
int scn_intersect(const struct scene *scn, const cgm_ray *ray, struct rayhit *hit);
+/* --- objects --- */
struct object *create_object(int type);
void free_object(struct object *obj);
void calc_object_matrix(struct object *obj);
+/* --- lights --- */
+struct light *create_light(void);
+void free_light(struct light *lt);
+
+int set_light_name(struct light *lt, const char *name);
+void set_light_color(struct light *lt, float r, float g, float b);
+void set_light_energy(struct light *lt, float e);
+
#endif /* SCENE_H_ */
static void mdl_motion(int x, int y);
static void draw_object(struct object *obj);
+static void setup_material(struct material *mtl);
static void draw_grid(void);
static void tbn_callback(rtk_widget *w, void *cls);
static void act_addobj(void);
static void act_rmobj(void);
+static void fix_rect(rtk_rect *rect);
static void draw_rband(void);
static void moveobj(struct object *obj, int px0, int py0, int px1, int py1);
gaw_enable(GAW_CULL_FACE);
gaw_enable(GAW_LIGHTING);
gaw_enable(GAW_LIGHT0);
+
+ rend_pan(0, -TOOLBAR_HEIGHT);
return 0;
}
draw_grid();
- gaw_mtl_diffuse(0.5, 0.5, 0.5, 1);
-
num = scn_num_objects(scn);
for(i=0; i<num; i++) {
+ setup_material(scn->objects[i]->mtl);
+
if(i == selobj) {
gaw_zoffset(1);
gaw_enable(GAW_POLYGON_OFFSET);
gaw_pop_matrix();
}
+static void setup_material(struct material *mtl)
+{
+ gaw_mtl_diffuse(mtl->kd.x, mtl->kd.y, mtl->kd.z, 1.0f);
+ gaw_mtl_specular(mtl->ks.x, mtl->ks.y, mtl->ks.z, mtl->shin);
+ gaw_mtl_emission(mtl->ke.x, mtl->ke.y, mtl->ke.z);
+}
+
static void draw_grid(void)
{
gaw_save();
}
rendering = 1;
rend_size(win_width, win_height);
+ fix_rect(&rband);
rend_begin(rband.x, rband.y, rband.width, rband.height);
}
}
}
-static void draw_rband(void)
+static void fix_rect(rtk_rect *rect)
{
- int i, x, y, w, h;
- uint32_t *fbptr, *bptr;
+ int x, y, w, h;
x = rband.x;
y = rband.y;
h = rband.height;
}
- fbptr = framebuf + y * win_width + x;
- bptr = fbptr + win_width * (h - 1);
+ rect->x = x;
+ rect->y = y;
+ rect->width = w;
+ rect->height = h;
+}
+
+static void draw_rband(void)
+{
+ int i;
+ rtk_rect rect;
+ uint32_t *fbptr, *bptr;
+
+ rect = rband;
+ fix_rect(&rect);
+
+ fbptr = framebuf + rect.y * win_width + rect.x;
+ bptr = fbptr + win_width * (rect.height - 1);
- for(i=0; i<w; i++) {
+ for(i=0; i<rect.width; i++) {
fbptr[i] ^= 0xffffff;
bptr[i] ^= 0xffffff;
}
fbptr += win_width;
- for(i=0; i<h-2; i++) {
+ for(i=0; i<rect.height-2; i++) {
fbptr[0] ^= 0xffffff;
- fbptr[w - 1] ^= 0xffffff;
+ fbptr[rect.width - 1] ^= 0xffffff;
fbptr += win_width;
}
}
--- /dev/null
+/*
+RetroRay - integrated standalone vintage modeller/renderer
+Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include "texture.h"
+#include "geom.h"
+#include "imago2.h"
+#include "logger.h"
+#include "sizeint.h"
+#include "util.h"
+
+static cgm_vec3 lookup_pixmap(const struct texture *btex, const struct rayhit *hit);
+static cgm_vec3 lookup_chess(const struct texture *btex, const struct rayhit *hit);
+static cgm_vec3 lookup_fbm2d(const struct texture *btex, const struct rayhit *hit);
+static cgm_vec3 lookup_fbm3d(const struct texture *btex, const struct rayhit *hit);
+static cgm_vec3 lookup_marble2d(const struct texture *btex, const struct rayhit *hit);
+static cgm_vec3 lookup_marble3d(const struct texture *btex, const struct rayhit *hit);
+
+static const cgm_vec3 (*lookup[])(const struct texture*, const struct rayhit*) = {
+ lookup_pixmap,
+ lookup_chess,
+ lookup_fbm2d,
+ lookup_fbm3d,
+ lookup_marble2d,
+ lookup_marble3d
+};
+
+static const char *defname_fmt[] = {
+ "pixmap%03d", "chess%03d",
+ "fbm%03d", "sfbm%03d",
+ "marble%03d", "smarble%03d"
+};
+
+struct texture *create_texture(int type)
+{
+ struct texture *tex;
+ struct tex_pixmap *tpix;
+ struct tex_chess *tchess;
+ struct tex_fbm *tfbm;
+ static int texidx;
+ char namebuf[64];
+
+ switch(type) {
+ case TEX_PIXMAP:
+ if(!(tpix = malloc(sizeof *tpix))) {
+ goto fail;
+ }
+ tex = (struct texture*)tpix;
+ tpix->img = 0;
+ break;
+
+ case TEX_CHESS:
+ if(!(tchess = malloc(sizeof *tchess))) {
+ goto fail;
+ }
+ tex = (struct texture*)tchess;
+ cgm_vcons(tchess->color, 0, 0, 0);
+ cgm_vcons(tchess->color + 1, 1, 1, 1);
+ break;
+
+ case TEX_FBM2D:
+ case TEX_FBM3D:
+ if(!(tfbm = malloc(sizeof *tfbm))) {
+ goto fail;
+ }
+ tex = (struct texture*)tfbm;
+ tfbm->octaves = 1;
+ cgm_vcons(tfbm->color, 0, 0, 0);
+ cgm_vcons(tfbm->color + 1, 1, 1, 1);
+ break;
+
+ default:
+ errormsg("tried to allocate invalid texture type (%d)\n", type);
+ return 0;
+ }
+
+ tex->type = type;
+ tex->name = 0;
+ cgm_vcons(&tex->offs, 0, 0, 0);
+ cgm_vcons(&tex->scale, 1, 1, 1);
+ tex->lookup = lookup[type];
+
+ sprintf(namebuf, defname_fmt[type], texidx++);
+ tex_set_name(tex, namebuf);
+ return tex;
+
+fail:
+ errormsg("failed to allocate texture\n");
+ return 0;
+}
+
+void free_texture(struct texture *tex)
+{
+ if(!tex) return;
+ free(tex->name);
+ free(tex);
+}
+
+void tex_set_name(struct texture *tex, const char *name)
+{
+ char *tmp = strdup(name);
+ if(!tmp) return;
+ free(tex->name);
+ tex->name = tmp;
+}
+
+#define XFORM_UV(tex, u, v) \
+ do { \
+ u *= (tex)->scale.x + (tex)->offs.x; \
+ v *= (tex)->scale.y + (tex)->offs.y; \
+ } while(0)
+
+#define XFORM_UVW(tex, u, v, w) \
+ do { \
+ u *= (tex)->scale.x + (tex)->offs.x; \
+ v *= (tex)->scale.y + (tex)->offs.y; \
+ w *= (tex)->scale.z * (tex)->offs.z; \
+ } while(0)
+
+static cgm_vec3 lookup_pixmap(const struct texture *btex, const struct rayhit *hit)
+{
+ struct tex_pixmap *tex = (struct tex_pixmap*)btex;
+ int px, py, twidth, theight;
+ int r, g, b;
+ uint32_t texel;
+ float u = hit->uv.x;
+ float v = hit->uv.y;
+
+ twidth = tex->img->width;
+ theight = tex->img->height;
+
+ XFORM_UV(tex, u, v);
+
+ px = cround64((float)twidth * u) % twidth;
+ py = cround64((float)theight * v) % theight;
+ if(px < 0) px += twidth;
+ if(py < 0) py += theight;
+
+ texel = ((uint32_t*)tex->img->pixels)[py * twidth + px];
+ r = texel & 0xff;
+ g = (texel >> 8) & 0xff;
+ b = (texel >> 16) & 0xff;
+
+ return cgm_vvec(r / 255.0f, g / 255.0f, b / 255.0f);
+}
+
+static cgm_vec3 lookup_chess(const struct texture *btex, const struct rayhit *hit)
+{
+ struct tex_chess *tex = (struct tex_chess*)btex;
+ int cx, cy, chess;
+ float u = hit->uv.x;
+ float v = hit->uv.y;
+
+ XFORM_UV(tex, u, v);
+
+ cx = cround64(u) & 1;
+ cy = cround64(v) & 1;
+ chess = cx ^ cy;
+ return tex->color[chess];
+}
+
+static cgm_vec3 lookup_fbm2d(const struct texture *btex, const struct rayhit *hit)
+{
+ return cgm_vvec(1, 0, 0); /* TODO */
+}
+
+static cgm_vec3 lookup_fbm3d(const struct texture *btex, const struct rayhit *hit)
+{
+ return cgm_vvec(1, 0, 0); /* TODO */
+}
+
+static cgm_vec3 lookup_marble2d(const struct texture *btex, const struct rayhit *hit)
+{
+ return cgm_vvec(1, 0, 0); /* TODO */
+}
+
+static cgm_vec3 lookup_marble3d(const struct texture *btex, const struct rayhit *hit)
+{
+ return cgm_vvec(1, 0, 0); /* TODO */
+}
--- /dev/null
+/*
+RetroRay - integrated standalone vintage modeller/renderer
+Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef TEXTURE_H_
+#define TEXTURE_H_
+
+#include "cgmath/cgmath.h"
+#include "imago2.h"
+
+struct rayhit;
+
+enum {
+ TEX_PIXMAP,
+ TEX_CHESS,
+ TEX_FBM2D,
+ TEX_FBM3D,
+ TEX_MARBLE2D,
+ TEX_MARBLE3D
+};
+
+#define TEX_COMMON_ATTR \
+ int type; \
+ char *name; \
+ cgm_vec3 offs, scale; \
+ cgm_vec3 (*lookup)(const struct texture*, const struct rayhit*)
+
+struct texture {
+ TEX_COMMON_ATTR;
+};
+
+struct tex_pixmap {
+ TEX_COMMON_ATTR;
+ struct img_pixmap *img;
+};
+
+struct tex_chess {
+ TEX_COMMON_ATTR;
+ cgm_vec3 color[2];
+};
+
+struct tex_fbm {
+ TEX_COMMON_ATTR;
+ int octaves;
+ cgm_vec3 color[2];
+};
+
+struct texture *create_texture(int type);
+void free_texture(struct texture *tex);
+
+void tex_set_name(struct texture *tex, const char *name);
+
+#endif /* TEXTURE_H_ */