Commit 1b005a29 authored by Simon Drouin's avatar Simon Drouin

Moved uniforms and GPU shaders to new class vtkShaderProperty

* vtkShaderProperty stored in vtkVolume to allow sharing between mappers
* Refactored vtkUniforms with more generic code
* Added getter functions to vtkUniforms for IO purposes
parent 51dc6ff3
......@@ -23,6 +23,7 @@
#include "vtkOpenGLSphereMapper.h"
#include "vtkNew.h"
#include "vtkProperty.h"
#include "vtkShaderProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
......@@ -87,10 +88,11 @@ int TestPDBBallAndStickShadowsDOFSSAA(int argc, char *argv[])
actor->GetProperty()->SetSpecular(0.4);
actor->GetProperty()->SetSpecularPower(40);
vtkShaderProperty * sp = actor->GetShaderProperty();
// we override the default shader very slightly so that
// the ambient color component is scaled off the diffuse
molmapper->GetFastAtomMapper()->AddShaderReplacement(
vtkShader::Fragment, // in the fragment shader
sp->AddFragmentShaderReplacement(
"//VTK::Color::Impl",
true, // before the standard replacements
"//VTK::Color::Impl\n" // we still want the default
......
......@@ -85,6 +85,7 @@ set(classes
vtkRendererSource
vtkSelectVisiblePoints
vtkShaderDeviceAdapter2
vtkShaderProperty
vtkSkybox
vtkTextActor
vtkTextActor3D
......@@ -93,6 +94,7 @@ set(classes
vtkTransformCoordinateSystems
vtkTransformInterpolator
vtkTupleInterpolator
vtkUniforms
vtkViewDependentErrorMetric
vtkViewport
vtkVisibilitySort
......
......@@ -21,6 +21,7 @@
#include "vtkInformationKey.h"
#include "vtkInformationIntegerKey.h"
#include "vtkInformationDoubleVectorKey.h"
#include "vtkShaderProperty.h"
#include <cassert>
vtkCxxSetObjectMacro(vtkProp,PropertyKeys,vtkInformation);
......@@ -49,6 +50,8 @@ vtkProp::vtkProp()
this->Consumers = nullptr;
this->PropertyKeys=nullptr;
this->ShaderProperty = nullptr;
}
//----------------------------------------------------------------------------
......@@ -65,6 +68,11 @@ vtkProp::~vtkProp()
{
this->PropertyKeys->Delete();
}
if (this->ShaderProperty)
{
this->ShaderProperty->UnRegister(this);
}
}
//----------------------------------------------------------------------------
......@@ -81,6 +89,7 @@ void vtkProp::ShallowCopy(vtkProp *prop)
this->Visibility = prop->GetVisibility();
this->Pickable = prop->GetPickable();
this->Dragable = prop->GetDragable();
this->SetShaderProperty(prop->GetShaderProperty());
}
//----------------------------------------------------------------------------
......@@ -353,3 +362,28 @@ bool vtkProp::RenderFilteredOverlay(vtkViewport *v,
}
return result;
}
void vtkProp::SetShaderProperty(vtkShaderProperty *property)
{
if( this->ShaderProperty != property )
{
if (this->ShaderProperty != nullptr) {this->ShaderProperty->UnRegister(this);}
this->ShaderProperty = property;
if (this->ShaderProperty != nullptr)
{
this->ShaderProperty->Register(this);
}
this->Modified();
}
}
vtkShaderProperty *vtkProp::GetShaderProperty()
{
if( this->ShaderProperty == nullptr )
{
this->ShaderProperty = vtkShaderProperty::New();
this->ShaderProperty->Register(this);
this->ShaderProperty->Delete();
}
return this->ShaderProperty;
}
......@@ -42,6 +42,7 @@ class vtkWindow;
class vtkInformation;
class vtkInformationIntegerKey;
class vtkInformationDoubleVectorKey;
class vtkShaderProperty;
class VTKRENDERINGCORE_EXPORT vtkProp : public vtkObject
{
......@@ -432,6 +433,14 @@ public:
int IsConsumer(vtkObject *c);
//@}
//@{
/**
* Set/Get the shader property.
*/
virtual void SetShaderProperty(vtkShaderProperty *property);
virtual vtkShaderProperty *GetShaderProperty();
//@}
protected:
vtkProp();
~vtkProp() override;
......@@ -456,6 +465,9 @@ protected:
vtkInformation *PropertyKeys;
// User-defined shader replacement and uniform variables
vtkShaderProperty *ShaderProperty;
private:
vtkProp(const vtkProp&) = delete;
void operator=(const vtkProp&) = delete;
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkShaderProperty.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 "vtkShaderProperty.h"
#include "vtkObjectFactory.h"
#include "vtkUniforms.h"
#include <algorithm>
vtkAbstractObjectFactoryNewMacro(vtkShaderProperty);
vtkShaderProperty::vtkShaderProperty()
{
this->VertexShaderCode = nullptr;
this->FragmentShaderCode = nullptr;
this->GeometryShaderCode = nullptr;
}
vtkShaderProperty::~vtkShaderProperty()
{
this->SetVertexShaderCode(nullptr);
this->SetFragmentShaderCode(nullptr);
this->SetGeometryShaderCode(nullptr);
}
void vtkShaderProperty::DeepCopy(vtkShaderProperty *p)
{
this->SetVertexShaderCode(p->GetVertexShaderCode());
this->SetFragmentShaderCode(p->GetFragmentShaderCode());
this->SetGeometryShaderCode(p->GetGeometryShaderCode());
}
vtkMTimeType vtkShaderProperty::GetShaderMTime()
{
vtkMTimeType fragUniformMTime = this->FragmentCustomUniforms->GetUniformListMTime();
vtkMTimeType vertUniformMTime = this->VertexCustomUniforms->GetUniformListMTime();
vtkMTimeType geomUniformMTime = this->GeometryCustomUniforms->GetUniformListMTime();
return std::max( { this->GetMTime(), fragUniformMTime, vertUniformMTime, geomUniformMTime } );
}
bool vtkShaderProperty::HasVertexShaderCode()
{
return this->VertexShaderCode && *this->VertexShaderCode;
}
bool vtkShaderProperty::HasFragmentShaderCode()
{
return this->FragmentShaderCode && *this->FragmentShaderCode;
}
bool vtkShaderProperty::HasGeometryShaderCode()
{
return this->GeometryShaderCode && *this->GeometryShaderCode;
}
//-----------------------------------------------------------------------------
void vtkShaderProperty::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkShaderProperty.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.
=========================================================================*/
/**
* @class vtkShaderProperty
* @brief represent GPU shader properties
*
* vtkShaderProperty is used to hold user-defined modifications of a
* GPU shader program used in a mapper.
*
* @sa
* vtkVolume vtkOpenGLUniform
*
* @par Thanks:
* Developed by Simon Drouin (sdrouin2@bwh.harvard.edu) at Brigham and Women's Hospital.
*
*/
#ifndef vtkShaderProperty_h
#define vtkShaderProperty_h
#include "vtkObject.h"
#include "vtkRenderingCoreModule.h" // For export macro
#include "vtkNew.h" // For iVars
class vtkUniforms;
class VTKRENDERINGCORE_EXPORT vtkShaderProperty : public vtkObject
{
public:
vtkTypeMacro(vtkShaderProperty,vtkObject);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Construct object with no shader replacements
*/
static vtkShaderProperty *New();
/**
* Assign one property to another.
*/
void DeepCopy(vtkShaderProperty *p);
/**
* @brief GetShaderMTime returns the last time a modification
* was made that affected the code of the shader (either code
* replacement was changed or one or more uniform variables were
* added or removed. This timestamp can be used by mappers to
* determine if the shader must be recompiled. Simply changing
* the value of an existing uniform variable doesn't affect this
* timestamp as it doesn't change the shader code.
* @return timestamp of the last modification
*/
vtkMTimeType GetShaderMTime();
//@{
/**
* Allow the program to set the shader codes used directly
* instead of using the built in templates. Be aware, if
* set, this template will be used for all cases,
* primitive types, picking etc.
*/
bool HasVertexShaderCode();
bool HasFragmentShaderCode();
bool HasGeometryShaderCode();
vtkSetStringMacro(VertexShaderCode);
vtkGetStringMacro(VertexShaderCode);
vtkSetStringMacro(FragmentShaderCode);
vtkGetStringMacro(FragmentShaderCode);
vtkSetStringMacro(GeometryShaderCode);
vtkGetStringMacro(GeometryShaderCode);
//@}
//@{
/**
* The Uniforms object allows to set custom uniform variables
* that are used in replacement shader code.
*/
vtkGetObjectMacro(FragmentCustomUniforms,vtkUniforms);
vtkGetObjectMacro(VertexCustomUniforms,vtkUniforms);
vtkGetObjectMacro(GeometryCustomUniforms,vtkUniforms);
//@}
//@{
/**
* This function enables you to apply your own substitutions
* to the shader creation process. The shader code in this class
* is created by applying a bunch of string replacements to a
* shader template. Using this function you can apply your
* own string replacements to add features you desire.
*/
virtual void AddVertexShaderReplacement(
const std::string& originalValue,
bool replaceFirst, // do this replacement before the default
const std::string& replacementValue,
bool replaceAll) = 0;
virtual void AddFragmentShaderReplacement(
const std::string& originalValue,
bool replaceFirst, // do this replacement before the default
const std::string& replacementValue,
bool replaceAll) = 0;
virtual void AddGeometryShaderReplacement(
const std::string& originalValue,
bool replaceFirst, // do this replacement before the default
const std::string& replacementValue,
bool replaceAll) = 0;
virtual int GetNumberOfShaderReplacements() = 0;
virtual std::string GetNthShaderReplacementTypeAsString( vtkIdType index ) = 0;
virtual void GetNthShaderReplacement(
vtkIdType index,
std::string & name,
bool & replaceFirst,
std::string & replacementValue,
bool & replaceAll ) = 0;
virtual void ClearVertexShaderReplacement(
const std::string& originalValue,
bool replaceFirst) = 0;
virtual void ClearFragmentShaderReplacement(
const std::string& originalValue,
bool replaceFirst) = 0;
virtual void ClearGeometryShaderReplacement(
const std::string& originalValue,
bool replaceFirst) = 0;
virtual void ClearAllVertexShaderReplacements() = 0;
virtual void ClearAllFragmentShaderReplacements() = 0;
virtual void ClearAllGeometryShaderReplacements() = 0;
virtual void ClearAllShaderReplacements() = 0;
//@}
protected:
vtkShaderProperty();
~vtkShaderProperty() override;
char *VertexShaderCode;
char *FragmentShaderCode;
char *GeometryShaderCode;
vtkNew<vtkUniforms> FragmentCustomUniforms;
vtkNew<vtkUniforms> VertexCustomUniforms;
vtkNew<vtkUniforms> GeometryCustomUniforms;
private:
vtkShaderProperty(const vtkShaderProperty&) = delete;
void operator=(const vtkShaderProperty&) = delete;
};
#endif
#include "vtkUniforms.h"
#include "vtkObjectFactory.h"
//----------------------------------------------------------------------------
// Return nullptr if no override is supplied.
vtkAbstractObjectFactoryNewMacro(vtkUniforms)
//-----------------------------------------------------------------------------
void vtkUniforms::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
std::string vtkUniforms::TupleTypeToString( TupleType tt )
{
std::string str;
switch (tt)
{
case vtkUniforms::TupleTypeScalar:
str = "TupleTypeScalar";
break;
case vtkUniforms::TupleTypeVector:
str = "TupleTypeVector";
break;
case vtkUniforms::TupleTypeMatrix:
str = "TupleTypeMatrix";
break;
default:
str = "TupleTypeInvalid";
break;
}
return str;
}
vtkUniforms::TupleType vtkUniforms::StringToTupleType( const std::string & s )
{
if( s == "TupleTypeScalar" )
{
return vtkUniforms::TupleTypeScalar;
}
else if( s == "TupleTypeVector" )
{
return vtkUniforms::TupleTypeVector;
}
else if( s == "TupleTypeMatrix" )
{
return vtkUniforms::TupleTypeMatrix;
}
return vtkUniforms::TupleTypeInvalid;
}
/* We only support int and float as internal data types for uniform variables */
std::string vtkUniforms::ScalarTypeToString( int scalarType )
{
if( scalarType == VTK_INT )
{
return "int";
}
else if ( scalarType == VTK_FLOAT )
{
return "float";
}
return "invalid";
}
int vtkUniforms::StringToScalarType( const std::string & s )
{
if( s == "int" )
{
return VTK_INT;
}
else if( s == "float" )
{
return VTK_FLOAT;
}
else
{
return VTK_VOID;
}
}
/*=========================================================================
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.
=========================================================================*/
/**
* @class vtkUniforms
* @brief helper class to set custom uniform variables in GPU shaders.
*
* This class implements a generic mechanism to declare and set the value of custom uniform
* variables to be used in GPU shader programs used by mappers. It allows users who specify
* custom shader code for mappers to change the value of the variable they define without
* triggering a costly rebuild of the shader. This class is used mostly as an interface and
* the implementation is found in graphics api specific derived classes (e.g.: vtkOpenGLUniforms).
*
* @sa
* vtkOpenGLUniforms vtkShaderProperty
*
* @par Thanks:
* Developed by Simon Drouin (sdrouin2@bwh.harvard.edu) at Brigham and Women's Hospital.
*/
#ifndef vtkUniforms_h
#define vtkUniforms_h
#include "vtkObject.h"
#include "vtkRenderingCoreModule.h" // For export macro
#include <vector> // member function parameters
class vtkMatrix3x3;
class vtkMatrix4x4;
class VTKRENDERINGCORE_EXPORT vtkUniforms : public vtkObject
{
public:
static vtkUniforms *New();
vtkTypeMacro(vtkUniforms, vtkObject);
void PrintSelf(ostream& os, vtkIndent indent) override;
virtual vtkMTimeType GetUniformListMTime() = 0;
//@{
/**
* Types of tuples that can be stored : scalar, vector, matrix
*/
//@}
enum TupleType
{
TupleTypeInvalid = 0,
TupleTypeScalar,
TupleTypeVector,
TupleTypeMatrix,
NumberOfTupleTypes
};
/** Convert between TupleType and string */
static std::string TupleTypeToString( TupleType tt );
static TupleType StringToTupleType( const std::string & s );
/** Convert between scalar types an string */
static std::string ScalarTypeToString( int scalaType );
static int StringToScalarType( const std::string & s );
/** Remove uniform variable named @p name */
virtual void RemoveUniform(const char *name) = 0;
/** Remove all uniform variables */
virtual void RemoveAllUniforms() = 0;
/** Generic setters and getter. Set and Get the value of
* uniform variable @p name, with TupleType @p tt, number
* of components @p nbComponents and values stored in
* @p value. These functions simplify io of uniforms */
virtual void SetUniform( const char * name, vtkUniforms::TupleType tt, int nbComponents, const std::vector<int> & value ) = 0;
virtual void SetUniform( const char * name, vtkUniforms::TupleType tt, int nbComponents, const std::vector<float> & value ) = 0;
virtual bool GetUniform( const char * name, std::vector<int> & value ) = 0;
virtual bool GetUniform( const char * name, std::vector<float> & value ) = 0;
/** Set the @p name uniform value to @p v. */
virtual void SetUniformi(const char *name, int v) = 0;
virtual void SetUniformf(const char *name, float v) = 0;
virtual void SetUniform2i(const char *name, const int v[2]) = 0;
virtual void SetUniform2f(const char *name, const float v[2]) = 0;
virtual void SetUniform3f(const char *name, const float v[3]) = 0;
virtual void SetUniform4f(const char *name, const float v[4]) = 0;
virtual void SetUniformMatrix3x3(const char *name, float *v) = 0;
virtual void SetUniformMatrix4x4(const char *name, float *v) = 0;
/** Set the @p name uniform array to @p f with @p count elements */
virtual void SetUniform1iv(const char *name, const int count, const int *f) = 0;
virtual void SetUniform1fv(const char *name, const int count, const float *f) = 0;
virtual void SetUniform2fv(const char *name, const int count, const float (*f)[2]) = 0;
virtual void SetUniform3fv(const char *name, const int count, const float (*f)[3]) = 0;
virtual void SetUniform4fv(const char *name, const int count, const float (*f)[4]) = 0;
virtual void SetUniformMatrix4x4v(const char *name, const int count, float *v) = 0;
/** Set the @p name uniform to @p v.
* The following are convenience functions and do not reflect
* the way the data is stored and sent to OpenGL. Data is
* converted to match one of the basic supported types */
virtual void SetUniform3f(const char *name, const double v[3]) = 0;
virtual void SetUniform3uc(const char *name, const unsigned char v[3]) = 0; // maybe remove
virtual void SetUniform4uc(const char *name, const unsigned char v[4]) = 0; // maybe remove
virtual void SetUniformMatrix(const char *name, vtkMatrix3x3 *v) = 0;
virtual void SetUniformMatrix(const char *name, vtkMatrix4x4 *v) = 0;
/** Get the @p name uniform value. Returns true on success. */
virtual bool GetUniformi(const char *name, int& v) = 0;
virtual bool GetUniformf(const char *name, float& v) = 0;
virtual bool GetUniform2i(const char *name, int v[2]) = 0;
virtual bool GetUniform2f(const char *name, float v[2]) = 0;
virtual bool GetUniform3f(const char *name, float v[3]) = 0;
virtual bool GetUniform4f(const char *name, float v[4]) = 0;
virtual bool GetUniformMatrix3x3(const char *name, float *v) = 0;
virtual bool GetUniformMatrix4x4(const char *name, float *v) = 0;
/** Get the @p name uniform to @p v.
* The following are convenience functions and do not reflect
* the way the data is stored and sent to OpenGL. Data is
* converted from one of the basic supported types */
virtual bool GetUniform3f(const char *name, double v[3]) = 0;
virtual bool GetUniform3uc(const char *name, unsigned char v[3]) = 0;
virtual bool GetUniform4uc(const char *name, unsigned char v[4]) = 0;
virtual bool GetUniformMatrix(const char *name, vtkMatrix3x3 *v) = 0;
virtual bool GetUniformMatrix(const char *name, vtkMatrix4x4 *v) = 0;
/** Get the @p name uniform vector to @p f with.
Buffer must be pre-allocated based on vector size provided by
*/
virtual bool GetUniform1iv(const char *name, std::vector<int>& f) = 0;
virtual bool GetUniform1fv(const char *name, std::vector<float>& f) = 0;
virtual bool GetUniform2fv(const char *name, std::vector<float>& f) = 0;
virtual bool GetUniform3fv(const char *name, std::vector<float>& f) = 0;
virtual bool GetUniform4fv(const char *name, std::vector<float>& f) = 0;
virtual bool GetUniformMatrix4x4v(const char *name, std::vector<float>& f) = 0;
/** Get number of all uniforms stored in this class */
virtual int GetNumberOfUniforms() = 0;
/** Get number of all uniforms stored in this class.
Valid range is between 0 and GetNumberOfUniforms() - 1.*/
virtual const char* GetNthUniformName(vtkIdType uniformIndex) = 0;
/** Get type of scalars stored in uniform @p name */
virtual int GetUniformScalarType(const char *name) = 0;
/** Get the tuple type stored in uniform @p name. This can be a scalar,
* a vector of a matrix. */
virtual TupleType GetUniformTupleType(const char *name) = 0;
/** Get the number of components stored in each tuple of uniform @p name.
* for example, a uniform with tuples of matrix type and 9 components
* contains 3x3 matrices */
virtual int GetUniformNumberOfComponents(const char *name) = 0;
/** Number of tuples of uniform @p name that contains a variable-size vector.
* For example, for 3 components uniforms of type vector, this is the number
* of triplets. */
virtual int GetUniformNumberOfTuples(const char *name) = 0;
protected:
vtkUniforms() {}
~vtkUniforms() override {}
private:
vtkUniforms(const vtkUniforms&) = delete;
void operator=(const vtkUniforms&) = delete;
};
#endif
......@@ -54,6 +54,7 @@ set(classes
vtkOpenGLRenderWindow
vtkOpenGLRenderer
vtkOpenGLShaderCache
vtkOpenGLShaderProperty
vtkOpenGLSkybox
vtkOpenGLSphereMapper
vtkOpenGLState
......@@ -62,6 +63,7 @@ set(classes
vtkOpenGLTextActor3D
vtkOpenGLTextMapper
vtkOpenGLTexture
vtkOpenGLUniforms
vtkOpenGLVertexArrayObject
vtkOpenGLVertexBufferObject
vtkOpenGLVertexBufferObjectCache
......@@ -172,6 +174,8 @@ set(opengl_overrides
PolyDataMapper
PolyDataMapper2D
Property
ShaderProperty
Uniforms
Renderer
RenderTimerLog
Skybox
......
vtk_add_test_cxx(vtkRenderingOpenGL2CxxTests tests
TestBlurAndSobelPasses.cxx
TestCoincident.cxx
TestCompositePolyDataMapper2CustomShader.cxx,NO_DATA
TestCompositePolyDataMapper2NaNPartial.cxx,NO_DATA
TestCompositePolyDataMapper2Pickability.cxx,NO_DATA
TestCompositePolyDataMapper2SharedArray.cxx,NO_DATA
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestCompositePolyDataMapper2NaNPartial.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 "vtkCompositePolyDataMapper2.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkFloatArray.h"
#include "vtkLookupTable.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkNew.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkSphereSource.h"
#include "vtkTrivialProducer.h"
#include "vtkShaderProperty.h"
#include <vtkTestUtilities.h>
#include <vtkRegressionTestImage.h>
void FillShaderProperty( vtkActor * actor )
{
// Modify the shader to color based on model normal
// To do this we have to modify the vertex shader
// to pass the normal in model coordinates
// through to the fragment shader. By default the normal
// is converted to View coordinates and then passed on.
// We keep that, but add a varying for the original normal.
// Then we modify the fragment shader to set the diffuse color
// based on that normal. First lets modify the vertex
// shader
vtkShaderProperty * sp = actor->GetShaderProperty();
sp->AddVertexShaderReplacement(
"//VTK::Normal::Dec", // replace the normal block
true, // before the standard replacements
"//VTK::Normal::Dec\n" // we still want the default
" out vec3 myNormalMCVSOutput;\n", //but we add this
false // only do it once
);
sp->AddVertexShaderReplacement(
"//VTK::Normal::Impl", // replace the normal block