summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2024-07-13 10:23:52 +0000
committerrodri <rgl@antares-labs.eu>2024-07-13 10:23:52 +0000
commit04d50b8bbe324fdb7a8a1e724fbc59c4050862f0 (patch)
tree285be4b648747346d96e5e0c87d0abec9f80d6a5
parent45c21bb2483adf0ad1dd2f9950681f6a07a14b24 (diff)
downloadlibgraphics-04d50b8bbe324fdb7a8a1e724fbc59c4050862f0.tar.gz
libgraphics-04d50b8bbe324fdb7a8a1e724fbc59c4050862f0.tar.bz2
libgraphics-04d50b8bbe324fdb7a8a1e724fbc59c4050862f0.zip
fix the geometry glitches when moving things around.
it's possible for interactive programs to keep updating the geometry of the scene during rendering, which caused primitives to look deformed or shattered in the final image. to avoid this we take a snapshot of the current state of things (scene and camera), and render based on that copy.
-rw-r--r--camera.c67
-rw-r--r--clip.c21
-rw-r--r--graphics.h10
-rw-r--r--internal.h2
-rw-r--r--render.c146
-rw-r--r--scene.c125
-rw-r--r--texture.c19
-rw-r--r--util.c15
8 files changed, 260 insertions, 145 deletions
diff --git a/camera.c b/camera.c
index 60f3c4a..ed220a3 100644
--- a/camera.c
+++ b/camera.c
@@ -106,6 +106,36 @@ verifycfg(Camera *c)
assert(c->clip.n > 0 && c->clip.n < c->clip.f);
}
+/* TODO the current camera abstraction is quite dirty, not pleasant to work with. make it better. */
+//Camera *
+//Cam(Camcfg cfg)
+//{
+//
+//}
+
+void
+reloadcamera(Camera *c)
+{
+ double a;
+ double l, r, b, t;
+
+ verifycfg(c);
+ switch(c->projtype){
+ case ORTHOGRAPHIC:
+ r = Dx(c->vp->fbctl->fb[0]->r)/2;
+ t = Dy(c->vp->fbctl->fb[0]->r)/2;
+ l = -r;
+ b = -t;
+ orthographic(c->proj, l, r, b, t, c->clip.n, c->clip.f);
+ break;
+ case PERSPECTIVE:
+ a = (double)Dx(c->vp->fbctl->fb[0]->r)/Dy(c->vp->fbctl->fb[0]->r);
+ perspective(c->proj, c->fov, a, c->clip.n, c->clip.f);
+ break;
+ default: sysfatal("unknown projection type");
+ }
+}
+
void
configcamera(Camera *c, Viewport *v, double fov, double n, double f, Projection p)
{
@@ -133,29 +163,6 @@ aimcamera(Camera *c, Point3 focus)
}
void
-reloadcamera(Camera *c)
-{
- double a;
- double l, r, b, t;
-
- verifycfg(c);
- switch(c->projtype){
- case ORTHOGRAPHIC:
- r = Dx(c->vp->fbctl->fb[0]->r)/2;
- t = Dy(c->vp->fbctl->fb[0]->r)/2;
- l = -r;
- b = -t;
- orthographic(c->proj, l, r, b, t, c->clip.n, c->clip.f);
- break;
- case PERSPECTIVE:
- a = (double)Dx(c->vp->fbctl->fb[0]->r)/Dy(c->vp->fbctl->fb[0]->r);
- perspective(c->proj, c->fov, a, c->clip.n, c->clip.f);
- break;
- default: sysfatal("unknown projection type");
- }
-}
-
-void
shootcamera(Camera *c, Shadertab *s)
{
static Scene *skyboxscene;
@@ -167,8 +174,9 @@ shootcamera(Camera *c, Shadertab *s)
job = emalloc(sizeof *job);
memset(job, 0, sizeof *job);
job->fb = c->vp->fbctl->getbb(c->vp->fbctl);
- job->camera = c;
- job->scene = c->scene;
+ job->camera = emalloc(sizeof *c);
+ *job->camera = *c;
+ job->scene = dupscene(c->scene); /* take a snapshot */
job->shaders = s;
job->donec = chancreate(sizeof(void*), 0);
@@ -176,21 +184,23 @@ shootcamera(Camera *c, Shadertab *s)
t0 = nanosec();
sendp(c->rctl->c, job);
recvp(job->donec);
+ delscene(job->scene); /* destroy the snapshot */
/*
* if the scene has a skybox, do another render pass,
- * filling in the fragments left untouched by the z-buffer.
+ * filling in the pixels left untouched.
*/
if(c->scene->skybox != nil){
if(skyboxscene == nil){
skyboxscene = newscene("skybox");
mdl = mkskyboxmodel();
- skyboxscene->addent(skyboxscene, newentity(mdl));
+ skyboxscene->addent(skyboxscene, newentity("skybox", mdl));
}
skyboxscene->skybox = c->scene->skybox;
- job->scene = skyboxscene;
+ job->scene = dupscene(skyboxscene);
job->shaders = &skyboxshader;
sendp(c->rctl->c, job);
recvp(job->donec);
+ delscene(job->scene);
}
t1 = nanosec();
c->vp->fbctl->swap(c->vp->fbctl);
@@ -199,5 +209,6 @@ shootcamera(Camera *c, Shadertab *s)
updatetimes(c, job);
chanfree(job->donec);
+ free(job->camera);
free(job);
}
diff --git a/clip.c b/clip.c
index db1f58e..a7750cf 100644
--- a/clip.c
+++ b/clip.c
@@ -75,7 +75,7 @@ eqpt3(Point3 a, Point3 b)
* - https://github.com/aap/librw/blob/14dab85dcae6f3762fb2b1eda4d58d8e67541330/tools/playground/tl_tests.cpp#L522
*/
int
-clipprimitive(Primitive *p)
+clipprimitive(Primitive *p, Primitive *cp)
{
/* signed distance from each clipping plane */
static double sdm[6][4] = {
@@ -97,8 +97,8 @@ clipprimitive(Primitive *p)
Vout = &Voutp;
memset(Vin, 0, sizeof Vinp);
memset(Vout, 0, sizeof Voutp);
- for(i = 0; i < p[0].type+1; i++)
- addvert(Vin, p[0].v[i]);
+ for(i = 0; i < p->type+1; i++)
+ addvert(Vin, p->v[i]);
for(j = 0; j < 6 && Vin->n > 0; j++){
for(i = 0; i < Vin->n; i++){
@@ -133,10 +133,11 @@ allin:
if(Vout->n < 2)
cleanpoly(Vout);
- else switch(p[0].type){
+ else switch(p->type){
case PLine:
- p[0].v[0] = dupvertex(&Vout->v[0]);
- p[0].v[1] = eqpt3(Vout->v[0].p, Vout->v[1].p)? dupvertex(&Vout->v[2]): dupvertex(&Vout->v[1]);
+ cp[0] = *p;
+ cp[0].v[0] = dupvertex(&Vout->v[0]);
+ cp[0].v[1] = eqpt3(Vout->v[0].p, Vout->v[1].p)? dupvertex(&Vout->v[2]): dupvertex(&Vout->v[1]);
cleanpoly(Vout);
np = 1;
break;
@@ -148,10 +149,10 @@ allin:
* are referenced on every triangle, so duplicate them
* to avoid complications during rasterization.
*/
- p[np] = p[0];
- p[np].v[0] = i < Vout->n-2-1? dupvertex(&Vout->v[0]): Vout->v[0];
- p[np].v[1] = Vout->v[i+1];
- p[np].v[2] = i < Vout->n-2-1? dupvertex(&Vout->v[i+2]): Vout->v[i+2];
+ cp[np] = *p;
+ cp[np].v[0] = i < Vout->n-2-1? dupvertex(&Vout->v[0]): Vout->v[0];
+ cp[np].v[1] = Vout->v[i+1];
+ cp[np].v[2] = i < Vout->n-2-1? dupvertex(&Vout->v[i+2]): Vout->v[i+2];
}
break;
}
diff --git a/graphics.h b/graphics.h
index fbeea2f..47a41cd 100644
--- a/graphics.h
+++ b/graphics.h
@@ -156,6 +156,7 @@ struct Model
struct Entity
{
RFrame3;
+ char *name;
Model *mdl;
Entity *prev, *next;
@@ -294,10 +295,10 @@ struct Camera
};
/* camera */
+void reloadcamera(Camera*);
void configcamera(Camera*, Viewport*, double, double, double, Projection);
void placecamera(Camera*, Point3, Point3, Point3);
void aimcamera(Camera*, Point3);
-void reloadcamera(Camera*);
void shootcamera(Camera*, Shadertab*);
/* viewport */
@@ -326,10 +327,13 @@ void orthographic(Matrix3, double, double, double, double, double, double);
/* scene */
int loadobjmodel(Model*, OBJ*);
Model *newmodel(void);
+Model *dupmodel(Model*);
void delmodel(Model*);
-Entity *newentity(Model*);
+Entity *newentity(char*, Model*);
+Entity *dupentity(Entity*);
void delentity(Entity*);
Scene *newscene(char*);
+Scene *dupscene(Scene*);
void delscene(Scene*);
void clearscene(Scene*);
@@ -339,6 +343,7 @@ Vertexattr *getvattr(Vertex*, char*);
/* texture */
Texture *alloctexture(int, Memimage*);
+Texture *duptexture(Texture*);
void freetexture(Texture*);
Color neartexsampler(Texture*, Point2);
Color bilitexsampler(Texture*, Point2);
@@ -353,6 +358,7 @@ double fmax(double, double);
Point2 modulapt2(Point2, Point2);
Point3 modulapt3(Point3, Point3);
Memimage *rgb(ulong);
+Memimage *dupmemimage(Memimage*);
/* color */
Color srgb2linear(Color);
diff --git a/internal.h b/internal.h
index 6e66229..361b013 100644
--- a/internal.h
+++ b/internal.h
@@ -51,7 +51,7 @@ void delvattrs(Vertex*);
void fprintvattrs(int, Vertex*);
/* clip */
-int clipprimitive(Primitive*);
+int clipprimitive(Primitive*, Primitive*);
int rectclipline(Rectangle, Point*, Point*);
/* util */
diff --git a/render.c b/render.c
index 278c186..3d980af 100644
--- a/render.c
+++ b/render.c
@@ -67,13 +67,13 @@ isvisible(Point3 p)
}
static int
-isfacingback(Primitive p)
+isfacingback(Primitive *p)
{
double sa; /* signed area */
- sa = p.v[0].p.x * p.v[1].p.y - p.v[0].p.y * p.v[1].p.x +
- p.v[1].p.x * p.v[2].p.y - p.v[1].p.y * p.v[2].p.x +
- p.v[2].p.x * p.v[0].p.y - p.v[2].p.y * p.v[0].p.x;
+ sa = p->v[0].p.x * p->v[1].p.y - p->v[0].p.y * p->v[1].p.x +
+ p->v[1].p.x * p->v[2].p.y - p->v[1].p.y * p->v[2].p.x +
+ p->v[2].p.x * p->v[0].p.y - p->v[2].p.y * p->v[0].p.x;
return sa <= 0;
}
@@ -277,7 +277,7 @@ tilerdurden(void *arg)
SUparams *params, *newparams;
Rastertask *task;
VSparams vsp;
- Primitive *ep, *p; /* primitives to raster */
+ Primitive *ep, *cp, *p; /* primitives to raster */
Rectangle *wr, bbox;
Channel **taskchans;
ulong Δy, nproc;
@@ -285,7 +285,7 @@ tilerdurden(void *arg)
uvlong t0;
tp = arg;
- p = emalloc(sizeof(*p)*16);
+ cp = emalloc(sizeof(*cp)*16);
taskchans = tp->taskchans;
nproc = tp->nproc;
wr = emalloc(nproc*sizeof(Rectangle));
@@ -323,27 +323,27 @@ tilerdurden(void *arg)
for(ep = params->eb; ep != params->ee; ep++){
np = 1; /* start with one. after clipping it might change */
- *p = *ep;
- switch(ep->type){
+ p = ep;
+ switch(p->type){
case PPoint:
- p[0].v[0].mtl = ep->mtl;
- p[0].v[0].attrs = nil;
- p[0].v[0].nattrs = 0;
+ p->v[0].mtl = p->mtl;
+ p->v[0].attrs = nil;
+ p->v[0].nattrs = 0;
- vsp.v = &p[0].v[0];
+ vsp.v = &p->v[0];
vsp.idx = 0;
- p[0].v[0].p = params->vshader(&vsp);
+ p->v[0].p = params->vshader(&vsp);
- if(!isvisible(p[0].v[0].p))
+ if(!isvisible(p->v[0].p))
break;
- p[0].v[0].p = clip2ndc(p[0].v[0].p);
- p[0].v[0].p = ndc2viewport(params->fb, p[0].v[0].p);
+ p->v[0].p = clip2ndc(p->v[0].p);
+ p->v[0].p = ndc2viewport(params->fb, p->v[0].p);
- bbox.min.x = p[0].v[0].p.x;
- bbox.min.y = p[0].v[0].p.y;
- bbox.max.x = p[0].v[0].p.x+1;
- bbox.max.y = p[0].v[0].p.y+1;
+ bbox.min.x = p->v[0].p.x;
+ bbox.min.y = p->v[0].p.y;
+ bbox.max.x = p->v[0].p.x+1;
+ bbox.max.y = p->v[0].p.y+1;
for(i = 0; i < nproc; i++)
if(rectXrect(bbox,wr[i])){
@@ -352,37 +352,39 @@ tilerdurden(void *arg)
task = emalloc(sizeof *task);
task->params = newparams;
task->wr = wr[i];
- task->p = p[0];
- task->p.v[0] = dupvertex(&p[0].v[0]);
+ task->p = *p;
+ task->p.v[0] = dupvertex(&p->v[0]);
sendp(taskchans[i], task);
}
- delvattrs(&p[0].v[0]);
+ delvattrs(&p->v[0]);
break;
case PLine:
for(i = 0; i < 2; i++){
- p[0].v[i].mtl = ep->mtl;
- p[0].v[i].attrs = nil;
- p[0].v[i].nattrs = 0;
+ p->v[i].mtl = p->mtl;
+ p->v[i].attrs = nil;
+ p->v[i].nattrs = 0;
- vsp.v = &p[0].v[i];
+ vsp.v = &p->v[i];
vsp.idx = i;
- p[0].v[i].p = params->vshader(&vsp);
+ p->v[i].p = params->vshader(&vsp);
}
- if(!isvisible(p[0].v[0].p) || !isvisible(p[0].v[1].p))
- np = clipprimitive(p);
+ if(!isvisible(p->v[0].p) || !isvisible(p->v[1].p)){
+ np = clipprimitive(p, cp);
+ p = cp;
+ }
- while(np--){
- p[np].v[0].p = clip2ndc(p[np].v[0].p);
- p[np].v[1].p = clip2ndc(p[np].v[1].p);
+ for(; np--; p++){
+ p->v[0].p = clip2ndc(p->v[0].p);
+ p->v[1].p = clip2ndc(p->v[1].p);
- p[np].v[0].p = ndc2viewport(params->fb, p[np].v[0].p);
- p[np].v[1].p = ndc2viewport(params->fb, p[np].v[1].p);
+ p->v[0].p = ndc2viewport(params->fb, p->v[0].p);
+ p->v[1].p = ndc2viewport(params->fb, p->v[1].p);
- bbox.min.x = min(p[np].v[0].p.x, p[np].v[1].p.x);
- bbox.min.y = min(p[np].v[0].p.y, p[np].v[1].p.y);
- bbox.max.x = max(p[np].v[0].p.x, p[np].v[1].p.x)+1;
- bbox.max.y = max(p[np].v[0].p.y, p[np].v[1].p.y)+1;
+ bbox.min.x = min(p->v[0].p.x, p->v[1].p.x);
+ bbox.min.y = min(p->v[0].p.y, p->v[1].p.y);
+ bbox.max.x = max(p->v[0].p.x, p->v[1].p.x)+1;
+ bbox.max.y = max(p->v[0].p.y, p->v[1].p.y)+1;
for(i = 0; i < nproc; i++)
if(rectXrect(bbox,wr[i])){
@@ -391,47 +393,49 @@ tilerdurden(void *arg)
task = emalloc(sizeof *task);
task->params = newparams;
task->wr = wr[i];
- task->p = p[np];
- task->p.v[0] = dupvertex(&p[np].v[0]);
- task->p.v[1] = dupvertex(&p[np].v[1]);
+ task->p = *p;
+ task->p.v[0] = dupvertex(&p->v[0]);
+ task->p.v[1] = dupvertex(&p->v[1]);
sendp(taskchans[i], task);
}
- delvattrs(&p[np].v[0]);
- delvattrs(&p[np].v[1]);
+ delvattrs(&p->v[0]);
+ delvattrs(&p->v[1]);
}
break;
case PTriangle:
for(i = 0; i < 3; i++){
- p[0].v[i].mtl = p->mtl;
- p[0].v[i].attrs = nil;
- p[0].v[i].nattrs = 0;
- p[0].v[i].tangent = p->tangent;
+ p->v[i].mtl = p->mtl;
+ p->v[i].attrs = nil;
+ p->v[i].nattrs = 0;
+ p->v[i].tangent = p->tangent;
- vsp.v = &p[0].v[i];
+ vsp.v = &p->v[i];
vsp.idx = i;
- p[0].v[i].p = params->vshader(&vsp);
+ p->v[i].p = params->vshader(&vsp);
}
- if(!isvisible(p[0].v[0].p) || !isvisible(p[0].v[1].p) || !isvisible(p[0].v[2].p))
- np = clipprimitive(p);
+ if(!isvisible(p->v[0].p) || !isvisible(p->v[1].p) || !isvisible(p->v[2].p)){
+ np = clipprimitive(p, cp);
+ p = cp;
+ }
- while(np--){
- p[np].v[0].p = clip2ndc(p[np].v[0].p);
- p[np].v[1].p = clip2ndc(p[np].v[1].p);
- p[np].v[2].p = clip2ndc(p[np].v[2].p);
+ for(; np--; p++){
+ p->v[0].p = clip2ndc(p->v[0].p);
+ p->v[1].p = clip2ndc(p->v[1].p);
+ p->v[2].p = clip2ndc(p->v[2].p);
/* culling */
-// if(isfacingback(p[np]))
+// if(isfacingback(*p))
// goto skiptri;
- p[np].v[0].p = ndc2viewport(params->fb, p[np].v[0].p);
- p[np].v[1].p = ndc2viewport(params->fb, p[np].v[1].p);
- p[np].v[2].p = ndc2viewport(params->fb, p[np].v[2].p);
+ p->v[0].p = ndc2viewport(params->fb, p->v[0].p);
+ p->v[1].p = ndc2viewport(params->fb, p->v[1].p);
+ p->v[2].p = ndc2viewport(params->fb, p->v[2].p);
- bbox.min.x = min(min(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x);
- bbox.min.y = min(min(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y);
- bbox.max.x = max(max(p[np].v[0].p.x, p[np].v[1].p.x), p[np].v[2].p.x)+1;
- bbox.max.y = max(max(p[np].v[0].p.y, p[np].v[1].p.y), p[np].v[2].p.y)+1;
+ bbox.min.x = min(min(p->v[0].p.x, p->v[1].p.x), p->v[2].p.x);
+ bbox.min.y = min(min(p->v[0].p.y, p->v[1].p.y), p->v[2].p.y);
+ bbox.max.x = max(max(p->v[0].p.x, p->v[1].p.x), p->v[2].p.x)+1;
+ bbox.max.y = max(max(p->v[0].p.y, p->v[1].p.y), p->v[2].p.y)+1;
for(i = 0; i < nproc; i++)
if(rectXrect(bbox,wr[i])){
@@ -440,16 +444,16 @@ tilerdurden(void *arg)
task = emalloc(sizeof *task);
task->params = newparams;
task->wr = wr[i];
- task->p = p[np];
- task->p.v[0] = dupvertex(&p[np].v[0]);
- task->p.v[1] = dupvertex(&p[np].v[1]);
- task->p.v[2] = dupvertex(&p[np].v[2]);
+ task->p = *p;
+ task->p.v[0] = dupvertex(&p->v[0]);
+ task->p.v[1] = dupvertex(&p->v[1]);
+ task->p.v[2] = dupvertex(&p->v[2]);
sendp(taskchans[i], task);
}
//skiptri:
- delvattrs(&p[np].v[0]);
- delvattrs(&p[np].v[1]);
- delvattrs(&p[np].v[2]);
+ delvattrs(&p->v[0]);
+ delvattrs(&p->v[1]);
+ delvattrs(&p->v[2]);
}
break;
default: sysfatal("alien primitive detected");
diff --git a/scene.c b/scene.c
index ba30de9..95c6203 100644
--- a/scene.c
+++ b/scene.c
@@ -166,18 +166,12 @@ loadobjmodel(Model *m, OBJ *obj)
if(objmtl->map_Kd != nil){
mtl->diffusemap = alloctexture(sRGBTexture, nil);
- mtl->diffusemap->image = allocmemimaged(objmtl->map_Kd->r, objmtl->map_Kd->chan, objmtl->map_Kd->data);
- if(mtl->diffusemap->image == nil)
- sysfatal("allocmemimaged: %r");
- mtl->diffusemap->image->data->ref++;
+ mtl->diffusemap->image = dupmemimage(objmtl->map_Kd);
}
if(objmtl->norm != nil){
mtl->normalmap = alloctexture(RAWTexture, nil);
- mtl->normalmap->image = allocmemimaged(objmtl->norm->r, objmtl->norm->chan, objmtl->norm->data);
- if(mtl->normalmap->image == nil)
- sysfatal("allocmemimaged: %r");
- mtl->normalmap->image->data->ref++;
+ mtl->normalmap->image = dupmemimage(objmtl->norm);
}
addmtlmap(&mtlmap, objmtl, m->nmaterials-1);
@@ -318,29 +312,61 @@ newmodel(void)
return m;
}
-void
-delmodel(Model *m)
+Model *
+dupmodel(Model *m)
{
+ Model *nm;
+ int i;
+
if(m == nil)
- return;
+ return nil;
+
+ nm = newmodel();
if(m->tex != nil)
- freetexture(m->tex);
+ nm->tex = duptexture(m->tex);
if(m->nmaterials > 0){
- while(m->nmaterials--){
- freetexture(m->materials[m->nmaterials].diffusemap);
- freetexture(m->materials[m->nmaterials].normalmap);
- free(m->materials[m->nmaterials].name);
+ nm->nmaterials = m->nmaterials;
+ nm->materials = emalloc(nm->nmaterials*sizeof(*nm->materials));
+ for(i = 0; i < m->nmaterials; i++){
+ nm->materials[i] = m->materials[i];
+ nm->materials[i].diffusemap = duptexture(m->materials[i].diffusemap);
+ nm->materials[i].normalmap = duptexture(m->materials[i].normalmap);
+ nm->materials[i].name = strdup(m->materials[i].name);
+ if(nm->materials[i].name == nil)
+ sysfatal("strdup: %r");
}
- free(m->materials);
}
- if(m->nprims > 0)
- free(m->prims);
- memset(m, 0, sizeof *m);
+ if(m->nprims > 0){
+ nm->nprims = m->nprims;
+ nm->prims = emalloc(nm->nprims*sizeof(*nm->prims));
+ for(i = 0; i < m->nprims; i++){
+ nm->prims[i] = m->prims[i];
+ if(nm->nmaterials > 0 && m->prims[i].mtl != nil)
+ nm->prims[i].mtl = &nm->materials[m->prims[i].mtl - m->materials];
+ }
+ }
+ return nm;
+}
+
+void
+delmodel(Model *m)
+{
+ if(m == nil)
+ return;
+
+ freetexture(m->tex);
+ while(m->nmaterials--){
+ freetexture(m->materials[m->nmaterials].diffusemap);
+ freetexture(m->materials[m->nmaterials].normalmap);
+ free(m->materials[m->nmaterials].name);
+ }
+ free(m->materials);
+ free(m->prims);
free(m);
}
Entity *
-newentity(Model *m)
+newentity(char *name, Model *m)
{
Entity *e;
@@ -349,19 +375,37 @@ newentity(Model *m)
e->bx = Vec3(1,0,0);
e->by = Vec3(0,1,0);
e->bz = Vec3(0,0,1);
+ e->name = name == nil? nil: strdup(name);
e->mdl = m;
e->prev = e->next = nil;
return e;
}
+Entity *
+dupentity(Entity *e)
+{
+ Entity *ne;
+
+ if(e == nil)
+ return nil;
+
+ ne = newentity(nil, nil);
+ *ne = *e;
+ if(e->name != nil)
+ ne->name = strdup(e->name);
+ ne->mdl = dupmodel(e->mdl);
+ ne->prev = ne->next = nil;
+ return ne;
+}
+
void
delentity(Entity *e)
{
if(e == nil)
return;
- if(e->mdl != nil)
- delmodel(e->mdl);
- memset(e, 0, sizeof *e);
+
+ delmodel(e->mdl);
+ free(e->name);
free(e);
}
@@ -399,15 +443,20 @@ newscene(char *name)
return s;
}
-void
-delscene(Scene *s)
+Scene *
+dupscene(Scene *s)
{
+ Scene *ns;
+ Entity *e;
+
if(s == nil)
- return;
- clearscene(s);
- free(s->name);
- memset(s, 0, sizeof *s);
- free(s);
+ return nil;
+
+ ns = newscene(s->name);
+ if(s->nents > 0)
+ for(e = s->ents.next; e != &s->ents; e = e->next)
+ ns->addent(ns, dupentity(e));
+ return ns;
}
void
@@ -420,6 +469,16 @@ clearscene(Scene *s)
s->delent(s, e);
delentity(e);
}
- if(s->skybox != nil)
- freecubemap(s->skybox);
+ freecubemap(s->skybox);
+}
+
+void
+delscene(Scene *s)
+{
+ if(s == nil)
+ return;
+
+ clearscene(s);
+ free(s->name);
+ free(s);
}
diff --git a/texture.c b/texture.c
index 86b2cbd..c75f29c 100644
--- a/texture.c
+++ b/texture.c
@@ -122,9 +122,25 @@ alloctexture(int type, Memimage *i)
return t;
}
+Texture *
+duptexture(Texture *t)
+{
+ Texture *nt;
+
+ if(t == nil)
+ return nil;
+
+ nt = alloctexture(t->type, nil);
+ nt->image = dupmemimage(t->image);
+ return nt;
+}
+
void
freetexture(Texture *t)
{
+ if(t == nil)
+ return;
+
freememimage(t->image);
free(t);
}
@@ -161,6 +177,9 @@ freecubemap(Cubemap *cm)
{
int i;
+ if(cm == nil)
+ return;
+
for(i = 0; i < 6; i++)
freetexture(cm->faces[i]);
free(cm->name);
diff --git a/util.c b/util.c
index db538a3..0b9003e 100644
--- a/util.c
+++ b/util.c
@@ -84,3 +84,18 @@ rgb(ulong c)
memfillcolor(i, c);
return i;
}
+
+Memimage *
+dupmemimage(Memimage *i)
+{
+ Memimage *ni;
+
+ if(i == nil)
+ return nil;
+
+ ni = allocmemimaged(i->r, i->chan, i->data);
+ if(ni == nil)
+ sysfatal("allocmemimaged: %r");
+ ni->data->ref++;
+ return ni;
+}