Skip to content

Commit 610dec0

Browse files
committed
Merge pull request #453 from johnhaddon/wireframeWithVertexShaders
IECoreGL : Fixes relating to custom vertex shaders
2 parents eb40735 + 901a62e commit 610dec0

File tree

9 files changed

+375
-145
lines changed

9 files changed

+375
-145
lines changed

include/IECoreGL/Primitive.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ class IECOREGL_API Primitive : public Renderable
9696
/// Most classes will not need to override this method - reasons for overriding would be
9797
/// to substitute in custom geometry or vertex shaders and/or to bind in attributes
9898
/// not already specified with addUniformAttribute() or addVertexAttribute().
99+
///
100+
/// \todo We need to rethink this mechanism. The problem is that we've ended up using
101+
/// this method for two things - firstly to get a ShaderSetup where all the primitive
102+
/// variables are bound (good), and secondly we've abused it to actually change the
103+
/// shader in PointsPrimitive and CurvePrimitive. Asking for a setup for one shader and getting
104+
/// back a setup for another doesn't make a great deal of sense. There are several
105+
/// competing sources of source code for shaders :
106+
///
107+
/// - The user-provided source coming through Renderer::shader().
108+
/// - The vertex and geometry shaders that PointsPrimitive and CurvesPrimitive need
109+
/// to insert.
110+
/// - The constant fragment shader that Primitive needs to insert to do wireframe
111+
/// shading etc.
112+
/// - The ID fragment shader needed for the Selector.
113+
///
114+
/// We should redesign our API so that we first resolve these requirements to generate
115+
/// a shader, and then use shaderSetup() just to apply primitive variables to it.
99116
virtual const Shader::Setup *shaderSetup( const Shader *shader, State *state ) const;
100117
/// Adds the primitive variables held by this Primitive to the specified Shader::Setup.
101118
/// Vertex attributes will be prefixed as specified, and for each vertex attribute

include/IECoreGL/Selector.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,18 @@ class IECOREGL_API Selector : boost::noncopyable
119119

120120
/// The IDRender mode requires a shader which takes a name
121121
/// via a "uniform uint ieCoreGLName" parameter and outputs it
122-
/// via an "out uint ieCoreGLNameOut" fragment output.
123-
/// Typically one is set up automatically in baseState(), but
124-
/// if rendering must be performed with an alternative shader
125-
/// then it may be passed via this function.
122+
/// via an "out uint ieCoreGLNameOut" fragment output. The
123+
/// defaultIDShader() meets these criteria and will be bound
124+
/// automatically when the selector constructs in IDRender mode.
125+
/// Note however, that if any other shaders are subsequently
126+
/// bound while the selector is active, they too must meet the
127+
/// same critera.
126128
void pushIDShader( const IECoreGL::Shader *idShader );
127-
128-
/// Revert to previous ID shader:
129+
/// Reverts to the previous ID shader.
129130
void popIDShader();
131+
132+
/// A shader suitable for use in IDRender mode.
133+
static const Shader *defaultIDShader();
130134

131135
/// Returns the currently active Selector - this may be used
132136
/// in drawing code to retrieve a selector to call loadName()

include/IECoreGL/ShaderStateComponent.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ class IECOREGL_API ShaderStateComponent : public StateComponent
7676
ShaderLoader *shaderLoader();
7777
TextureLoader *textureLoader();
7878

79+
/// Returns a hash to uniquely identify this shader state.
80+
IECore::MurmurHash hash() const;
81+
7982
/// Returns a Shader::Setup object for binding the shader. This function can
8083
/// only be called from a thread with a valid GL context.
8184
Shader::Setup *shaderSetup();

src/IECoreGL/Primitive.cpp

Lines changed: 104 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "IECoreGL/Exception.h"
4545
#include "IECoreGL/TypedStateComponent.h"
4646
#include "IECoreGL/ShaderStateComponent.h"
47+
#include "IECoreGL/ShaderLoader.h"
4748
#include "IECoreGL/Shader.h"
4849
#include "IECoreGL/NumericTraits.h"
4950
#include "IECoreGL/UniformFunctions.h"
@@ -74,66 +75,60 @@ IECOREGL_TYPEDSTATECOMPONENT_SPECIALISEANDINSTANTIATE( Primitive::PointWidth, Pr
7475
IECOREGL_TYPEDSTATECOMPONENT_SPECIALISEANDINSTANTIATE( Primitive::Selectable, PrimitiveSelectableTypeId, bool, true );
7576
IECOREGL_TYPEDSTATECOMPONENT_SPECIALISEANDINSTANTIATE( Primitive::TransparencySort, PrimitiveTransparencySortStateComponentTypeId, bool, true );
7677

77-
}
78+
} // namespace IECoreGL
7879

7980
//////////////////////////////////////////////////////////////////////////
80-
// Primitive implementation
81+
// Utilities
8182
//////////////////////////////////////////////////////////////////////////
8283

83-
IE_CORE_DEFINERUNTIMETYPED( Primitive );
84-
85-
Primitive::Primitive()
84+
namespace
8685
{
87-
}
8886

89-
Primitive::~Primitive()
87+
struct FlatConstant
9088
{
91-
}
9289

93-
void Primitive::addPrimitiveVariable( const std::string &name, const IECore::PrimitiveVariable &primVar )
94-
{
95-
if ( primVar.interpolation == IECore::PrimitiveVariable::Constant )
90+
FlatConstant( const IECore::MurmurHash &hash, const Shader::ConstSetupPtr &shaderSetup = Shader::ConstSetupPtr() )
91+
: hash( hash ), shaderSetup( shaderSetup )
9692
{
97-
addUniformAttribute( name, primVar.data );
9893
}
99-
else
94+
95+
IECore::MurmurHash hash;
96+
Shader::ConstSetupPtr shaderSetup;
97+
98+
bool operator < ( const FlatConstant &rhs )
10099
{
101-
addVertexAttribute( name, primVar.data );
100+
return hash < rhs.hash;
102101
}
103-
}
104102

105-
static Shader *constant2()
103+
};
104+
105+
// Makes a constant shader by taking the vertex and geometry
106+
// shader from the state and combining them with a flat fragment
107+
// shader.
108+
const Shader::Setup *flatConstantShaderSetup( State *state, bool forIDRender )
106109
{
110+
ShaderStateComponent *shaderStateComponent = state->get<ShaderStateComponent>();
107111

108-
static const char *vertexSource =
109-
110-
"#if __VERSION__ <= 120\n"
111-
"#define in attribute\n"
112-
"#define out varying\n"
113-
"#endif\n"
114-
""
115-
"in vec3 vertexP;"
116-
""
117-
"void main()"
118-
"{"
119-
" gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4( vertexP, 1 );"
120-
"}";
121-
122-
static const char *fragmentSource =
123-
124-
"void main()"
125-
"{"
126-
"gl_FragColor = vec4( 1, 1, 1, 1 );"
127-
"}";
112+
// Get a hash to represent the shader we're about to make.
113+
IECore::MurmurHash hash = shaderStateComponent->hash();
114+
hash.append( forIDRender );
128115

129-
static ShaderPtr s = new Shader( vertexSource, fragmentSource );
130-
return s.get();
131-
}
116+
// If we've made an equivalent shader before, then
117+
// just return it.
118+
static vector<FlatConstant> flatConstants;
119+
vector<FlatConstant>::iterator it = lower_bound(
120+
flatConstants.begin(),
121+
flatConstants.end(),
122+
hash
123+
);
124+
if( it != flatConstants.end() && it->hash == hash )
125+
{
126+
return it->shaderSetup.get();
127+
}
132128

133-
static Shader *flatConstant()
134-
{
129+
// If we haven't, then make one.
135130

136-
static const char *fragmentSource =
131+
static const std::string constantFragmentSource =
137132

138133
"uniform vec3 Cs;" // get colour from uniform Cs, bypassing vertexCs
139134
""
@@ -142,10 +137,48 @@ static Shader *flatConstant()
142137
" gl_FragColor = vec4( Cs, 1 );"
143138
"}";
144139

145-
static ShaderPtr s = new Shader( "", fragmentSource );
146-
return s.get();
140+
const std::string &fragmentSource = forIDRender ? Selector::defaultIDShader()->fragmentSource() : constantFragmentSource;
141+
142+
const Shader *originalShader = shaderStateComponent->shaderSetup()->shader();
143+
ShaderLoader *shaderLoader = shaderStateComponent->shaderLoader();
144+
ConstShaderPtr shader = shaderLoader->create( originalShader->vertexSource(), originalShader->geometrySource(), fragmentSource );
145+
Shader::SetupPtr shaderSetup = new Shader::Setup( shader );
146+
shaderStateComponent->addParametersToShaderSetup( shaderSetup.get() );
147+
148+
// Put it in our store so we don't have to remake it next time.
149+
150+
flatConstants.insert( it, FlatConstant( hash, shaderSetup.get() ) );
151+
152+
return shaderSetup.get();
147153
}
148154

155+
} // namespace
156+
157+
//////////////////////////////////////////////////////////////////////////
158+
// Primitive implementation
159+
//////////////////////////////////////////////////////////////////////////
160+
161+
IE_CORE_DEFINERUNTIMETYPED( Primitive );
162+
163+
Primitive::Primitive()
164+
{
165+
}
166+
167+
Primitive::~Primitive()
168+
{
169+
}
170+
171+
void Primitive::addPrimitiveVariable( const std::string &name, const IECore::PrimitiveVariable &primVar )
172+
{
173+
if ( primVar.interpolation == IECore::PrimitiveVariable::Constant )
174+
{
175+
addUniformAttribute( name, primVar.data );
176+
}
177+
else
178+
{
179+
addVertexAttribute( name, primVar.data );
180+
}
181+
}
149182

150183
void Primitive::render( State *state ) const
151184
{
@@ -163,9 +196,10 @@ void Primitive::render( State *state ) const
163196
// with a simple shader and early out.
164197
if( currentSelector && currentSelector->mode() == Selector::GLSelect )
165198
{
166-
const Shader *constantShader = constant2();
167-
const Shader::Setup *constantSetup = shaderSetup( constantShader, state );
168-
Shader::Setup::ScopedBinding constantBinding( *constantSetup );
199+
const Shader::Setup *uniformSetup = flatConstantShaderSetup( state, /* forIDRender = */ false );
200+
Shader::Setup::ScopedBinding uniformBinding( *uniformSetup );
201+
const Shader::Setup *primitiveSetup = shaderSetup( uniformSetup->shader(), state );
202+
Shader::Setup::ScopedBinding primitiveBinding( *primitiveSetup );
169203
render( state, Primitive::DrawSolid::staticTypeId() );
170204
return;
171205
}
@@ -177,8 +211,18 @@ void Primitive::render( State *state ) const
177211
{
178212
glDepthMask( !depthSortRequested( state ) );
179213
// the state itself will have a shader with some nice uniform parameter
180-
// values. we are responsible for binding this setup.
181-
Shader::Setup *uniformSetup = state->get<ShaderStateComponent>()->shaderSetup();
214+
// values. we are responsible for binding this setup. unless we're performing
215+
// an id render for selection, in which case we're responsible for binding an
216+
// id shader.
217+
const Shader::Setup *uniformSetup = NULL;
218+
if( currentSelector && currentSelector->mode() == Selector::IDRender )
219+
{
220+
uniformSetup = flatConstantShaderSetup( state, /* forIDRender = */ true );
221+
}
222+
else
223+
{
224+
uniformSetup = state->get<ShaderStateComponent>()->shaderSetup();
225+
}
182226
Shader::Setup::ScopedBinding uniformBinding( *uniformSetup );
183227
// we then bind our own setup on top, adding in the parameters
184228
// stored on the primitive itself.
@@ -210,18 +254,18 @@ void Primitive::render( State *state ) const
210254
}
211255

212256
// get a constant shader suitable for drawing wireframes, points etc.
213-
const Shader *constantShader = flatConstant();
214-
if( currentSelector && currentSelector->mode() == Selector::IDRender )
215-
{
216-
// if we're in IDRender mode, then the constant shader is unsuitable,
217-
// and we should instead use the ID shader we've been given.
218-
constantShader = state->get<ShaderStateComponent>()->shaderSetup()->shader();
219-
}
220-
221-
const Shader::Setup *constantSetup = shaderSetup( constantShader, state );
222-
Shader::Setup::ScopedBinding constantBinding( *constantSetup );
257+
// we do this by taking the current shader from the state and overriding
258+
// just the fragment shader within it - we want to keep any vertex or
259+
// geometry shader the user has specified. again, if we're performing an
260+
// id render, we're responsible for binding an id shader instead.
261+
const Shader::Setup *uniformSetup = flatConstantShaderSetup( state, currentSelector && currentSelector->mode() == Selector::IDRender );
262+
Shader::Setup::ScopedBinding uniformBinding( *uniformSetup );
263+
264+
const Shader::Setup *primitiveSetup = shaderSetup( uniformSetup->shader(), state );
265+
Shader::Setup::ScopedBinding primitiveBinding( *primitiveSetup );
266+
223267
GLint csIndex = -1;
224-
if( const Shader::Parameter *csParameter = constantSetup->shader()->csParameter() )
268+
if( const Shader::Parameter *csParameter = uniformSetup->shader()->csParameter() )
225269
{
226270
csIndex = csParameter->location;
227271
}

0 commit comments

Comments
 (0)