Commit ea72e514 authored by David C. Lonie's avatar David C. Lonie

Add hidden line removal support.

parent 4bf1a140
Pipeline #22224 passed with stage
......@@ -42,6 +42,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestGlyph3DMapperOrientationArray.cxx
TestGlyph3DMapperPicking.cxx
TestGradientBackground.cxx
TestHiddenLineRemovalPass.cxx
TestHomogeneousTransformOfActor.cxx
TestInteractorStyleImageProperty.cxx,NO_VALID
TestInteractorTimers.cxx,NO_VALID
......
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCompositeDataGeometryFilter.h"
#include "vtkExodusIIReader.h"
#include "vtkNew.h"
#include "vtkOpenGLRenderer.h"
#include "vtkCompositePolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkTestUtilities.h"
int TestHiddenLineRemovalPass(int argc, char* argv[])
{
vtkNew<vtkRenderWindowInteractor> iren;
vtkNew<vtkRenderWindow> renWin;
renWin->SetMultiSamples(0);
iren->SetRenderWindow(renWin.Get());
vtkNew<vtkRenderer> renderer;
renderer->UseHiddenLineRemovalOn();
renWin->AddRenderer(renderer.Get());
const char* fileName =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/can.ex2");
vtkNew<vtkExodusIIReader> reader;
reader->SetFileName(fileName);
delete [] fileName;
vtkNew<vtkCompositeDataGeometryFilter> geomFilter;
geomFilter->SetInputConnection(reader->GetOutputPort());
vtkNew<vtkCompositePolyDataMapper> mapper;
mapper->SetInputConnection(geomFilter->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper.Get());
actor->GetProperty()->SetColor(1., 0., 0.);
actor->GetProperty()->SetRepresentationToWireframe();
renderer->AddActor(actor.Get());
// Workaround a rendering bug. See gitlab issue #16816.
actor->GetProperty()->LightingOff();
renWin->SetSize(500,500);
renderer->SetBackground(8., 7., 1.);
renderer->SetBackground2(.3, .1, .2);
renderer->GradientBackgroundOn();
renderer->GetActiveCamera()->ParallelProjectionOn();
renderer->GetActiveCamera()->SetPosition(-340., -70., -50.);
renderer->GetActiveCamera()->SetFocalPoint(-2.5, 3., -5.);
renderer->GetActiveCamera()->SetViewUp(0, 0.5, -1);
renderer->GetActiveCamera()->SetParallelScale(12);
renWin->Render();
int retVal = vtkRegressionTestImage(renWin.Get());
if (retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return !retVal;
}
......@@ -10,6 +10,7 @@ vtk_module(vtkRenderingCore
vtkFiltersGeometry
vtksys
TEST_DEPENDS
vtkIOExodus
vtkIOLegacy
vtkIOParallel
vtkIOXML
......
......@@ -119,6 +119,8 @@ vtkRenderer::vtkRenderer()
this->UseShadows = 0;
this->UseHiddenLineRemoval = 0;
this->UseDepthPeeling=0;
this->OcclusionRatio=0.0;
this->MaximumNumberOfPeels=4;
......@@ -378,6 +380,12 @@ void vtkRenderer::Render(void)
this->InvokeEvent(vtkCommand::EndEvent,NULL);
}
// ----------------------------------------------------------------------------
void vtkRenderer::DeviceRenderOpaqueGeometry()
{
this->UpdateOpaquePolygonalGeometry();
}
// ----------------------------------------------------------------------------
// Description:
// Render translucent polygonal geometry. Default implementation just call
......@@ -580,13 +588,8 @@ int vtkRenderer::UpdateGeometry()
// no time (culled) it would have been removed from
// the list
// loop through props and give them a chance to
// render themselves as opaque geometry
for ( i = 0; i < this->PropArrayCount; i++ )
{
this->NumberOfPropsRendered +=
this->PropArray[i]->RenderOpaqueGeometry(this);
}
// Opaque geometry first:
this->DeviceRenderOpaqueGeometry();
// do the render library specific stuff about translucent polygonal geometry.
// As it can be expensive, do a quick check if we can skip this step
......@@ -647,6 +650,18 @@ int vtkRenderer::UpdateTranslucentPolygonalGeometry()
return result;
}
// ----------------------------------------------------------------------------
int vtkRenderer::UpdateOpaquePolygonalGeometry()
{
int result = 0;
for (int i = 0; i < this->PropArrayCount; i++ )
{
result += this->PropArray[i]->RenderOpaqueGeometry(this);
}
this->NumberOfPropsRendered += result;
return result;
}
// ----------------------------------------------------------------------------
vtkWindow *vtkRenderer::GetVTKWindow()
{
......
......@@ -239,6 +239,13 @@ public:
// Create an image. Subclasses of vtkRenderer must implement this method.
virtual void DeviceRender() =0;
// Description:
// Render opaque polygonal geometry. Default implementation just calls
// UpdateOpaquePolygonalGeometry().
// Subclasses of vtkRenderer that can deal with, e.g. hidden line removal must
// override this method.
virtual void DeviceRenderOpaqueGeometry();
// Description:
// Render translucent polygonal geometry. Default implementation just call
// UpdateTranslucentPolygonalGeometry().
......@@ -528,6 +535,13 @@ public:
vtkGetMacro(UseShadows,int);
vtkBooleanMacro(UseShadows,int);
// Description:
// If this flag is true and the rendering engine supports it, wireframe
// geometry will be drawn using hidden line removal.
vtkSetMacro(UseHiddenLineRemoval, int)
vtkGetMacro(UseHiddenLineRemoval, int)
vtkBooleanMacro(UseHiddenLineRemoval, int)
// Set/Get a custom render pass.
// Initial value is NULL.
void SetPass(vtkRenderPass *p);
......@@ -649,6 +663,12 @@ protected:
// times with depth peeling technique.
virtual int UpdateTranslucentPolygonalGeometry();
// Description:
// Ask all props to update and draw any opaque polygonal
// geometry. This includes both vtkActors and vtkVolumes
// Return the number of rendered props.
virtual int UpdateOpaquePolygonalGeometry();
// Description:
// Ask the active camera to do whatever it needs to do prior to rendering.
// Creates a camera if none found active.
......@@ -676,6 +696,11 @@ protected:
// Initial value is off.
int UseShadows;
// Description:
// When this flag is on and the rendering engine supports it, wireframe
// polydata will be rendered using hidden line removal.
int UseHiddenLineRemoval;
// Description:
// If this flag is on and the GPU supports it, depth peeling is used
// for rendering translucent materials.
......
......@@ -35,6 +35,7 @@ set(Module_SRCS
vtkGLSLShaderDeviceAdapter2.cxx
vtkGaussianBlurPass.cxx
vtkGenericOpenGLRenderWindow.cxx
vtkHiddenLineRemovalPass.cxx
vtkHardwareSelectionPolyDataPainter.cxx # Needed by vtkPainterPolyDataMapper
vtkImageProcessingPass.cxx
vtkLightingHelper.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkHiddenLineRemovalPass.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkHiddenLineRemovalPass.h"
#include "vtkActor.h"
#include "vtkMapper.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGL.h"
#include "vtkOpenGLError.h"
#include "vtkProp.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderState.h"
#include <string>
// Define to print debug statements to the OpenGL CS stream (useful for e.g.
// apitrace debugging):
//#define ANNOTATE_STREAM
namespace
{
void annotate(const std::string &str)
{
#ifdef ANNOTATE_STREAM
vtkOpenGLStaticCheckErrorMacro("Error before glDebug.")
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER,
GL_DEBUG_SEVERITY_NOTIFICATION,
0, str.size(), str.c_str());
vtkOpenGLClearErrorMacro();
#else // ANNOTATE_STREAM
(void)str;
#endif // ANNOTATE_STREAM
}
}
vtkStandardNewMacro(vtkHiddenLineRemovalPass)
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::PrintSelf(std::ostream &os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::Render(const vtkRenderState *s)
{
this->NumberOfRenderedProps = 0;
// Separate the wireframe props from the others:
std::vector<vtkProp*> wireframeProps;
std::vector<vtkProp*> otherProps;
for (int i = 0; i < s->GetPropArrayCount(); ++i)
{
bool isWireframe = false;
vtkProp *prop = s->GetPropArray()[i];
vtkActor *actor = vtkActor::SafeDownCast(prop);
if (actor)
{
vtkProperty *property = actor->GetProperty();
if (property->GetRepresentation() == VTK_WIREFRAME)
{
isWireframe = true;
}
}
if (isWireframe)
{
wireframeProps.push_back(actor);
}
else
{
otherProps.push_back(actor);
}
}
vtkViewport *vp = s->GetRenderer();
// Render the non-wireframe geometry as normal:
annotate("Rendering non-wireframe props.");
this->NumberOfRenderedProps = this->RenderProps(otherProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after non-wireframe geometry.");
// Store the coincident topology parameters -- we want to force polygon
// offset to keep the drawn lines sharp:
int ctMode = vtkMapper::GetResolveCoincidentTopology();
double ctFactor, ctUnits;
vtkMapper::GetResolveCoincidentTopologyPolygonOffsetParameters(ctFactor,
ctUnits);
vtkMapper::SetResolveCoincidentTopology(VTK_RESOLVE_POLYGON_OFFSET);
vtkMapper::SetResolveCoincidentTopologyPolygonOffsetParameters(2.0, 2.0);
// Draw the wireframe props as surfaces into the depth buffer only:
annotate("Rendering wireframe prop surfaces.");
this->SetRepresentation(wireframeProps, VTK_SURFACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
this->RenderProps(wireframeProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after wireframe surface rendering.");
// Now draw the wireframes as normal:
annotate("Rendering wireframes.");
this->SetRepresentation(wireframeProps, VTK_WIREFRAME);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
this->NumberOfRenderedProps = this->RenderProps(wireframeProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after wireframe rendering.");
// Restore the previous coincident topology parameters:
vtkMapper::SetResolveCoincidentTopology(ctMode);
vtkMapper::SetResolveCoincidentTopologyPolygonOffsetParameters(ctFactor,
ctUnits);
}
//------------------------------------------------------------------------------
bool vtkHiddenLineRemovalPass::WireframePropsExist(vtkProp **propArray,
int nProps)
{
for (int i = 0; i < nProps; ++i)
{
vtkActor *actor = vtkActor::SafeDownCast(propArray[i]);
if (actor)
{
vtkProperty *property = actor->GetProperty();
if (property->GetRepresentation() == VTK_WIREFRAME)
{
return true;
}
}
}
return false;
}
//------------------------------------------------------------------------------
vtkHiddenLineRemovalPass::vtkHiddenLineRemovalPass()
{
}
//------------------------------------------------------------------------------
vtkHiddenLineRemovalPass::~vtkHiddenLineRemovalPass()
{
}
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::SetRepresentation(std::vector<vtkProp *> &props,
int repr)
{
for (std::vector<vtkProp*>::iterator it = props.begin(), itEnd = props.end();
it != itEnd; ++it)
{
vtkActor *actor = vtkActor::SafeDownCast(*it);
if (actor)
{
actor->GetProperty()->SetRepresentation(repr);
}
}
}
//------------------------------------------------------------------------------
int vtkHiddenLineRemovalPass::RenderProps(std::vector<vtkProp *> &props,
vtkViewport *vp)
{
int propsRendered = 0;
for (std::vector<vtkProp*>::iterator it = props.begin(), itEnd = props.end();
it != itEnd; ++it)
{
propsRendered += (*it)->RenderOpaqueGeometry(vp);
}
return propsRendered;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkHiddenLineRemovalPass.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkHiddenLineRemovalPass - RenderPass for HLR.
//
// .SECTION Description
// This render pass renders wireframe polydata such that only the front
// wireframe surface is drawn.
#ifndef vtkHiddenLineRemovalPass_h
#define vtkHiddenLineRemovalPass_h
#include "vtkRenderingOpenGLModule.h" // For export macro
#include "vtkRenderPass.h"
#include <vector> // For std::vector!
class vtkProp;
class vtkViewport;
class VTKRENDERINGOPENGL_EXPORT vtkHiddenLineRemovalPass : public vtkRenderPass
{
public:
static vtkHiddenLineRemovalPass* New();
vtkTypeMacro(vtkHiddenLineRemovalPass, vtkRenderPass)
virtual void PrintSelf(ostream &os, vtkIndent indent);
virtual void Render(const vtkRenderState *s);
// Description:
// Returns true if any of the nProps in propArray are rendered as wireframe.
static bool WireframePropsExist(vtkProp **propArray, int nProps);
protected:
vtkHiddenLineRemovalPass();
~vtkHiddenLineRemovalPass();
void SetRepresentation(std::vector<vtkProp*> &props, int repr);
int RenderProps(std::vector<vtkProp*> &props, vtkViewport *vp);
private:
vtkHiddenLineRemovalPass(const vtkHiddenLineRemovalPass&) VTK_DELETE_FUNCTION;
void operator=(const vtkHiddenLineRemovalPass&) VTK_DELETE_FUNCTION;
};
#endif // vtkHiddenLineRemovalPass_h
......@@ -15,7 +15,9 @@ PURPOSE. See the above copyright notice for more information.
#include "vtkOpenGLRenderer.h"
#include "vtkCuller.h"
#include "vtkHiddenLineRemovalPass.h"
#include "vtkLightCollection.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLCamera.h"
#include "vtkOpenGLLight.h"
......@@ -283,6 +285,29 @@ void vtkOpenGLRenderer::DeviceRender(void)
vtkTimerLog::MarkEndEvent("OpenGL Dev Render");
}
// ----------------------------------------------------------------------------
void vtkOpenGLRenderer::DeviceRenderOpaqueGeometry()
{
bool useHLR =
this->UseHiddenLineRemoval &&
vtkHiddenLineRemovalPass::WireframePropsExist(this->PropArray,
this->PropArrayCount);
if (useHLR)
{
vtkNew<vtkHiddenLineRemovalPass> hlrPass;
vtkRenderState s(this);
s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
s.SetFrameBuffer(0);
hlrPass->Render(&s);
this->NumberOfPropsRendered += hlrPass->GetNumberOfRenderedProps();
}
else
{
this->Superclass::DeviceRenderOpaqueGeometry();
}
}
// ----------------------------------------------------------------------------
// Description:
// Render translucent polygonal geometry. Default implementation just call
......
......@@ -38,6 +38,10 @@ public:
// Concrete open gl render method.
void DeviceRender(void);
// Description:
// Overridden to support hidden line removal.
virtual void DeviceRenderOpaqueGeometry();
// Description:
// Render translucent polygonal geometry. Default implementation just call
// UpdateTranslucentPolygonalGeometry().
......
......@@ -26,6 +26,7 @@ set(Module_SRCS
vtkGaussianBlurPass.cxx
vtkGenericCompositePolyDataMapper2.cxx
vtkGenericOpenGLRenderWindow.cxx
vtkHiddenLineRemovalPass.cxx
vtkImageProcessingPass.cxx
vtkLightingMapPass.cxx
vtkLightsPass.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkHiddenLineRemovalPass.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtk_glew.h"
#include "vtkHiddenLineRemovalPass.h"
#include "vtkActor.h"
#include "vtkMapper.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLError.h"
#include "vtkProp.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderState.h"
#include <string>
// Define to print debug statements to the OpenGL CS stream (useful for e.g.
// apitrace debugging):
//#define ANNOTATE_STREAM
namespace
{
void annotate(const std::string &str)
{
#ifdef ANNOTATE_STREAM
vtkOpenGLStaticCheckErrorMacro("Error before glDebug.")
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER,
GL_DEBUG_SEVERITY_NOTIFICATION,
0, str.size(), str.c_str());
vtkOpenGLClearErrorMacro();
#else // ANNOTATE_STREAM
(void)str;
#endif // ANNOTATE_STREAM
}
}
vtkStandardNewMacro(vtkHiddenLineRemovalPass)
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::PrintSelf(std::ostream &os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::Render(const vtkRenderState *s)
{
this->NumberOfRenderedProps = 0;
// Separate the wireframe props from the others:
std::vector<vtkProp*> wireframeProps;
std::vector<vtkProp*> otherProps;
for (int i = 0; i < s->GetPropArrayCount(); ++i)
{
bool isWireframe = false;
vtkProp *prop = s->GetPropArray()[i];
vtkActor *actor = vtkActor::SafeDownCast(prop);
if (actor)
{
vtkProperty *property = actor->GetProperty();
if (property->GetRepresentation() == VTK_WIREFRAME)
{
isWireframe = true;
}
}
if (isWireframe)
{
wireframeProps.push_back(actor);
}
else
{
otherProps.push_back(actor);
}
}
vtkViewport *vp = s->GetRenderer();
// Render the non-wireframe geometry as normal:
annotate("Rendering non-wireframe props.");
this->NumberOfRenderedProps = this->RenderProps(otherProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after non-wireframe geometry.");
// Store the coincident topology parameters -- we want to force polygon
// offset to keep the drawn lines sharp:
int ctMode = vtkMapper::GetResolveCoincidentTopology();
double ctFactor, ctUnits;
vtkMapper::GetResolveCoincidentTopologyPolygonOffsetParameters(ctFactor,
ctUnits);
vtkMapper::SetResolveCoincidentTopology(VTK_RESOLVE_POLYGON_OFFSET);
vtkMapper::SetResolveCoincidentTopologyPolygonOffsetParameters(2.0, 2.0);
// Draw the wireframe props as surfaces into the depth buffer only:
annotate("Rendering wireframe prop surfaces.");
this->SetRepresentation(wireframeProps, VTK_SURFACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
this->RenderProps(wireframeProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after wireframe surface rendering.");
// Now draw the wireframes as normal:
annotate("Rendering wireframes.");
this->SetRepresentation(wireframeProps, VTK_WIREFRAME);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
this->NumberOfRenderedProps = this->RenderProps(wireframeProps, vp);
vtkOpenGLStaticCheckErrorMacro("Error after wireframe rendering.");
// Restore the previous coincident topology parameters:
vtkMapper::SetResolveCoincidentTopology(ctMode);
vtkMapper::SetResolveCoincidentTopologyPolygonOffsetParameters(ctFactor,
ctUnits);
}
//------------------------------------------------------------------------------
bool vtkHiddenLineRemovalPass::WireframePropsExist(vtkProp **propArray,
int nProps)
{
for (int i = 0; i < nProps; ++i)
{
vtkActor *actor = vtkActor::SafeDownCast(propArray[i]);
if (actor)
{
vtkProperty *property = actor->GetProperty();
if (property->GetRepresentation() == VTK_WIREFRAME)
{
return true;
}
}
}
return false;
}
//------------------------------------------------------------------------------
vtkHiddenLineRemovalPass::vtkHiddenLineRemovalPass()
{
}
//------------------------------------------------------------------------------
vtkHiddenLineRemovalPass::~vtkHiddenLineRemovalPass()
{
}
//------------------------------------------------------------------------------
void vtkHiddenLineRemovalPass::SetRepresentation(std::vector<vtkProp *> &props,
int repr)
{
for (std::vector<vtkProp*>::iterator it = props.begin(), itEnd = props.end();
it != itEnd; ++it)
{
vtkActor *actor = vtkActor::SafeDownCast(*it);
if (actor)
{
actor->GetProperty()->SetRepresentation(repr);
}
}
}
//------------------------------------------------------------------------------
int vtkHiddenLineRemovalPass::RenderProps(std::vector<vtkProp *> &props,
vtkViewport *vp)
{
int propsRendered = 0;
for (std::vector<vtkProp*>::iterator it = props.begin(), itEnd = props.end();
it != itEnd; ++it)
{
propsRendered += (*it)->RenderOpaqueGeometry(vp);
}
return propsRendered;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkHiddenLineRemovalPass.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkHiddenLineRemovalPass - RenderPass for HLR.
//
// .SECTION Description
// This render pass renders wireframe polydata such that only the front
// wireframe surface is drawn.
#ifndef vtkHiddenLineRemovalPass_h
#define vtkHiddenLineRemovalPass_h
#include "vtkRenderingOpenGL2Module.h" // For export macro
#include "vtkOpenGLRenderPass.h"