/*
* Copyright (c) 2006-2007 Hypertriton, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Light source node object.
*/
#include
#include
#include
#include
#include
SG_Light *
SG_LightNew(void *parent, const char *name)
{
SG_Light *lt;
lt = Malloc(sizeof(SG_Light));
AG_ObjectInitNamed(lt, &sgLightClass, name);
AG_ObjectAttach(parent, lt);
return (lt);
}
static void
Init(void *_Nonnull obj)
{
SG_Light *lt = obj;
static int nlight = GL_LIGHT0; /* XXX */
lt->pri = 0;
lt->light = nlight++; /* XXX test */
lt->ambient = M_ColorBlack();
lt->diffuse = M_ColorWhite();
lt->specular = M_ColorWhite();
lt->spot_dir = M_VecZero3();
lt->spot_exponent = 3.0;
lt->spot_cutoff = 180.0;
lt->Kc = 1.0;
lt->Kl = 0.0;
lt->Kq = 0.0;
#ifdef HAVE_GLU
lt->qo = gluNewQuadric();
gluQuadricDrawStyle(lt->qo, GLU_FILL);
gluQuadricNormals(lt->qo, GLU_SMOOTH);
#endif
}
static void
Destroy(void *_Nonnull obj)
{
#ifdef HAVE_GLU
SG_Light *lt = obj;
gluDeleteQuadric(lt->qo);
#endif
}
static int
Load(void *_Nonnull obj, AG_DataSource *_Nonnull ds, const AG_Version *_Nonnull ver)
{
SG_Light *lt = obj;
lt->pri = (int)AG_ReadSint32(ds);
lt->light = (int)AG_ReadSint32(ds);
lt->ambient = M_ReadColor(ds);
lt->diffuse = M_ReadColor(ds);
lt->specular = M_ReadColor(ds);
lt->spot_dir = M_ReadVector3(ds);
lt->spot_exponent = M_ReadReal(ds);
lt->spot_cutoff = M_ReadReal(ds);
lt->Kc = M_ReadReal(ds);
lt->Kl = M_ReadReal(ds);
lt->Kq = M_ReadReal(ds);
return (0);
}
static int
Save(void *_Nonnull obj, AG_DataSource *_Nonnull ds)
{
SG_Light *lt = obj;
AG_WriteSint32(ds, (Sint32)lt->pri);
AG_WriteSint32(ds, (Sint32)lt->light);
M_WriteColor(ds, <->ambient);
M_WriteColor(ds, <->diffuse);
M_WriteColor(ds, <->specular);
M_WriteVector3(ds, <->spot_dir);
M_WriteReal(ds, lt->spot_exponent);
M_WriteReal(ds, lt->spot_cutoff);
M_WriteReal(ds, lt->Kc);
M_WriteReal(ds, lt->Kl);
M_WriteReal(ds, lt->Kq);
return (0);
}
static void
Draw(void *_Nonnull obj, SG_View *_Nonnull view)
{
#ifdef HAVE_GLU
SG_Light *lt = obj;
if (lt->spot_cutoff == 180.0) {
gluSphere(lt->qo, 0.125, 4, 4);
} else {
gluCylinder(lt->qo, 0.125, 0.125, 1.0, 4, 3);
}
#endif /* HAVE_GLU */
}
/*
* Set up OpenGL light sources for rendering.
* Must be called from widget draw context.
*/
void
SG_LightSetup(SG_Light *lt)
{
M_Vector3 v;
GLfloat posf[4];
GLfloat dirf[4];
AG_ObjectLock(lt);
GL_Enable(lt->light);
v = SG_NodePos(lt);
posf[0] = (GLfloat)v.x;
posf[1] = (GLfloat)v.y;
posf[2] = (GLfloat)v.z;
posf[3] = 1.0f;
glLightfv(lt->light, GL_POSITION, posf);
#ifdef DOUBLE_PRECISION
{
float ambient[4];
float diffuse[4];
float specular[4];
M_ColorTo4fv(<->ambient, ambient);
glLightfv(lt->light, GL_AMBIENT, ambient);
M_ColorTo4fv(<->diffuse, diffuse);
glLightfv(lt->light, GL_DIFFUSE, diffuse);
M_ColorTo4fv(<->specular, specular);
glLightfv(lt->light, GL_SPECULAR, specular);
if (lt->spot_cutoff != 180.0) {
v = SG_NodeDir(lt);
dirf[0] = (GLfloat)v.x;
dirf[1] = (GLfloat)v.y;
dirf[2] = (GLfloat)v.z;
dirf[3] = 1.0f;
glLightfv(lt->light, GL_SPOT_DIRECTION, dirf);
}
}
#else
glLightfv(lt->light, GL_AMBIENT, (GLfloat *)<->ambient);
glLightfv(lt->light, GL_DIFFUSE, (GLfloat *)<->diffuse);
glLightfv(lt->light, GL_SPECULAR, (GLfloat *)<->specular);
if (lt->spot_cutoff != 180.0) {
glLightfv(lt->light, GL_SPOT_DIRECTION, dirf);
}
#endif
glLightf(lt->light, GL_SPOT_EXPONENT, (GLfloat)lt->spot_exponent);
glLightf(lt->light, GL_SPOT_CUTOFF, (GLfloat)lt->spot_cutoff);
glLightf(lt->light, GL_CONSTANT_ATTENUATION, (GLfloat)lt->Kc);
glLightf(lt->light, GL_LINEAR_ATTENUATION, (GLfloat)lt->Kl);
glLightf(lt->light, GL_QUADRATIC_ATTENUATION, (GLfloat)lt->Kq);
AG_ObjectUnlock(lt);
}
static void
SelectColor(AG_Event *_Nonnull event)
{
AG_HSVPal *pal = AG_PTR(1);
void *color = AG_PTR(2);
AG_Mutex *lock = AG_PTR(3);
M_BindRealMp(pal, "RGBAv", color, lock);
}
static void *_Nullable
Edit(void *_Nonnull obj, SG_View *_Nullable sgv)
{
SG_Light *lt = obj;
AG_Mutex *lock = &OBJECT(lt)->pvt.lock;
AG_Notebook *nb;
AG_NotebookTab *ntab;
AG_HSVPal *pal;
AG_Numerical *num;
nb = AG_NotebookNew(NULL, AG_NOTEBOOK_EXPAND);
ntab = AG_NotebookAdd(nb, _("Src"), AG_BOX_VERT);
{
M_EditTranslate3Mp(ntab, _("Position: "),
&SGNODE(lt)->T, lock);
num = AG_NumericalNew(ntab, 0, NULL, _("Priority: "));
AG_BindIntMp(num, "value", <->pri, lock);
AG_SeparatorNewHoriz(ntab);
AG_LabelNew(ntab, 0, _("Using: GL_LIGHT%i"),
(int)(lt->light - GL_LIGHT0));
AG_SeparatorNewHoriz(ntab);
num = AG_NumericalNew(ntab, 0, "deg", _("Cutoff angle: "));
M_BindRealMp(num, "value", <->spot_cutoff, lock);
num = AG_NumericalNew(ntab, 0, NULL, _("Spot exponent: "));
M_BindRealMp(num, "value", <->spot_exponent, lock);
M_SetReal(num, "inc", 0.1);
}
ntab = AG_NotebookAdd(nb, _("Attenuation"), AG_BOX_VERT);
{
num = AG_NumericalNew(ntab, 0, NULL, "Kc: ");
M_BindRealMp(num, "value", <->Kc, lock);
M_SetReal(num, "inc", 0.01);
num = AG_NumericalNew(ntab, 0, NULL, "Kl: ");
M_BindRealMp(num, "value", <->Kl, lock);
M_SetReal(num, "inc", 0.001);
num = AG_NumericalNew(ntab, 0, NULL, "Kq: ");
M_BindRealMp(num, "value", <->Kq, lock);
M_SetReal(num, "inc", 0.00001);
}
ntab = AG_NotebookAdd(nb, _("Color"), AG_BOX_VERT);
{
AG_Toolbar *bar;
pal = AG_HSVPalNew(ntab, AG_HSVPAL_EXPAND);
M_BindRealMp(pal, "RGBAv", (void *)<->ambient, lock);
bar = AG_ToolbarNew(ntab, AG_TOOLBAR_HORIZ, 1,
AG_TOOLBAR_HOMOGENOUS|AG_TOOLBAR_STICKY);
{
AG_ToolbarButton(bar, _("Ambient"), 1,
SelectColor, "%p,%p", pal, <->ambient, lock);
AG_ToolbarButton(bar, _("Diffuse"), 0,
SelectColor, "%p,%p", pal, <->diffuse, lock);
AG_ToolbarButton(bar, _("Specular"), 0,
SelectColor, "%p,%p", pal, <->specular, lock);
}
}
return (nb);
}
#if 0
static void
MenuInstance(void *_Nonnull obj, AG_MenuItem *_Nonnull m, SG_View *_Nonnull sgv)
{
#if 0
SG_Light *lt = obj;
AG_MenuAction(m, _("Light parameters..."), NULL,
EditLightParams, "%p", lt);
#endif
}
#endif
SG_NodeClass sgLightClass = {
{
"SG_Node:SG_Light",
sizeof(SG_Light),
{ 0,0 },
Init,
NULL, /* free */
Destroy,
Load,
Save,
SG_NodeEdit
},
NULL, /* menuInstance */
NULL, /* menuClass */
Draw,
NULL, /* intersect */
Edit
};