HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
tetprim/GR_PrimTetra.C
/*
* Copyright (c) 2024
* Side Effects Software Inc. All rights reserved.
*
* Redistribution and use of Houdini Development Kit samples 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. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `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 SIDE EFFECTS SOFTWARE 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.
*/
#include "GR_PrimTetra.h"
#include <GR/GR_Light.h>
#include "GEO_PrimTetra.h"
#include <GR/GR_Utils.h>
#include <RE/RE_Geometry.h>
using namespace HDK_Sample;
// See GEO_PrimTetra::registerMyself() for installation of the hook.
: GUI_PrimitiveHook("Tetrahedron")
{
}
GR_PrimTetraHook::~GR_PrimTetraHook()
{
}
const GEO_Primitive *geo_prim,
const GR_RenderInfo *info,
const char *cache_name,
GR_PrimAcceptResult &processed)
{
return new GR_PrimTetra(info, cache_name, geo_prim);
}
const char *cache_name,
const GEO_Primitive *prim)
: GR_Primitive(info, cache_name, GA_PrimCompat::TypeMask(0))
{
myID = prim->getTypeId().get();
myGeo = NULL;
}
GR_PrimTetra::~GR_PrimTetra()
{
delete myGeo;
}
int geo_type,
const GT_PrimitiveHandle &ph,
const GEO_Primitive *prim)
{
if(geo_type == myID)
return GR_PROCESSED;
}
void
const GT_PrimitiveHandle &primh,
const GR_UpdateParms &p)
{
if(r.isVulkan())
return;
// Fetch the GEO primitive from the GT primitive handle
const GEO_PrimTetra *tet = NULL;
// GL3 and above requires named vertex attributes, while GL2 and GL1 use
// the older builtin names.
const char *posname = "P";
const char *nmlname = "N";
RE_VertexArray *pos = NULL;
RE_VertexArray *nml = NULL;
// Initialize the geometry with the proper name for the GL cache
if(!myGeo)
myGeo = new RE_Geometry;
getGEOPrimFromGT<GEO_PrimTetra>(primh, tet);
const int num_tets = tet ? 1 : 0;
if(num_tets == 0)
{
delete myGeo;
myGeo = NULL;
return;
}
// Extract tetra point positions.
{
const GU_Detail *dtl = georl.getGdp();
for(int v=0; v<4; v++)
pt.append(dtl->getPos3(dtl->vertexPoint(tet->getVertexOffset(v))));
}
// Initialize the number of points in the geometry.
myGeo->setNumPoints( 12 * num_tets);
const GR_Decoration pdecs[] = { GR_POINT_MARKER,
dp, pdecs, GR_POINT_ATTRIB);
// Fetch P (point position). If its cache version matches, no upload is
// required.
pos = myGeo->findCachedAttrib(r, posname, RE_GPU_FLOAT32, 3,
if(pos->getCacheVersion() != dp.geo_version)
{
// map() returns a pointer to the GL buffer
UT_Vector3F *pdata = static_cast<UT_Vector3F *>(pos->map(r));
if(pdata)
{
for(int t=0; t<num_tets; t++)
{
pdata[0] = pt(t*4);
pdata[1] = pt(t*4+1);
pdata[2] = pt(t*4+2);
pdata[3] = pt(t*4);
pdata[4] = pt(t*4+2);
pdata[5] = pt(t*4+3);
pdata[6] = pt(t*4+1);
pdata[7] = pt(t*4+2);
pdata[8] = pt(t*4+3);
pdata[9] = pt(t*4);
pdata[10] = pt(t*4+3);
pdata[11] = pt(t*4+1);
pdata += 12;
}
// unmap the buffer so it can be used by GL
pos->unmap(r);
// Always set the cache version after assigning data.
pos->setCacheVersion(dp.geo_version);
}
}
// NOTE: you can add more attributes from a detail, such as Cd, uv, Alpha
// by repeating the process above.
// Fetch N (point normal). This just generates normals for the tetras.
nml = myGeo->findCachedAttrib(r, nmlname, RE_GPU_FLOAT32, 3,
if(nml->getCacheVersion() != dp.geo_version)
{
UT_Vector3F *ndata = static_cast<UT_Vector3F *>(nml->map(r));
if(ndata)
{
UT_Vector3F n0, n1, n2, n3;
// This just creates primitive normals for the tet. It's currently
// not 100% correct (FIXME).
for(int t=0; t<num_tets; t++)
{
n0 = -cross(pt(t*4+2)-pt(t*4), pt(t*4+1)-pt(t*4)).normalize();
n1 = cross(pt(t*4+3)-pt(t*4), pt(t*4+2)-pt(t*4)).normalize();
n2 = -cross(pt(t*4+3)-pt(t*4+1),pt(t*4+2)-pt(t*4+1)).normalize();
n3 = -cross(pt(t*4+1)-pt(t*4), pt(t*4+3)-pt(t*4)).normalize();
ndata[0] = ndata[1] = ndata[2] = n0;
ndata[3] = ndata[4] = ndata[5] = n1;
ndata[6] = ndata[7] = ndata[8] = n2;
ndata[9] = ndata[10] = ndata[11] = n3;
ndata += 12;
}
nml->unmap(r);
// Always set the cache version after assigning data.
nml->setCacheVersion(dp.geo_version);
}
}
// Extra constant inputs for the GL3 default shaders we are using.
// This isn't required unless
fpreal32 col[3] = { 1.0, 1.0, 1.0 };
fpreal32 uv[2] = { 0.0, 0.0 };
fpreal32 alpha = 1.0;
fpreal32 pnt = 0.0;
myGeo->createConstAttribute(r, "Cd", RE_GPU_FLOAT32, 3, col);
myGeo->createConstAttribute(r, "uv", RE_GPU_FLOAT32, 2, uv);
myGeo->createConstAttribute(r, "Alpha", RE_GPU_FLOAT32, 1, &alpha);
myGeo->createConstAttribute(r, "pointSelection", RE_GPU_FLOAT32, 1,&pnt);
myInstancedFlag =
GR_Utils::buildInstanceIndex(r, myGeo, false, dummy, 0, 0);
// Connectivity, for both shaded and wireframe
}
// GL3 shaders. These use some of the builtin Houdini shaders, which are
// described by the .prog file format - a simple container format for various
// shader stages and other information.
static RE_ShaderHandle theNQShader("material/GL32/beauty_lit.prog");
static RE_ShaderHandle theNQFlatShader("material/GL32/beauty_flat_lit.prog");
static RE_ShaderHandle theNQUnlitShader("material/GL32/beauty_unlit.prog");
static RE_ShaderHandle theHQShader("material/GL32/beauty_material.prog");
static RE_ShaderHandle theLineShader("basic/GL32/wire_color.prog");
static RE_ShaderHandle theConstShader("material/GL32/constant.prog");
static RE_ShaderHandle theZCubeShader("basic/GL32/depth_cube.prog");
static RE_ShaderHandle theZLinearShader("basic/GL32/depth_linear.prog");
static RE_ShaderHandle theMatteShader("basic/GL32/matte.prog");
void
GR_RenderMode render_mode,
{
if(!myGeo)
return;
bool need_wire = (render_mode == GR_RENDER_WIREFRAME ||
render_mode == GR_RENDER_HIDDEN_LINE ||
render_mode == GR_RENDER_GHOST_LINE ||
render_mode == GR_RENDER_MATERIAL_WIREFRAME ||
// Shaded mode rendering
if(render_mode == GR_RENDER_BEAUTY ||
render_mode == GR_RENDER_MATERIAL ||
render_mode == GR_RENDER_CONSTANT ||
render_mode == GR_RENDER_HIDDEN_LINE ||
render_mode == GR_RENDER_GHOST_LINE ||
render_mode == GR_RENDER_DEPTH ||
render_mode == GR_RENDER_DEPTH_LINEAR ||
render_mode == GR_RENDER_DEPTH_CUBE ||
render_mode == GR_RENDER_MATTE)
{
// enable polygon offset if doing a wireframe on top of shaded
bool polyoff = r->isPolygonOffset();
if(need_wire)
r->polygonOffset(true);
r->pushShader();
// GL3 requires the use of shaders. The fixed function pipeline
// GL builtins (which are deprecated, like gl_ModelViewMatrix)
// are not initialized in the GL3 renderer.
if(render_mode == GR_RENDER_BEAUTY)
{
r->bindShader(theNQUnlitShader);
else if(flags & GR_RENDER_FLAG_FLAT_SHADED)
r->bindShader(theNQFlatShader);
else
r->bindShader(theNQShader);
}
else if(render_mode == GR_RENDER_MATERIAL)
{
r->bindShader(theHQShader);
}
else if(render_mode == GR_RENDER_CONSTANT ||
render_mode == GR_RENDER_DEPTH ||
render_mode == GR_RENDER_HIDDEN_LINE ||
render_mode == GR_RENDER_GHOST_LINE)
{
// Reuse constant for depth-only since it's so lightweight.
r->bindShader(theConstShader);
}
else if(render_mode == GR_RENDER_DEPTH_LINEAR)
{
// Depth written to world-space Z instead of non-linear depth
// buffer Z ([0..1] near-far depth range)
r->bindShader(theZLinearShader);
}
else if(render_mode == GR_RENDER_DEPTH_CUBE)
{
// Linear depth written to
r->bindShader(theZCubeShader);
}
else if(render_mode == GR_RENDER_MATTE)
{
r->bindShader(theMatteShader);
}
// setup materials and lighting
{
if(!mat)
// Set up lighting for any GL3 lighting blocks
if(shader && dp.opts->getLightList())
dp.opts->getLightList()->glLights()->bindForShader(r, shader);
// set up the main material block for GL3
mat->updateShaderForMaterial(r, 0, true,
}
// Draw call for the geometry
else
myGeo->draw(r, RE_GEO_SHADED_IDX);
if(r->getShader())
r->popShader();
if(need_wire && !polyoff)
r->polygonOffset(polyoff);
}
// Wireframe rendering
if(need_wire)
{
// GL3 requires a shader even for simple wireframe rendering.
r->pushShader(theLineShader);
else
myGeo->draw(r, RE_GEO_WIRE_IDX);
r->popShader();
}
}
void
{
drawDecorationForGeo(r, myGeo, decor, p.opts, p.render_flags,
}
int
const GR_DisplayOption *opt,
unsigned int pick_type,
GR_PickStyle pick_style,
bool has_pick_map)
{
// This example is not pickable.
return 0;
}