#include "VRAY_DemoSprite.h"
#include <VRAY/VRAY_IO.h>
#include <GU/GU_Detail.h>
#include <GU/GU_PrimPoly.h>
#include <UT/UT_Defines.h>
#include <SYS/SYS_Floor.h>
#define MIN_CHUNK 8
#define SPRITE_LIMIT 1000
#define META_CORRECT 0.5
#define DEFAULT_ATTRIB_PATTERN ""
namespace HDK_Sample {
class vray_SpriteAttribMap
{
public:
GB_AttributeRef myOffset;
int myDIndex;
const GB_Attribute *myAttrib;
int myFloats;
vray_SpriteAttribMap *myNext;
};
}
using namespace HDK_Sample;
static vray_SpriteAttribMap *
makeMap(const GU_Detail *tplate, const GB_Attribute *atr)
{
vray_SpriteAttribMap *map;
UT_String n;
map = new vray_SpriteAttribMap();
map->myOffset = tplate->findPointAttrib(atr);
map->myDIndex = -1;
map->myAttrib = atr;
if (atr->getType() == GB_ATTRIB_FLOAT)
map->myFloats = atr->getSize() / sizeof(float);
else if (atr->getType()==GB_ATTRIB_INT || atr->getType()==GB_ATTRIB_INDEX)
map->myFloats = atr->getSize() / sizeof(int);
else if (atr->getType() == GB_ATTRIB_VECTOR)
map->myFloats = 3;
else map->myFloats = 0;
return map;
}
static void
destroyMap(vray_SpriteAttribMap *map)
{
vray_SpriteAttribMap *next;
while (map)
{
next = map->myNext;
delete map;
map = next;
}
}
static void
setAttribMap(vray_SpriteAttribMap *&maphead, const GU_Detail *gdp,
const char *pattern)
{
const GB_AttributeDict *dict;
const GB_Attribute *atr;
UT_String name;
vray_SpriteAttribMap *map;
int index = 0;
if (!gdp)
return;
dict = &gdp->pointAttribs();
for (atr = dict->getHead(); atr; atr = (const GB_Attribute *)atr->next())
{
name = atr->getName();
if (name.multiMatch(pattern) && atr->getType() != GB_ATTRIB_MIXED)
{
map = makeMap(gdp, atr);
map->myDIndex = index++;
map->myNext = maphead;
maphead = map;
}
}
}
static VRAY_ProceduralArg theArgs[] = {
VRAY_ProceduralArg("velocity", "string", "v"),
VRAY_ProceduralArg("object", "string", ""),
VRAY_ProceduralArg("attribute", "string", ""),
VRAY_ProceduralArg("chunksize", "int", "16"),
VRAY_ProceduralArg("maxsprites", "int", "1000"),
VRAY_ProceduralArg(),
};
VRAY_Procedural *
allocProcedural(const char *)
{
return new VRAY_DemoSprite();
}
const VRAY_ProceduralArg *
getProceduralArgs(const char *)
{
return theArgs;
}
VRAY_DemoSprite::VRAY_DemoSprite()
{
myBox.initBounds(0, 0, 0);
myVelBox = myBox;
myParms = 0;
}
static void
getRoughSpriteBox(UT_BoundingBox &box, UT_BoundingBox &vbox,
const GEO_Point *point, const UT_Vector3 &sprite_scale,
const GB_AttributeRef &voff,
fpreal tscale, const UT_Matrix4 &xform)
{
fpreal maxradius;
static fpreal isin45 = 1.0F / SYSsin(M_PI/4);
UT_Vector3 pt;
maxradius = SYSmax(sprite_scale.x(), sprite_scale.y()) * isin45 * 0.5F;
pt = UT_Vector3(-maxradius, -maxradius, 0)*xform; box.initBounds(pt);
pt = UT_Vector3(-maxradius, maxradius, 0)*xform; box.enlargeBounds(pt);
pt = UT_Vector3( maxradius, -maxradius, 0)*xform; box.enlargeBounds(pt);
pt = UT_Vector3( maxradius, maxradius, 0)*xform; box.enlargeBounds(pt);
box.translate(point->getPos());
vbox = box;
if (voff.isValid())
{
UT_Vector3 vel;
int i;
fpreal amount;
vel = point->getValue<UT_Vector3>(voff);
for (i = 0; i < 3; i++)
{
amount = vel(i) * tscale;
if (amount < 0)
vbox.vals[i][1] -= amount;
else vbox.vals[i][0] -= amount;
}
}
}
static inline void
clampBox(UT_BoundingBox &from, const UT_BoundingBox &to)
{
if (from.vals[0][0] < to.vals[0][0]) from.vals[0][0] = to.vals[0][0];
if (from.vals[1][0] < to.vals[1][0]) from.vals[1][0] = to.vals[1][0];
if (from.vals[2][0] < to.vals[2][0]) from.vals[2][0] = to.vals[2][0];
if (from.vals[0][1] > to.vals[0][1]) from.vals[0][1] = to.vals[0][1];
if (from.vals[1][1] > to.vals[1][1]) from.vals[1][1] = to.vals[1][1];
if (from.vals[2][1] > to.vals[2][1]) from.vals[2][1] = to.vals[2][1];
}
static inline int
testClampBox(const UT_BoundingBox &from, const UT_BoundingBox &to)
{
if (from.vals[0][0] < to.vals[0][0]) return 1;
if (from.vals[1][0] < to.vals[1][0]) return 1;
if (from.vals[2][0] < to.vals[2][0]) return 1;
if (from.vals[0][1] > to.vals[0][1]) return 1;
if (from.vals[1][1] > to.vals[1][1]) return 1;
if (from.vals[2][1] > to.vals[2][1]) return 1;
return 0;
}
int
VRAY_DemoSprite::initChild(VRAY_DemoSprite *sprite, const UT_BoundingBox &box)
{
int i, first, idx;
UT_BoundingBox tbox, tvbox;
const GEO_Point *ppt;
GU_Detail *gdp;
UT_Matrix4 xform;
myParms = sprite->myParms;
myParms->myRefCount++;
gdp = getPointGdp();
UT_Vector3 sprite_scale(0.1, 0.1, 0.1);
myPointList.resize(sprite->myPointList.entries());
for (i = sprite->myPointList.entries(); i-- > 0; )
{
idx = sprite->myPointList(i);
ppt = gdp->points()(idx);
if (box.isInside(ppt->getPos()))
{
myPointList.append(idx);
}
}
myPointList.resize(myPointList.entries());
first = 1;
xform = myParms->myXformInverse;
for (i = myPointList.entries(); i-- > 0; )
{
idx = myPointList(i);
ppt = gdp->points()(idx);
if (myParms->mySpriteScaleOff.isValid())
sprite_scale = ppt->getValue<UT_Vector3>(myParms->mySpriteScaleOff);
getRoughSpriteBox(tbox, tvbox, ppt, sprite_scale,
myParms->myVelOff, myParms->myTimeScale, xform);
if (first)
{
myBox = tbox;
myVelBox = tvbox;
first = 0;
}
else
{
myBox.enlargeBounds(tbox);
myVelBox.enlargeBounds(tvbox);
}
}
if (first)
return 0;
clampBox(myBox, box);
return 1;
}
VRAY_DemoSprite::~VRAY_DemoSprite()
{
myParms->myRefCount--;
if (!myParms->myRefCount)
{
destroyMap(myParms->myAttribMap);
delete myParms;
}
}
const char *
VRAY_DemoSprite::getClassName()
{
return "VRAY_DemoSprite";
}
int
VRAY_DemoSprite::initialize(const UT_BoundingBox *box)
{
void *handle;
const char *name;
GEO_Point *ppt;
int i, first;
UT_BoundingBox tbox, tvbox;
GU_Detail *gdp;
UT_Matrix4 xform;
UT_String str;
int vblur;
myParms = new VRAY_DemoSpriteParms;
myParms->myGdp = 0;
myParms->myVelOff.clear();
myParms->mySpriteScaleOff.clear();
myParms->mySpriteRotOff.clear();
myParms->mySpriteShopOff.clear();
myParms->mySpriteTexOff.clear();
myParms->myChunkSize = MIN_CHUNK * 2;
myParms->mySpriteLimit = SPRITE_LIMIT;
myParms->myRefCount = 1;
myParms->myAttribMap = 0;
name = 0;
if (import("object", str))
name = str.isstring() ? (const char *)str : 0;
handle = queryObject(name);
if (!handle)
{
VRAYerror("%s couldn't find object '%s'", getClassName(), name);
return 0;
}
name = queryObjectName(handle);
gdp = myParms->myGdp = (GU_Detail *)queryGeometry(handle, 0);
if (!gdp)
{
VRAYerror("%s object '%s' has no geometry", getClassName(), name);
return 0;
}
if (!import("object:velocityscale", &myParms->myTimeScale, 1))
myParms->myTimeScale = 0.5F / 24.0F;
vblur = 0;
import("object:velocityblur", &vblur, 0);
myParms->myXformInverse = queryTransform(handle, 0);
myParms->myXformInverse.invert();
myParms->mySpriteScaleOff = gdp->findPointAttrib("spritescale",
sizeof(float) * 3, GB_ATTRIB_FLOAT);
myParms->mySpriteRotOff = gdp->findPointAttrib("spriterot",
sizeof(float), GB_ATTRIB_FLOAT);
myParms->mySpriteShopOff = gdp->findPointAttrib("spriteshop",
sizeof(int), GB_ATTRIB_INDEX);
myParms->mySpriteTexOff = gdp->findPointAttrib("spriteuv",
sizeof(float) * 4, GB_ATTRIB_FLOAT);
if (vblur)
{
str = 0;
import("velocity", str);
if (str.isstring())
{
myParms->myVelOff = gdp->findPointAttrib(str, sizeof(UT_Vector3),
GB_ATTRIB_VECTOR);
if (myParms->myVelOff.isInvalid())
myParms->myVelOff = gdp->findPointAttrib(str, sizeof(UT_Vector3),
GB_ATTRIB_FLOAT);
if (myParms->myVelOff.isInvalid())
VRAYwarning("%s object (%s) couldn't find the '%s' attribute",
getClassName(), name, (const char *)str);
}
}
UT_Vector3 sprite_scale(0.1, 0.1, 0.1);
first = 1;
xform = myParms->myXformInverse;
for (i = gdp->points().entries(); i-- > 0; )
{
ppt = gdp->points()(i);
if (myParms->mySpriteScaleOff.isValid())
sprite_scale = ppt->getValue<UT_Vector3>(myParms->mySpriteScaleOff);
getRoughSpriteBox(tbox, tvbox, ppt, sprite_scale,
myParms->myVelOff, myParms->myTimeScale, xform);
myPointList.append(i);
if (first)
{
myBox = tbox;
myVelBox = tvbox;
first = 0;
}
else
{
myBox.enlargeBounds(tbox);
myVelBox.enlargeBounds(tvbox);
}
}
if (first)
{
VRAYwarning("%s found no points in %s", getClassName(), name);
return 0;
}
if (!myPointList.entries())
return 0;
str = 0;
import("attribute", str);
if (str.isstring())
setAttribMap(myParms->myAttribMap, gdp, str);
import("chunksize", &myParms->myChunkSize, 1);
if (myParms->myChunkSize < MIN_CHUNK)
myParms->myChunkSize = MIN_CHUNK;
import("maxsprites", &myParms->mySpriteLimit, 1);
if (myParms->mySpriteLimit < SPRITE_LIMIT)
myParms->mySpriteLimit = SPRITE_LIMIT;
if (box)
{
if (myParms->myVelOff.isValid())
{
if (testClampBox(myBox, *box) || testClampBox(myVelBox, *box))
VRAYwarning("%s[%s] cannot render a partial box %s",
getClassName(), name, "with motion blur");
}
else
{
clampBox(myBox, *box);
clampBox(myVelBox, *box);
}
}
return 1;
}
void
VRAY_DemoSprite::getBoundingBox(UT_BoundingBox &box)
{
box = myVelBox;
}
#define DEFAULT_SIZE 0.05F
#define SPRITE_SIZE(xsize, ysize, size) \
{ xsize = src_point->getValue<float>(parms.mySpriteScaleOff, 0) * 0.5F; \
ysize = src_point->getValue<float>(parms.mySpriteScaleOff, 1) * 0.5F; }
#define SPRITE_TEXTURE(u1, v1, u2, v2, txt) \
{ UT_Vector3 txt = src_point->getValue<UT_Vector3>(parms.mySpriteTexOff); \
u1 = txt.x(); v1 = txt.y(); u2 = txt.x()+txt.z(); v2 = txt.y()+txt.z(); }
#define ASSIGN_VERTEX(poly, ppt, i, XDELTA, YDELTA, u, v) \
ppt = poly->getVertex(i).getPt(); \
ppt->getPos() = src_point->getPos(); \
ppt->getPos().x() XDELTA; ppt->getPos().y() YDELTA; \
if (txt_off >= 0) \
ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(u, v, 0));
static void
applyMapToPrimitive(vray_SpriteAttribMap *map,
const UT_RefArray<GB_AttributeRef> &dest_offsets,
GEO_Primitive *dest, const GEO_Point *src_point)
{
float float_buffer[128];
float *abuf = NULL;
float *fdata;
int asize = 0;
while (map)
{
if (map->myFloats)
{
if (map->myFloats < 128)
fdata = float_buffer;
else
{
if (map->myFloats > asize)
{
delete [] abuf;
asize = map->myFloats + 1024;
abuf = new float[asize];
}
fdata = abuf;
}
src_point->get<float>(map->myOffset, fdata, map->myFloats);
dest->set<float>(dest_offsets(map->myDIndex), fdata, map->myFloats);
}
map = map->myNext;
}
delete [] abuf;
}
static int
makeSpritePoly(GU_Detail *gdp, GU_Detail *src, UT_IntArray &points,
const VRAY_DemoSpriteParms &parms, const char *srcpath)
{
int i;
GB_AttributeRef shop_off;
GB_AttributeRef txt_off;
fpreal xSize = DEFAULT_SIZE;
fpreal ySize = DEFAULT_SIZE;
fpreal uMin = 0.0F;
fpreal vMin = 0.0F;
fpreal uMax = 1.0F;
fpreal vMax = 1.0F;
GU_PrimPoly *poly;
const GEO_Point *src_point;
GEO_Point *ppt;
UT_Matrix4 xform;
UT_Matrix4 view_inverse;
UT_RefArray<GB_AttributeRef> dest_offsets;
static int num_polys = 0;
view_inverse = parms.myXformInverse;
if (points.entries())
{
if (parms.mySpriteShopOff.isValid())
{
static int minus1 = -1;
shop_off = gdp->addPrimAttrib("shop_vm_surface", sizeof(int),
GB_ATTRIB_INDEX, (void *)&minus1);
GB_Attribute *shop_attr =
gdp->primitiveAttribs().find("shop_vm_surface", sizeof(int),
GB_ATTRIB_INDEX);
const GB_Attribute *sprite_shop_attr
= src->pointAttribs().find("spriteshop", sizeof(int),
GB_ATTRIB_INDEX);
UT_ASSERT(shop_attr && sprite_shop_attr);
if (shop_attr && sprite_shop_attr)
{
UT_String path, fullpath;
int numidx;
numidx = sprite_shop_attr->getIndexSize();
for( i = 0; i < numidx; i++ )
{
path = sprite_shop_attr->getIndex(i);
if( path.isstring() && path(0) != '/' )
{
fullpath = srcpath;
fullpath += "/";
fullpath += path;
fullpath.collapseAbsolutePath();
}
else
fullpath = path;
shop_attr->addIndex(fullpath);
}
}
if (parms.mySpriteTexOff.isValid())
{
txt_off = gdp->addTextureAttribute(GEO_POINT_DICT);
}
}
if (parms.myAttribMap)
{
vray_SpriteAttribMap *map;
const GB_Attribute *atr;
dest_offsets.resize(parms.myAttribMap->myDIndex+1);
dest_offsets.entries(parms.myAttribMap->myDIndex+1);
for (map = parms.myAttribMap; map; map = map->myNext)
{
atr = map->myAttrib;
dest_offsets(map->myDIndex) = gdp->addPrimAttrib(atr);
UT_ASSERT(dest_offsets(map->myDIndex).isValid());
if (atr->getType() == GB_ATTRIB_INDEX)
gdp->primitiveAttribs().findClone(atr)->mergeIndex(atr);
}
}
}
for (i = 0; i < points.entries(); i++)
{
poly = GU_PrimPoly::build(gdp, 4, GU_POLY_CLOSED, 1);
src_point = src->points()(points(i));
if (parms.mySpriteScaleOff.isValid())
{
SPRITE_SIZE(xSize, ySize, ssize);
}
if (parms.mySpriteTexOff.isValid())
{
SPRITE_TEXTURE(uMin, vMin, uMax, vMax, txt);
}
xform.identity();
xform.translate(src_point->getPos().x(),
src_point->getPos().y(),
src_point->getPos().z());
xform.leftMult(view_inverse);
if (parms.mySpriteRotOff.isValid())
xform.prerotate(UT_Axis3::ZAXIS, UTdegToRad(
src_point->getValue<float>(parms.mySpriteRotOff)));
ppt = poly->getVertex(2).getPt();
ppt->getPos().x() += xSize; ppt->getPos().y() += ySize;
ppt->getPos() *= xform;
if (txt_off.isValid())
{
ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMax, vMax, 0));
}
ppt = poly->getVertex(3).getPt();
ppt->getPos().x() -= xSize; ppt->getPos().y() += ySize;
ppt->getPos() *= xform;
if (txt_off.isValid())
{
ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMin, vMax, 0));
}
ppt = poly->getVertex(0).getPt();
ppt->getPos().x() -= xSize; ppt->getPos().y() -= ySize;
ppt->getPos() *= xform;
if (txt_off.isValid())
{
ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMin, vMin, 0));
}
ppt = poly->getVertex(1).getPt();
ppt->getPos().x() += xSize; ppt->getPos().y() -= ySize;
ppt->getPos() *= xform;
if (txt_off.isValid())
{
ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMax, vMin, 0));
}
if (shop_off.isValid())
{
poly->setValue<int>(shop_off,
src_point->getValue<int>(parms.mySpriteShopOff));
}
if (parms.myAttribMap)
applyMapToPrimitive(parms.myAttribMap, dest_offsets,
(GEO_Primitive *)poly->castToGeo(), src_point);
}
num_polys += gdp->primitives().entries();
return gdp->primitives().entries();
}
static void
velocityMove(GU_Detail *gdp2, GU_Detail *gdp1, GU_Detail *src,
const UT_IntArray &points, const GB_AttributeRef &off,
fpreal scale)
{
int i;
GEO_Point *ppt;
const GEO_Point *spt;
UT_Vector3 vel;
gdp2->merge(*gdp1);
for (i = points.entries(); i-- > 0;)
{
spt = src->points()(points(i));
vel = spt->getValue<UT_Vector3>(off);
ppt = gdp2->points()(i*4);
ppt->getPos() += UT_Vector4(scale * vel.x(),
scale * vel.y(),
scale * vel.z(),
0);
ppt = gdp2->points()(i*4+1);
ppt->getPos() += UT_Vector4(scale * vel.x(),
scale * vel.y(),
scale * vel.z(),
0);
ppt = gdp2->points()(i*4+2);
ppt->getPos() += UT_Vector4(scale * vel.x(),
scale * vel.y(),
scale * vel.z(),
0);
ppt = gdp2->points()(i*4+3);
ppt->getPos() += UT_Vector4(scale * vel.x(),
scale * vel.y(),
scale * vel.z(),
0);
}
}
static inline int
computeDivs(fpreal inc, fpreal min)
{
int divs = (int)SYSceil(inc / min);
if (divs < 1) divs = 1;
else if (divs > 4) divs = 4;
return divs;
}
void
VRAY_DemoSprite::render()
{
GU_Detail *gdp, *vgdp;
fpreal max;
UT_BoundingBox kidbox;
VRAY_DemoSprite *kid;
int dogeo;
int nx, ny, nz;
int ix, iy, iz;
fpreal xinc, yinc, zinc, factor;
fpreal xv, yv, zv;
fpreal dfactor;
int sprite_limit = myParms->mySpriteLimit;
fpreal lod;
dogeo = 1;
lod = getLevelOfDetail(myBox);
if (lod > myParms->myChunkSize && myPointList.entries() > sprite_limit)
{
dogeo = 0;
xinc = myBox.sizeX();
yinc = myBox.sizeY();
zinc = myBox.sizeZ();
max = myBox.sizeMax();
dfactor = (xinc+yinc+zinc)/max;
factor = SYSpow((fpreal)myPointList.entries() / sprite_limit,
1.0F/dfactor);
if (factor > 4)
factor = 4;
max /= factor;
nx = ::computeDivs(xinc, max);
ny = ::computeDivs(yinc, max);
nz = ::computeDivs(zinc, max);
if (nx == 1 && ny == 1 && nz == 1)
{
if (xinc > yinc)
{
if (xinc > zinc) nx = 2;
else nz = 2;
}
else
{
if (yinc > zinc) ny = 2;
else nz = 2;
}
}
xinc /= (fpreal)nx;
yinc /= (fpreal)ny;
zinc /= (fpreal)nz;
for (iz = 0, zv = myBox.vals[2][0]; iz < nz; iz++, zv += zinc)
{
for (iy = 0, yv = myBox.vals[1][0]; iy < ny; iy++, yv += yinc)
{
for (ix = 0, xv = myBox.vals[0][0]; ix < nx; ix++, xv += xinc)
{
kidbox.initBounds(xv, yv, zv);
kidbox.enlargeBounds(xv+xinc, yv+yinc, zv+zinc);
kid = new VRAY_DemoSprite();
if (!kid->initChild(this, kidbox))
delete kid;
else
{
if (openProceduralObject())
{
addProcedural(kid);
closeObject();
}
else
{
dogeo = 1;
break;
}
}
}
if (dogeo) break;
}
if (dogeo) break;
}
}
if (dogeo)
{
if (lod < 3) lod = 3;
gdp = allocateGeometry();
if (!makeSpritePoly(gdp, getPointGdp(), myPointList, *myParms,
queryRootName()))
freeGeometry(gdp);
else
{
if (myParms->myVelOff.isValid())
{
vgdp = allocateGeometry();
velocityMove(vgdp, gdp, getPointGdp(), myPointList,
myParms->myVelOff, getTime());
}
else vgdp = 0;
openGeometryObject();
addGeometry(gdp, 0);
if (vgdp)
{
addGeometry(vgdp, 1);
}
closeObject();
}
}
}