diff options
28 files changed, 1938 insertions, 1172 deletions
diff --git a/o3d/converter/converter.gyp b/o3d/converter/converter.gyp index ddb5344..ffede11f 100644 --- a/o3d/converter/converter.gyp +++ b/o3d/converter/converter.gyp @@ -62,13 +62,6 @@ ], }, ], - ['renderer == "gles2"', - { - 'dependencies': [ - '../build/libs.gyp:cg_libs', - ], - }, - ], ['OS == "mac"', { 'postbuilds': [ diff --git a/o3d/core/core.gyp b/o3d/core/core.gyp index 323a4fe..46c5d4b 100644 --- a/o3d/core/core.gyp +++ b/o3d/core/core.gyp @@ -57,7 +57,6 @@ { 'include_dirs': [ '../../<(glewdir)/include', - '../../<(cgdir)/include', ], }, ], @@ -296,7 +295,6 @@ ['renderer == "gles2"', { 'dependencies': [ - '../build/libs.gyp:cg_libs', '../build/libs.gyp:gles2_libs', ], }, diff --git a/o3d/core/cross/client_info.h b/o3d/core/cross/client_info.h index 2d2c9b7..552ce02 100644 --- a/o3d/core/cross/client_info.h +++ b/o3d/core/cross/client_info.h @@ -66,6 +66,15 @@ class ClientInfo { return software_renderer_; } + // Whether or not shaders are GLSL. + bool glsl() const { +#if defined(RENDERER_GLES2) + return true; +#else + return false; +#endif + } + // Whether or not the underlying GPU supports non power of 2 textures. // NOTE: O3D always supports non power of 2 textures from a public API // point of view and massages the data underneath to make this work. diff --git a/o3d/core/cross/effect_test.cc b/o3d/core/cross/effect_test.cc index e240296..f301b61 100644 --- a/o3d/core/cross/effect_test.cc +++ b/o3d/core/cross/effect_test.cc @@ -74,6 +74,54 @@ void EffectTest::TearDown() { namespace { +#if defined(RENDERER_GLES2) + +char kLambertEffect[] = + "attribute vec4 position; // : POSITION; \n" + "attribute vec3 normal; // : NORMAL; \n" + "attribute vec2 texcoord1; // : TEXCOORD1; \n" + " \n" + "uniform mat4 worldViewProjection; // : WorldViewProjection; \n" + "uniform mat4 world; // : World; \n" + "uniform mat4 worldInverseTranspose; // : WorldInverseTranspose; \n" + "uniform vec3 lightWorldPos; \n" + " \n" + "varying vec3 v_n; // : TEXCOORD1; \n" + "varying vec3 v_l; // : TEXCOORD2; \n" + "varying vec2 v_diffuseUV; // : TEXCOORD0; \n" + " \n" + "void main() { \n" + " gl_Position = worldViewProjection * position; \n" + " v_n = (worldInverseTranspose * vec4(normal, 0)).xyz; \n" + " v_l = lightWorldPos - (world * position).xyz; \n" + " v_diffuseUV = texcoord1; \n" + "} \n" + " \n" + "// This hack is here for now since O3D only allows one string \n" + "// to be passed to Effect::loadFromFXString \n" + "// #o3d SplitMarker \n" + " \n" + "uniform vec4 lightColor; \n" + "uniform vec4 emissive; \n" + "uniform vec4 ambient; \n" + "uniform sampler2D diffuseSampler; \n" + " \n" + "varying vec3 v_n; // : TEXCOORD1; \n" + "varying vec3 v_l; // : TEXCOORD2; \n" + "varying vec2 v_diffuseUV; // : TEXCOORD0; \n" + " \n" + "void main() { \n" + " vec4 diffuse = texture2D(diffuseSampler, v_diffuseUV); \n" + " vec3 l = normalize(v_l); \n" + " vec3 n = normalize(v_n); \n" + " vec4 litR = vec4(dot(n,l),0,0,0); // fixme hack! \n" + " gl_FragColor = emissive+lightColor*(ambient+diffuse*litR.y); \n" + "} \n" + "// #o3d MatrixLoadOrder RowMajor \n" + ""; + +#else + char kLambertEffect[] = "struct a2v { \n" "float4 pos : POSITION; \n" @@ -118,6 +166,8 @@ char kLambertEffect[] = "// #o3d MatrixLoadOrder RowMajor \n" ""; +#endif + struct ParamInfo { const char* name; const ObjectBase::Class* type; @@ -126,6 +176,23 @@ struct ParamInfo { const ObjectBase::Class* sas_type; }; +#if defined(RENDERER_GLES2) +ParamInfo expected_params[] = { + { "lightWorldPos", ParamFloat3::GetApparentClass(), 0, "", NULL, }, + { "lightColor", ParamFloat4::GetApparentClass(), 0, "", NULL, }, + { "emissive", ParamFloat4::GetApparentClass(), 0, "", NULL, }, + { "ambient", ParamFloat4::GetApparentClass(), 0, "", NULL, }, + { "diffuseSampler", ParamSampler::GetApparentClass(), 0, "", NULL, }, + { "worldViewProjection", ParamMatrix4::GetApparentClass(), 0, + "WORLDVIEWPROJECTION", + WorldViewProjectionParamMatrix4::GetApparentClass(), }, + { "world", ParamMatrix4::GetApparentClass(), 0, + "WORLD", WorldParamMatrix4::GetApparentClass(), }, + { "worldInverseTranspose", ParamMatrix4::GetApparentClass(), 0, + "WORLDINVERSETRANSPOSE", + WorldInverseTransposeParamMatrix4::GetApparentClass(), }, +}; +#else ParamInfo expected_params[] = { { "lightWorldPos", ParamFloat3::GetApparentClass(), 0, "", NULL, }, { "lightColor", ParamFloat4::GetApparentClass(), 0, "", NULL, }, @@ -141,6 +208,7 @@ ParamInfo expected_params[] = { { "worldIT", ParamMatrix4::GetApparentClass(), 0, "WORLDINVERSETRANSPOSE", WorldInverseTransposeParamMatrix4::GetApparentClass(), }, }; +#endif EffectStreamInfo expected_streams[] = { EffectStreamInfo(Stream::POSITION, 0), diff --git a/o3d/core/cross/gles2/effect_gles2.cc b/o3d/core/cross/gles2/effect_gles2.cc index e0dfaca..f874334 100644 --- a/o3d/core/cross/gles2/effect_gles2.cc +++ b/o3d/core/cross/gles2/effect_gles2.cc @@ -33,7 +33,6 @@ // This file contains the definition of EffectGLES2, the OpenGLES2 // implementation of the abstract O3D class Effect. - // Disable pointer casting warning for OpenGLES2 calls that require a void* to // be cast to a GLuint #if defined(OS_WIN) @@ -64,28 +63,46 @@ namespace o3d { // draw polygons, etc. const int kNumLoggedEvents = 5; -// Convert a CGparameter data type into a Param type. Note that -// Param::BOOLEAN is never generated by the Cg compiler. -static const ObjectBase::Class* CgTypeToParamType(CGtype cg_type) { - switch (cg_type) { - case CG_FLOAT : - case CG_FLOAT1 : return ParamFloat::GetApparentClass(); - case CG_FLOAT2 : return ParamFloat2::GetApparentClass(); - case CG_FLOAT3 : return ParamFloat3::GetApparentClass(); - case CG_FLOAT4 : return ParamFloat4::GetApparentClass(); - case CG_INT : return ParamInteger::GetApparentClass(); - case CG_INT1 : return ParamInteger::GetApparentClass(); - case CG_FLOAT4x4 : return ParamMatrix4::GetApparentClass(); - case CG_BOOL : - case CG_BOOL1 : return ParamBoolean::GetApparentClass(); - case CG_SAMPLER : - case CG_SAMPLER1D : - case CG_SAMPLER2D : - case CG_SAMPLER3D : - case CG_SAMPLERCUBE : return ParamSampler::GetApparentClass(); +// Convert a GLunum data type into a Param type. +static const ObjectBase::Class* GLTypeToParamType(GLenum gl_type) { + switch (gl_type) { + case GL_FLOAT: + return ParamFloat::GetApparentClass(); + case GL_FLOAT_VEC2: + return ParamFloat2::GetApparentClass(); + case GL_FLOAT_VEC3: + return ParamFloat3::GetApparentClass(); + case GL_FLOAT_VEC4: + return ParamFloat4::GetApparentClass(); + case GL_INT: + return ParamInteger::GetApparentClass(); + case GL_INT_VEC2: + return NULL; + case GL_INT_VEC3: + return NULL; + case GL_INT_VEC4: + return NULL; + case GL_BOOL: + return ParamBoolean::GetApparentClass(); + case GL_BOOL_VEC2: + return NULL; + case GL_BOOL_VEC3: + return NULL; + case GL_BOOL_VEC4: + return NULL; + case GL_FLOAT_MAT2: + return NULL; + case GL_FLOAT_MAT3: + return NULL; + case GL_FLOAT_MAT4: + return ParamMatrix4::GetApparentClass(); + case GL_SAMPLER_2D: + return ParamSampler::GetApparentClass(); + case GL_SAMPLER_CUBE: + return ParamSampler::GetApparentClass(); default : { - DLOG(ERROR) << "Cannot convert CGtype " - << cgGetTypeString(cg_type) + DLOG(ERROR) << "Cannot convert GLtype " + << gl_type << " to a Param type."; return NULL; } @@ -94,14 +111,13 @@ static const ObjectBase::Class* CgTypeToParamType(CGtype cg_type) { // ----------------------------------------------------------------------------- -EffectGLES2::EffectGLES2(ServiceLocator* service_locator, CGcontext cg_context) +EffectGLES2::EffectGLES2(ServiceLocator* service_locator) : Effect(service_locator), semantic_manager_(service_locator->GetService<SemanticManager>()), renderer_(static_cast<RendererGLES2*>( service_locator->GetService<Renderer>())), - cg_context_(cg_context), - cg_vertex_(NULL), - cg_fragment_(NULL) { + gl_program_(0), + compile_count_(-1) { DLOG(INFO) << "EffectGLES2 Construct"; } @@ -109,373 +125,205 @@ EffectGLES2::EffectGLES2(ServiceLocator* service_locator, CGcontext cg_context) // constants tables. EffectGLES2::~EffectGLES2() { DLOG(INFO) << "EffectGLES2 Destruct \"" << name() << "\""; - if (cg_vertex_) { - cgDestroyProgram(cg_vertex_); - } - if (cg_fragment_) { - cgDestroyProgram(cg_fragment_); + ClearProgram(); +} + +void EffectGLES2::ClearProgram() { + if (gl_program_) { + glDeleteProgram(gl_program_); + gl_program_ = 0; } + set_source(""); } -// Rewrites vertex program assembly code to match GLES2 semantics for clipping. -// This parses the source, breaking it down into pieces: -// - declaration ("!!ARBvp1.0") -// - comments (that contain the parameter information) -// - instructions -// - "END" token. -// Then it rewrites the instructions so that 'result.position' doesn't get -// written directly, instead it is written to a temporary variable. Then a -// transformation is done on that variable before outputing to -// 'result.position': -// - offset x and y by half a pixel (times w). -// - remap z from [0..w] to [-w..w]. -// - invert y, if render targets are active. -// -// Note that for the 1/2 pixel offset, we need a parameter that depends on the -// current viewport. This is done through 'program.env[0]' which is shared -// across all programs (so we only have to update it once when we change the -// viewport), because Cg won't use them currently (it uses 'program.local' -// instead). -static bool RewriteVertexProgramSource(String *source) { - String::size_type pos = source->find('\n'); - if (pos == String::npos) { - DLOG(ERROR) << "could not find program declaration"; - return false; +GLuint EffectGLES2::LoadShader(GLenum type, const char* shader_src) { + GLuint shader = glCreateShader(type); + if (shader == 0) { + return 0; } - String decl(*source, 0, pos + 1); - String::size_type start_comments = pos + 1; - // skip the comments that contain the parameters etc. - for (; pos < source->size(); pos = source->find('\n', pos)) { - ++pos; - if (pos >= source->size()) - break; - if ((*source)[pos] != '#') - break; + + // Load the shader source + glShaderSource(shader, 1, &shader_src, NULL); + // Compile the shader + glCompileShader(shader); + // Check the compile status + GLint value; + glGetShaderiv(shader, GL_COMPILE_STATUS, &value); + if (value == 0) { + GLint error_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &error_length); + scoped_array<char> buffer(new char[error_length + 1]); + GLsizei length; + glGetShaderInfoLog(shader, error_length + 1, &length, buffer.get()); + O3D_ERROR(service_locator()) << "Effect Compile Error: " << buffer.get(); + DLOG(ERROR) << "Error compiling shader:" << buffer.get(); + glDeleteShader(shader); + return 0; } - if (pos >= source->size()) { - // we only found comments. - return false; + return shader; +} + +namespace { + +String::size_type GetEndOfIdentifier(const String& original, + String::size_type start) { + if (start < original.size()) { + // check that first character is alpha or '_' + if (isalpha(original[start]) || original[start] == '_') { + String::size_type end = original.size(); + String::size_type position = start; + while (position < end) { + char c = original[position]; + if (!isalnum(c) && c != '_') { + break; + } + ++position; + } + return position; + } } - String comments(*source, start_comments, pos - start_comments); + return String::npos; +} - String::size_type end_token = source->find("\nEND", pos + 1); - if (end_token == String::npos) { - DLOG(ERROR) << "Compiled shader doesn't have an END token"; +bool GetIdentifierAfterString(const String& original, + const String& phrase, + String* word) { + String::size_type position = original.find(phrase); + if (position == String::npos) { return false; } - String instructions(*source, pos, end_token + 1 - pos); - - // Replace accesses to 'result.position' by accesses to our temp variable - // '$O3D_HPOS'. - // '$' is a valid symbol for identifiers, but Cg doesn't seem to be using - // it, so we can use it to ensure we don't have name conflicts. - static const char kOutPositionRegister[] = "result.position"; - for (String::size_type i = instructions.find(kOutPositionRegister); - i < String::npos; i = instructions.find(kOutPositionRegister, i)) { - instructions.replace(i, strlen(kOutPositionRegister), "$O3D_HPOS"); - } - *source = decl + - comments + - // .x = 1/viewport.width; .y = -1/viewport.height; .z = 2.0; w = +/-1.0; - "PARAM $O3D_HELPER = program.env[0];\n" - "TEMP $O3D_HPOS;\n" + - instructions + - // hpos.x <- hpos.x + hpos.w / viewport.width; - // hpos.y <- hpos.y - hpos.w / viewport.height; - "MAD $O3D_HPOS.xy, $O3D_HELPER.xyyy, $O3D_HPOS.w, $O3D_HPOS.xyyy;\n" - // Invert y, based on the w component of the helper arg. - "MUL $O3D_HPOS.y, $O3D_HELPER.w, $O3D_HPOS.y;\n" - // hpos.z <- hpos.z * 2 - hpos.w - "MAD $O3D_HPOS.z, $O3D_HPOS.z, $O3D_HELPER.z, -$O3D_HPOS.w;\n" - "MOV result.position, $O3D_HPOS;\n" - "END\n"; - return true; + // Find end of identifier + String::size_type start = position + phrase.size(); + String::size_type end = GetEndOfIdentifier(original, start); + if (end != start && end != String::npos) { + *word = String(original, start, end - start); + return true; + } + return false; } +} // anonymous namespace + // Initializes the Effect object using the shaders found in an FX formatted // string. bool EffectGLES2::LoadFromFXString(const String& effect) { DLOG(INFO) << "EffectGLES2 LoadFromFXString"; renderer_->MakeCurrentLazy(); - set_source(""); + ++compile_count_; + ClearProgram(); - String vertex_shader_entry_point; - String fragment_shader_entry_point; - MatrixLoadOrder matrix_load_order; - // TODO(gman): Check for failure once shader parser is in. - if (!ValidateFX(effect, - &vertex_shader_entry_point, - &fragment_shader_entry_point, - &matrix_load_order)) { + String matrix_load_order_str; + if (!GetIdentifierAfterString(effect, + kMatrixLoadOrderPrefix, + &matrix_load_order_str)) { + O3D_ERROR(service_locator()) << "Failed to find \"" + << kMatrixLoadOrderPrefix + << "\" in Effect"; return false; } - - set_matrix_load_order(matrix_load_order); - - // Compile the original vertex program once, to get the ARBVP1 assembly code. - CGprogram original_vp = cgCreateProgram(cg_context_, CG_SOURCE, - effect.c_str(), CG_PROFILE_ARBVP1, - vertex_shader_entry_point.c_str(), - NULL); - const char* listing = cgGetLastListing(cg_context_); - if (original_vp == NULL) { - O3D_ERROR(service_locator()) << "Effect Compile Error: " - << cgGetErrorString(cgGetError()) << " : " - << listing; + bool column_major = matrix_load_order_str.c_str() == "ColumnMajor"; + MatrixLoadOrder matrix_load_order = column_major ? COLUMN_MAJOR : ROW_MAJOR; + + // Split the effect + const char* kSplitMarker = "// #o3d SplitMarker"; + String::size_type split_pos = effect.find(kSplitMarker); + if (split_pos == String::npos) { + O3D_ERROR(service_locator()) << "Missing '" << kSplitMarker + << "' in shader: " << effect; return false; } - if (listing && listing[0] != 0) { - DLOG(WARNING) << "Effect Compile Warnings: " << listing; - } - - String vp_assembly = cgGetProgramString(original_vp, CG_COMPILED_PROGRAM); - cgDestroyProgram(original_vp); - if (!RewriteVertexProgramSource(&vp_assembly)) { - return false; - } - cg_vertex_ = cgCreateProgram(cg_context_, CG_OBJECT, vp_assembly.c_str(), - CG_PROFILE_ARBVP1, - vertex_shader_entry_point.c_str(), NULL); - listing = cgGetLastListing(cg_context_); - if (cg_vertex_ == NULL) { - O3D_ERROR(service_locator()) << "Effect post-rewrite Compile Error: " - << cgGetErrorString(cgGetError()) << " : " - << listing; - return false; - } + String vertex_shader(effect.substr(0, split_pos)); + String fragment_shader("// " + effect.substr(split_pos)); -#ifdef OS_WIN - // Get metrics for length of the vertex shader - const char* shader_data = cgGetProgramString(cg_vertex_, CG_COMPILED_PROGRAM); - metric_vertex_shader_instruction_count.AddSample(strlen(shader_data)); -#endif - - if (listing && listing[0] != 0) { - DLOG(WARNING) << "Effect post-rewrite compile warnings: " << listing; - } - - CHECK_GL_ERROR(); + set_matrix_load_order(matrix_load_order); - // If the program rewrite introduced some syntax or semantic errors, we won't - // know it until we load the program (through a GLES2 error). - // So flush all GLES2 errors first... - do {} while (glGetError() != GL_NO_ERROR); - - // ... Then load the program ... - cgGLLoadProgram(cg_vertex_); - - // ... And check for GLES2 errors. - if (glGetError() != GL_NO_ERROR) { - O3D_ERROR(service_locator()) - << "Effect post-rewrite GLES2 Error: " - << glGetString(GL_PROGRAM_ERROR_STRING_ARB) - << "\nSource: \n" - << vp_assembly; + GLuint gl_vertex_shader = + LoadShader(GL_VERTEX_SHADER, vertex_shader.c_str()); + if (!gl_vertex_shader) { return false; } - - cg_fragment_ = cgCreateProgram(cg_context_, CG_SOURCE, effect.c_str(), - CG_PROFILE_ARBFP1, - fragment_shader_entry_point.c_str(), NULL); - listing = cgGetLastListing(cg_context_); - if (cg_fragment_ == NULL) { - O3D_ERROR(service_locator()) << "Effect Compile Error: " - << cgGetErrorString(cgGetError()) << " : " - << listing; + GLuint gl_fragment_shader = + LoadShader(GL_FRAGMENT_SHADER, fragment_shader.c_str()); + if (!gl_fragment_shader) { + glDeleteShader(gl_vertex_shader); return false; } - -#ifdef OS_WIN - // Get metrics for length of the fragment shader - shader_data = cgGetProgramString(cg_fragment_, CG_COMPILED_PROGRAM); - metric_pixel_shader_instruction_count.AddSample(strlen(shader_data)); -#endif - - if (listing && listing[0] != 0) { - DLOG(WARNING) << "Effect Compile Warnings: " << listing; + gl_program_ = glCreateProgram(); + if (!gl_program_) { + glDeleteShader(gl_fragment_shader); + glDeleteShader(gl_vertex_shader); + return false; } - - cgGLLoadProgram(cg_fragment_); - - // Also check for GLES2 errors, in case Cg managed to compile, but generated a - // bad program. - if (glGetError() != GL_NO_ERROR) { - O3D_ERROR(service_locator()) - << "Effect GLES2 Error: " - << glGetString(GL_PROGRAM_ERROR_STRING_ARB); + glAttachShader(gl_program_, gl_vertex_shader); + glAttachShader(gl_program_, gl_fragment_shader); + glLinkProgram(gl_program_); + glDeleteShader(gl_vertex_shader); + glDeleteShader(gl_fragment_shader); + // Check the compile status + GLint value; + glGetProgramiv(gl_program_, GL_LINK_STATUS, &value); + if (value == 0) { + GLint error_length; + glGetProgramiv(gl_program_, GL_INFO_LOG_LENGTH, &error_length); + scoped_array<char> buffer(new char[error_length + 1]); + GLsizei length; + glGetProgramInfoLog(gl_program_, error_length + 1, &length, buffer.get()); + O3D_ERROR(service_locator()) << "Effect Link Error: " << buffer.get(); + DLOG(ERROR) << "Error linking programr:" << buffer.get(); + glDeleteProgram(gl_program_); return false; } - // TODO(o3d): remove this (OLD path for textures). - FillSamplerToTextureMap(effect); - CHECK_GL_ERROR(); set_source(effect); return true; } -// Fills the sampler->texture map. This needs to compile the source as an -// effect because the state assignments get lost when compiled as a -// shader. -// Note that we compile the raw effect, which shouldn't have any -// technique/pass, so we don't actually create programs, just parse the -// uniforms and state assignments. -void EffectGLES2::FillSamplerToTextureMap(const String &effect) { - CGeffect cg_effect = cgCreateEffect(cg_context_, effect.c_str(), NULL); - if (!cg_effect) { - DLOG(ERROR) << "Could not compile the effect to find " - << "Sampler->Texture associations"; - return; - } - for (CGparameter param = cgGetFirstEffectParameter(cg_effect); - param; param = cgGetNextLeafParameter(param)) { - CGtype cg_type = cgGetParameterType(param); - switch (cg_type) { - case CG_SAMPLER: - case CG_SAMPLER1D: - case CG_SAMPLER2D: - case CG_SAMPLER3D: - case CG_SAMPLERCUBE: - break; - default: - continue; - } - CGstateassignment state_assignment = - cgGetNamedSamplerStateAssignment(param, "Texture"); - if (!state_assignment) - continue; - CGparameter texture_param = - cgGetTextureStateAssignmentValue(state_assignment); - if (!texture_param) - continue; - DCHECK((cgGetParameterType(texture_param) == CG_TEXTURE)); - sampler_to_texture_map_[cgGetParameterName(param)] = - cgGetParameterName(texture_param); - } - cgDestroyEffect(cg_effect); -} - -// TODO(o3d): remove this (OLD path for textures). -String EffectGLES2::GetTextureNameFromSamplerParamName( - const String &sampler_name) { - std::map<String, String>::iterator it = - sampler_to_texture_map_.find(sampler_name); - if (it != sampler_to_texture_map_.end()) { - return it->second; - } else { - return ""; - } -} - -// Given a CG_SAMPLER parameter, find the corresponding CG_TEXTURE -// parameter. From this CG_TEXTURE, find a matching Param by name. -ParamTexture* EffectGLES2::GetTextureParamFromCgSampler( - CGparameter cg_sampler, - const std::vector<ParamObject*> ¶m_objects) { - DLOG(INFO) << "EffectGLES2 GetTextureParamFromCgSampler"; - DLOG_ASSERT(cgGetParameterType(cg_sampler) != CG_SAMPLER); - String sampler_name = cgGetParameterName(cg_sampler); - String param_name = GetTextureNameFromSamplerParamName(sampler_name); - if (param_name.size() == 0) { - // Sampler has no texture associated with it. - return NULL; - } - // Find a matching Param with the same name as the CG_TEXTURE. - for (unsigned int i = 0; i < param_objects.size(); ++i) { - Param* param = param_objects[i]->GetUntypedParam(param_name); - if (param && param->IsA(ParamTexture::GetApparentClass())) { - // Success. - DLOG(INFO) << "EffectGLES2 Matched CG_SAMPLER \"" - << sampler_name - << "\" To Param \"" - << param_name << "\""; - return down_cast<ParamTexture*>(param); - } - } - DLOG(INFO) << "No matching Param for CG_TEXTURE \"" - << param_name - << "\" used by CG_SAMPLER \"" - << sampler_name << "\""; - return NULL; -} - void EffectGLES2::GetShaderParamInfo( - CGprogram program, - CGenum name_space, + GLuint program, std::map<String, EffectParameterInfo>* info_map) { DCHECK(info_map); + if (!program) { + return; + } - // Loop over all parameters, visiting only CGparameters that have - // had storage allocated to them. - CGparameter cg_param = cgGetFirstParameter(program, name_space); - for (; cg_param != NULL; cg_param = cgGetNextParameter(cg_param)) { - CGenum variability = cgGetParameterVariability(cg_param); - if (variability != CG_UNIFORM) - continue; - CGenum direction = cgGetParameterDirection(cg_param); - if (direction != CG_IN) - continue; - String name = cgGetParameterName(cg_param); - CGtype cg_type = cgGetParameterType(cg_param); - // Texture parameters need special handling as the c3cImport system - // records a handle to the CG_TEXTURE param, not the CG_SAMPLER - // param. D3D sets textures by binding a bitmap to the Texture - // param, Cg binds bitmaps to the Sampler parameter. We solve this - // by keeping an internal collection of Texture-Sampler mappings - // that is built here, so we can later to do the reverse lookup. - // - // TODO(o3d): This will not solve the one-to-many problem of one - // Texture being used by many Sampler params, but it's enough to get - // us up and running. - // - // TODO(o3d): Once we start using samplers exclusively, this special - // treatment of textures should go away. For the time being though, we do - // end up creating a texture param on the param_object. - if (cg_type == CG_SAMPLER1D || - cg_type == CG_SAMPLER2D || - cg_type == CG_SAMPLER3D || - cg_type == CG_SAMPLERCUBE) { - // rename the parameter to have the name of the texture. - String texture_param_name = GetTextureNameFromSamplerParamName(name); - if (texture_param_name.size() != 0) { - (*info_map)[texture_param_name] = EffectParameterInfo( - texture_param_name, - ParamTexture::GetApparentClass(), - 0, - "", - false); - } - } else if (cg_type == CG_TEXTURE) { - continue; - } - int num_elements; - if (cg_type == CG_ARRAY) { - num_elements = cgGetArraySize(cg_param, 0); - // Substitute the first element's type for our type. - cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); - } else { - num_elements = 0; + GLint num_uniforms = 0; + GLint max_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &num_uniforms); + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len); + // TODO(gman): Should we check for error? + scoped_array<char> name_buffer(new char[max_len + 1]); + // Loop over all parameters. + for (GLint ii = 0; ii < num_uniforms; ++ii) { + GLsizei length; + GLsizei size; + GLenum type; + glGetActiveUniform( + program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); + String name(name_buffer.get()); + // TODO(gman): Should we check for error? + // TODO(gman): Should we skip uniforms that start with "gl_"? + GLint location = glGetUniformLocation(program, name_buffer.get()); + int num_elements = 0; + if (size > 1) { + // It's an array. + num_elements = size; } - const ObjectBase::Class *param_class = CgTypeToParamType(cg_type); + const ObjectBase::Class *param_class = GLTypeToParamType(type); if (!param_class) continue; - const char* cg_semantic = cgGetParameterSemantic(cg_param); const ObjectBase::Class *sem_class = NULL; - if (cg_semantic != NULL && cg_semantic[0] != '\0') { - // NOTE: this semantic is not the regularised profile semantic output - // from the CGC compiler but the actual user supplied semantic from - // the shader source code, so this match is valid. - sem_class = semantic_manager_->LookupSemantic(cg_semantic); - } + // Since there is no SAS for GLSL let's just use reserved names. + sem_class = semantic_manager_->LookupSemantic(name); (*info_map)[name] = EffectParameterInfo( name, param_class, num_elements, - (cg_semantic != NULL) ? cg_semantic : "", + sem_class != NULL ? name : "", sem_class); } } @@ -484,15 +332,8 @@ void EffectGLES2::GetParameterInfo(EffectParameterInfoArray* info_array) { DCHECK(info_array); std::map<String, EffectParameterInfo> info_map; renderer_->MakeCurrentLazy(); - if (cg_vertex_) { - GetShaderParamInfo(cg_vertex_, CG_PROGRAM, &info_map); - GetShaderParamInfo(cg_vertex_, CG_GLOBAL, &info_map); - } - if (cg_fragment_) { - // create Param objects based on the parameters found in the fragment - // program. - GetShaderParamInfo(cg_fragment_, CG_PROGRAM, &info_map); - GetShaderParamInfo(cg_fragment_, CG_GLOBAL, &info_map); + if (gl_program_) { + GetShaderParamInfo(gl_program_, &info_map); } info_array->clear(); info_array->reserve(info_map.size()); @@ -504,34 +345,31 @@ void EffectGLES2::GetParameterInfo(EffectParameterInfoArray* info_array) { } void EffectGLES2::GetVaryingVertexShaderParamInfo( - CGprogram program, - CGenum name_space, + GLuint program, std::vector<EffectStreamInfo>* info_array) { - CGparameter cg_param = cgGetFirstLeafParameter(cg_vertex_, name_space); - for (; cg_param != NULL; cg_param = cgGetNextLeafParameter(cg_param)) { - CGenum variability = cgGetParameterVariability(cg_param); - if (variability != CG_VARYING) - continue; - CGenum direction = cgGetParameterDirection(cg_param); - if (direction != CG_IN) - continue; - - const char* cg_semantic = cgGetParameterSemantic(cg_param); - if (cg_semantic == NULL) - continue; - - int attr = SemanticNameToGLVertexAttribute(cg_semantic); - if (attr < 0) - continue; - - int semantic_index = 0; - Stream::Semantic semantic = GLVertexAttributeToStream(attr, - &semantic_index); - if (semantic == Stream::UNKNOWN_SEMANTIC) + GLint num_attribs = 0; + GLint max_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &num_attribs); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); + // TODO(gman): Should we check for error? + scoped_array<char> name_buffer(new char[max_len + 1]); + for (GLint ii = 0; ii < num_attribs; ++ii) { + GLsizei length; + GLsizei size; + GLenum type; + glGetActiveAttrib( + program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); + // TODO(gman): Should we check for error? + GLint location = glGetAttribLocation(program, name_buffer.get()); + String name(name_buffer.get()); + // Since GLSL has no semantics just go by name. + Stream::Semantic semantic; + int semantic_index; + if (!SemanticNameToSemantic(name, &semantic, &semantic_index)) { continue; + } - info_array->push_back(EffectStreamInfo(semantic, - semantic_index)); + info_array->push_back(EffectStreamInfo(semantic, semantic_index)); } } @@ -540,96 +378,66 @@ void EffectGLES2::GetStreamInfo( DCHECK(info_array); renderer_->MakeCurrentLazy(); info_array->clear(); - GetVaryingVertexShaderParamInfo(cg_vertex_, CG_PROGRAM, info_array); - GetVaryingVertexShaderParamInfo(cg_vertex_, CG_GLOBAL, info_array); + GetVaryingVertexShaderParamInfo(gl_program_, info_array); } - // private functions ----------------------------------------------------------- -// Loop over all the CG_SAMPLER objects and attach the GLuint handle for the -// GLES2 texture object that we discovered earlier. Then execute the -// CGstateassignments in the sampler_state to set up the texture unit. -// TODO(o3d): remove this (OLD path for textures). -void EffectGLES2::SetTexturesFromEffect(ParamCacheGLES2* param_cache_gl) { - DLOG_FIRST_N(INFO, kNumLoggedEvents) - << "EffectGLES2 EnableTexturesFromEffect"; - ParamCacheGLES2::SamplerParameterMap& map = param_cache_gl->sampler_map(); - ParamCacheGLES2::SamplerParameterMap::iterator i; - for (i = map.begin(); i != map.end(); ++i) { - CGparameter cg_param = i->first; - ParamTexture *param = i->second; - if (param != NULL) { - Texture *t = param->value(); - if (t) { - GLuint handle = static_cast<GLuint>(reinterpret_cast<intptr_t>( - t->GetTextureHandle())); - cgGLSetTextureParameter(cg_param, handle); - cgGLEnableTextureParameter(cg_param); - } - } - } - CHECK_GL_ERROR(); -} - -// Loop through all the uniform CGparameters on the effect and set their -// values from their corresponding Params on the various ParamObject (as stored -// in the ParamCacheGLES2). +// Loop through all the uniform parameters on the effect and set their values +// from their corresponding Params on the various ParamObject (as stored in the +// ParamCacheGLES2). void EffectGLES2::UpdateShaderUniformsFromEffect( ParamCacheGLES2* param_cache_gl) { DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGLES2 UpdateShaderUniformsFromEffect"; + renderer_->ResetTextureGroupSetCount(); ParamCacheGLES2::UniformParameterMap& map = param_cache_gl->uniform_map(); ParamCacheGLES2::UniformParameterMap::iterator i; for (i = map.begin(); i != map.end(); ++i) { - CGparameter cg_param = i->first; - i->second->SetEffectParam(renderer_, cg_param); + GLES2Parameter gl_param = i->first; + i->second->SetEffectParam(renderer_, gl_param); } CHECK_GL_ERROR(); } -// Loop through all the uniform CGparameters on the effect and reset their -// values. For now, this unbinds textures contained in sampler parameters. -void EffectGLES2::ResetShaderUniforms(ParamCacheGLES2* param_cache_gl) { +// Loop through all the uniform parameters on the effect and reset their values. +// For now, this unbinds textures contained in sampler parameters. +void EffectGLES2::ResetShaderUniforms(ParamCacheGLES2* param_cache_gles2) { DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGLES2 ResetShaderUniforms"; - ParamCacheGLES2::UniformParameterMap& map = param_cache_gl->uniform_map(); + ParamCacheGLES2::UniformParameterMap& map = param_cache_gles2->uniform_map(); ParamCacheGLES2::UniformParameterMap::iterator i; for (i = map.begin(); i != map.end(); ++i) { - CGparameter cg_param = i->first; - i->second->ResetEffectParam(renderer_, cg_param); + GLES2Parameter gl_param = i->first; + i->second->ResetEffectParam(renderer_, gl_param); } CHECK_GL_ERROR(); } // Updates the values of the vertex and fragment shader parameters using the -// current values in the param/cgparam caches. -void EffectGLES2::PrepareForDraw(ParamCacheGLES2* param_cache_gl) { +// current values in the param/glparam caches. +void EffectGLES2::PrepareForDraw(ParamCacheGLES2* param_cache_gles2) { DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGLES2 PrepareForDraw \"" << name() << "\""; DCHECK(renderer_->IsCurrent()); - if (cg_vertex_ && cg_fragment_) { + if (gl_program_) { // Initialise the render states for this pass, this includes the shaders. - cgGLBindProgram(cg_vertex_); - cgGLBindProgram(cg_fragment_); - UpdateShaderUniformsFromEffect(param_cache_gl); - - // TODO(o3d): remove this (OLD path for textures). - SetTexturesFromEffect(param_cache_gl); + glUseProgram(gl_program_); + UpdateShaderUniformsFromEffect(param_cache_gles2); } else { DLOG_FIRST_N(ERROR, kNumLoggedEvents) - << "No valid CGeffect found " + << "No valid GLES2effect found " << "in Effect \"" << name() << "\""; } CHECK_GL_ERROR(); } // Resets the render states back to their default value. -void EffectGLES2::PostDraw(ParamCacheGLES2* param_cache_gl) { +void EffectGLES2::PostDraw(ParamCacheGLES2* param_cache_gles2) { DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGLES2 PostDraw \"" << name() << "\""; DCHECK(renderer_->IsCurrent()); - ResetShaderUniforms(param_cache_gl); + ResetShaderUniforms(param_cache_gles2); CHECK_GL_ERROR(); } diff --git a/o3d/core/cross/gles2/effect_gles2.h b/o3d/core/cross/gles2/effect_gles2.h index a919fec..e9655ae 100644 --- a/o3d/core/cross/gles2/effect_gles2.h +++ b/o3d/core/cross/gles2/effect_gles2.h @@ -59,20 +59,23 @@ class ParamTexture; class RendererGLES2; class SemanticManager; +// TODO(gman): Replace. +typedef GLuint GLES2Parameter; + // A class to set an effect parameter from an O3D parameter. class EffectParamHandlerGLES2 : public RefCounted { public: typedef SmartPointer<EffectParamHandlerGLES2> Ref; virtual ~EffectParamHandlerGLES2() { } - // Sets a GLES2/Cg Effect Parameter by an O3D Param. + // Sets a GLES2 Effect Parameter by an O3D Param. virtual void SetEffectParam( - RendererGLES2* renderer, CGparameter cg_param) = 0; + RendererGLES2* renderer, GLES2Parameter param) = 0; - // Resets a GLES2/Cg Effect parameter to default (currently only - // unbinds textures contained in Sampler params). + // Resets a GLES2 Effect parameter to default (currently only unbinds textures + // contained in Sampler params). virtual void ResetEffectParam( - RendererGLES2* renderer, CGparameter cg_param) {} + RendererGLES2* renderer, GLES2Parameter param) {} }; // EffectGLES2 is an implementation of the Effect object for OpenGLES2. It @@ -81,7 +84,7 @@ class EffectParamHandlerGLES2 : public RefCounted { // provided separately as shader code or together in an FX file. class EffectGLES2 : public Effect { public: - EffectGLES2(ServiceLocator* service_locator, CGcontext cg_context); + explicit EffectGLES2(ServiceLocator* service_locator); virtual ~EffectGLES2(); // Reads the vertex and fragment shaders from string in the FX format. @@ -104,16 +107,8 @@ class EffectGLES2 : public Effect { virtual void GetStreamInfo( EffectStreamInfoArray* info_array); - // Given a CG_SAMPLER parameter, find the corresponding CG_TEXTURE - // parameterand from this CG_TEXTURE, find a matching Param by name in a list - // of ParamObject. - // TODO(o3d): remove this (OLD path for textures). - ParamTexture* GetTextureParamFromCgSampler( - CGparameter cg_sampler, - const std::vector<ParamObject*> ¶m_objects); - - CGprogram cg_vertex_program() { return cg_vertex_; } - CGprogram cg_fragment_program() { return cg_fragment_; } + GLuint gl_program() { return gl_program_; } + int compile_count() { return compile_count_; } private: // Loops through all the parameters in the ShapeDataGLES2 and updates the @@ -121,13 +116,13 @@ class EffectGLES2 : public Effect { void UpdateShaderUniformsFromEffect(ParamCacheGLES2* param_cache_gl); // Undoes the effect of the above. For now, this unbinds textures. void ResetShaderUniforms(ParamCacheGLES2* param_cache_gl); - void GetShaderParamInfo(CGprogram program, - CGenum name_space, + void GetShaderParamInfo(GLuint program, std::map<String, EffectParameterInfo>* info_map); void GetVaryingVertexShaderParamInfo( - CGprogram program, - CGenum name_space, + GLuint program, std::vector<EffectStreamInfo>* info_array); + void ClearProgram(); + GLuint LoadShader(GLenum type, const char* shader_src); // TODO(o3d): remove these (OLD path for textures). void SetTexturesFromEffect(ParamCacheGLES2* param_cache_gl); @@ -137,9 +132,8 @@ class EffectGLES2 : public Effect { SemanticManager* semantic_manager_; RendererGLES2* renderer_; - CGcontext cg_context_; - CGprogram cg_vertex_; - CGprogram cg_fragment_; + GLuint gl_program_; + int compile_count_; // TODO(o3d): remove this (OLD path for textures). std::map<String, String> sampler_to_texture_map_; diff --git a/o3d/core/cross/gles2/gles2_headers.h b/o3d/core/cross/gles2/gles2_headers.h index 6fc9c9c..d00e2ef 100644 --- a/o3d/core/cross/gles2/gles2_headers.h +++ b/o3d/core/cross/gles2/gles2_headers.h @@ -36,8 +36,6 @@ #if defined(OS_WIN) #include <GL/wglew.h> #endif -#include <Cg/cg.h> -#include <Cg/cgGL.h> #endif // O3D_CORE_CROSS_GLES2_GL_HEADERS_H_ diff --git a/o3d/core/cross/gles2/param_cache_gles2.cc b/o3d/core/cross/gles2/param_cache_gles2.cc index 8d3371a..921813f 100644 --- a/o3d/core/cross/gles2/param_cache_gles2.cc +++ b/o3d/core/cross/gles2/param_cache_gles2.cc @@ -45,22 +45,28 @@ namespace o3d { +namespace { + +String GetUniformSemantic(GLES2Parameter gl_param, const String& name) { + return name; +} + +} // anonymous namespace + typedef std::vector<ParamObject *> ParamObjectList; ParamCacheGLES2::ParamCacheGLES2(SemanticManager* semantic_manager, Renderer* renderer) : semantic_manager_(semantic_manager), renderer_(renderer), - last_vertex_program_(0), - last_fragment_program_(0) { + last_compile_count_(0) { } bool ParamCacheGLES2::ValidateEffect(Effect* effect) { DLOG_ASSERT(effect); - EffectGLES2* effect_gl = down_cast<EffectGLES2*>(effect); - return (effect_gl->cg_vertex_program() == last_vertex_program_ || - effect_gl->cg_fragment_program() == last_fragment_program_); + EffectGLES2* effect_gles2 = down_cast<EffectGLES2*>(effect); + return (effect_gles2->compile_count() == last_compile_count_); } void ParamCacheGLES2::UpdateCache(Effect* effect, @@ -71,15 +77,13 @@ void ParamCacheGLES2::UpdateCache(Effect* effect, DLOG_ASSERT(effect); EffectGLES2* effect_gl = down_cast<EffectGLES2*>(effect); - ScanCgEffectParameters(effect_gl->cg_vertex_program(), - effect_gl->cg_fragment_program(), + ScanGLEffectParameters(effect_gl->gl_program(), draw_element, element, material, override); - last_vertex_program_ = effect_gl->cg_vertex_program(); - last_fragment_program_ = effect_gl->cg_fragment_program(); + last_compile_count_ = effect_gl->compile_count(); } template <typename T> @@ -88,7 +92,7 @@ class TypedEffectParamHandlerGLES2 : public EffectParamHandlerGLES2 { explicit TypedEffectParamHandlerGLES2(T* param) : param_(param) { } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param); + virtual void SetEffectParam(RendererGLES2* renderer, GLES2Parameter gl_param); private: T* param_; }; @@ -98,10 +102,11 @@ class EffectParamHandlerGLMatrixRows : public EffectParamHandlerGLES2 { explicit EffectParamHandlerGLMatrixRows(ParamMatrix4* param) : param_(param) { } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void SetEffectParam(RendererGLES2* renderer, + GLES2Parameter gl_param) { // set the data as floats in row major order. Matrix4 mat = param_->value(); - cgSetMatrixParameterfr(cg_param, &mat[0][0]); + glUniformMatrix4fv(gl_param, 1, GL_FALSE, &mat[0][0]); } private: ParamMatrix4* param_; @@ -112,10 +117,11 @@ class EffectParamHandlerGLMatrixColumns : public EffectParamHandlerGLES2 { explicit EffectParamHandlerGLMatrixColumns(ParamMatrix4* param) : param_(param) { } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void SetEffectParam( + RendererGLES2* renderer, GLES2Parameter gl_param) { // set the data as floats in column major order. - Matrix4 mat = param_->value(); - cgSetMatrixParameterfc(cg_param, &mat[0][0]); + Matrix4 mat = transpose(param_->value()); + glUniformMatrix4fv(gl_param, 1, GL_FALSE, &mat[0][0]); } private: ParamMatrix4* param_; @@ -124,49 +130,49 @@ class EffectParamHandlerGLMatrixColumns : public EffectParamHandlerGLES2 { template <> void TypedEffectParamHandlerGLES2<ParamFloat>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { Float f = param_->value(); - cgSetParameter1f(cg_param, f); + glUniform1f(gl_param, f); }; template <> void TypedEffectParamHandlerGLES2<ParamFloat2>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { Float2 f = param_->value(); - cgSetParameter2fv(cg_param, f.GetFloatArray()); + glUniform2fv(gl_param, 1, f.GetFloatArray()); }; template <> void TypedEffectParamHandlerGLES2<ParamFloat3>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { Float3 f = param_->value(); - cgSetParameter3fv(cg_param, f.GetFloatArray()); + glUniform3fv(gl_param, 1, f.GetFloatArray()); }; template <> void TypedEffectParamHandlerGLES2<ParamFloat4>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { Float4 f = param_->value(); - cgSetParameter4fv(cg_param, f.GetFloatArray()); + glUniform4fv(gl_param, 1, f.GetFloatArray()); }; template <> void TypedEffectParamHandlerGLES2<ParamInteger>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { int i = param_->value(); - cgSetParameter1i(cg_param, i); + glUniform1i(gl_param, i); }; template <> void TypedEffectParamHandlerGLES2<ParamBoolean>::SetEffectParam( RendererGLES2* renderer, - CGparameter cg_param) { + GLES2Parameter gl_param) { int i = param_->value(); - cgSetParameter1i(cg_param, i); + glUniform1i(gl_param, i); }; class EffectParamHandlerForSamplersGLES2 : public EffectParamHandlerGLES2 { @@ -174,7 +180,8 @@ class EffectParamHandlerForSamplersGLES2 : public EffectParamHandlerGLES2 { explicit EffectParamHandlerForSamplersGLES2(ParamSampler* param) : param_(param) { } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void SetEffectParam( + RendererGLES2* renderer, GLES2Parameter gl_param) { SamplerGLES2* sampler_gl = down_cast<SamplerGLES2*>(param_->value()); if (!sampler_gl) { // Use the error sampler. @@ -185,14 +192,16 @@ class EffectParamHandlerForSamplersGLES2 : public EffectParamHandlerGLES2 { << "Missing Sampler for ParamSampler " << param_->name(); } } - sampler_gl->SetTextureAndStates(cg_param); + GLint handle = sampler_gl->SetTextureAndStates(gl_param); + glUniform1iv(gl_param, 1, &handle); } - virtual void ResetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void ResetEffectParam( + RendererGLES2* renderer, GLES2Parameter gl_param) { SamplerGLES2* sampler_gl = down_cast<SamplerGLES2*>(param_->value()); if (!sampler_gl) { sampler_gl = down_cast<SamplerGLES2*>(renderer->error_sampler()); } - sampler_gl->ResetTexture(cg_param); + sampler_gl->ResetTexture(gl_param); } private: ParamSampler* param_; @@ -201,100 +210,72 @@ class EffectParamHandlerForSamplersGLES2 : public EffectParamHandlerGLES2 { template <typename T> class EffectParamArrayHandlerGLES2 : public EffectParamHandlerGLES2 { public: - explicit EffectParamArrayHandlerGLES2(ParamParamArray* param) - : param_(param) { + typedef typename T::DataType DataType; + EffectParamArrayHandlerGLES2(ParamParamArray* param, GLsizei size) + : param_(param), + size_(size) { + values_.reset(new DataType[size]); } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void SetEffectParam( + RendererGLES2* renderer, GLES2Parameter gl_param) { ParamArray* param = param_->value(); if (param) { - int size = cgGetArraySize(cg_param, 0); - if (size != static_cast<int>(param->size())) { + if (size_ != static_cast<int>(param->size())) { O3D_ERROR(param->service_locator()) << "number of params in ParamArray does not match number of params " << "needed by shader array"; } else { - for (int i = 0; i < size; ++i) { + for (int i = 0; i < size_; ++i) { Param* untyped_element = param->GetUntypedParam(i); // TODO(gman): Make this check happen when building the param cache. // To do that would require that ParamParamArray mark it's owner // as changed if a Param in it's ParamArray changes. if (untyped_element->IsA(T::GetApparentClass())) { - CGparameter cg_element = cgGetArrayParameter(cg_param, i); - SetElement(cg_element, down_cast<T*>(untyped_element)); + SetElement(down_cast<T*>(untyped_element), &values_[i]); } else { O3D_ERROR(param->service_locator()) << "Param in ParamArray at index " << i << " is not a " << T::GetApparentClassName(); } } + SetElements(gl_param, size_, values_.get()); } } } - void SetElement(CGparameter cg_element, T* param); - - private: - ParamParamArray* param_; -}; - -template <bool column_major> -class EffectParamArrayMatrix4HandlerGLES2 : public EffectParamHandlerGLES2 { - public: - explicit EffectParamArrayMatrix4HandlerGLES2(ParamParamArray* param) - : param_(param) { - } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { - ParamArray* param = param_->value(); - if (param) { - int size = cgGetArraySize(cg_param, 0); - if (size != static_cast<int>(param->size())) { - O3D_ERROR(param->service_locator()) - << "number of params in ParamArray does not match number of params " - << "needed by shader array"; - } else { - for (int i = 0; i < size; ++i) { - Param* untyped_element = param->GetUntypedParam(i); - // TODO(gman): Make this check happen when building the param cache. - // To do that would require that ParamParamArray mark it's owner - // as changed if a Param in it's ParamArray changes. - if (untyped_element->IsA(ParamMatrix4::GetApparentClass())) { - CGparameter cg_element = cgGetArrayParameter(cg_param, i); - SetElement(cg_element, down_cast<ParamMatrix4*>(untyped_element)); - } else { - O3D_ERROR(param->service_locator()) - << "Param in ParamArray at index " << i - << " is not a ParamMatrix4"; - } - } - } - } - } - void SetElement(CGparameter cg_element, ParamMatrix4* param); + void SetElement(T* param, DataType* value); + void SetElements(GLES2Parameter gl_param, GLsizei size, + const DataType* values); private: ParamParamArray* param_; + scoped_array<DataType> values_; + GLsizei size_; }; class EffectParamArraySamplerHandlerGLES2 : public EffectParamHandlerGLES2 { public: - explicit EffectParamArraySamplerHandlerGLES2(ParamParamArray* param) - : param_(param) { + explicit EffectParamArraySamplerHandlerGLES2(ParamParamArray* param, + GLsizei size) + : param_(param), + size_(size) { + values_.reset(new GLint[size]); } - virtual void SetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void SetEffectParam(RendererGLES2* renderer, + GLES2Parameter gl_param) { ParamArray* param = param_->value(); if (param) { - int size = cgGetArraySize(cg_param, 0); - if (size != static_cast<int>(param->size())) { + if (size_ != static_cast<int>(param->size())) { O3D_ERROR(param->service_locator()) << "number of params in ParamArray does not match number of params " << "needed by shader array"; } else { - for (int i = 0; i < size; ++i) { + for (int i = 0; i < size_; ++i) { + GLint handle = 0; Param* untyped_element = param->GetUntypedParam(i); // TODO(gman): Make this check happen when building the param cache. // To do that would require that ParamParamArray mark it's owner // as changed if a Param in it's ParamArray changes. if (untyped_element->IsA(ParamSampler::GetApparentClass())) { - CGparameter cg_element = cgGetArrayParameter(cg_param, i); ParamSampler* element = down_cast<ParamSampler*>(untyped_element); SamplerGLES2* sampler_gl = down_cast<SamplerGLES2*>(element->value()); @@ -308,32 +289,33 @@ class EffectParamArraySamplerHandlerGLES2 : public EffectParamHandlerGLES2 { << "' index " << i; } } - sampler_gl->SetTextureAndStates(cg_element); + handle = sampler_gl->SetTextureAndStates(gl_param); } else { O3D_ERROR(param->service_locator()) << "Param in ParamArray at index " << i << " is not a ParamSampler"; } + values_[i] = handle; } + glUniform1iv(gl_param, size_, values_.get()); } } } - virtual void ResetEffectParam(RendererGLES2* renderer, CGparameter cg_param) { + virtual void ResetEffectParam(RendererGLES2* renderer, + GLES2Parameter gl_param) { ParamArray* param = param_->value(); if (param) { - int size = cgGetArraySize(cg_param, 0); - if (size == static_cast<int>(param->size())) { - for (int i = 0; i < size; ++i) { + if (size_ == static_cast<int>(param->size())) { + for (int i = 0; i < size_; ++i) { Param* untyped_element = param->GetUntypedParam(i); if (untyped_element->IsA(ParamSampler::GetApparentClass())) { - CGparameter cg_element = cgGetArrayParameter(cg_param, i); ParamSampler* element = down_cast<ParamSampler*>(untyped_element); SamplerGLES2* sampler_gl = down_cast<SamplerGLES2*>(element->value()); if (!sampler_gl) { sampler_gl = down_cast<SamplerGLES2*>(renderer->error_sampler()); } - sampler_gl->ResetTexture(cg_element); + sampler_gl->ResetTexture(gl_param); } } } @@ -342,129 +324,206 @@ class EffectParamArraySamplerHandlerGLES2 : public EffectParamHandlerGLES2 { private: ParamParamArray* param_; + scoped_array<GLint> values_; + GLsizei size_; }; template<> void EffectParamArrayHandlerGLES2<ParamFloat>::SetElement( - CGparameter cg_element, - ParamFloat* param) { - cgSetParameter1f(cg_element, param->value()); + ParamFloat* param, + float* value) { + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamFloat>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const float* values) { + glUniform1fv(gl_param, count, values); } template<> void EffectParamArrayHandlerGLES2<ParamFloat2>::SetElement( - CGparameter cg_element, - ParamFloat2* param) { - Float2 f = param->value(); - cgSetParameter2fv(cg_element, f.GetFloatArray()); + ParamFloat2* param, + Float2* value) { + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamFloat2>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const Float2* values) { + glUniform2fv(gl_param, count, values[0].GetFloatArray()); } template<> void EffectParamArrayHandlerGLES2<ParamFloat3>::SetElement( - CGparameter cg_element, - ParamFloat3* param) { - Float3 f = param->value(); - cgSetParameter3fv(cg_element, f.GetFloatArray()); + ParamFloat3* param, + Float3* value) { + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamFloat3>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const Float3* values) { + glUniform3fv(gl_param, count, values[0].GetFloatArray()); } template<> void EffectParamArrayHandlerGLES2<ParamFloat4>::SetElement( - CGparameter cg_element, - ParamFloat4* param) { - Float4 f = param->value(); - cgSetParameter4fv(cg_element, f.GetFloatArray()); + ParamFloat4* param, + Float4* value) { + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamFloat4>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const Float4* values) { + glUniform4fv(gl_param, count, values[0].GetFloatArray()); +} + +class ColumnMajorParamMatrix4 : public ParamMatrix4 { +}; + +class RowMajorParamMatrix4 : public ParamMatrix4 { +}; + +template<> +void EffectParamArrayHandlerGLES2<RowMajorParamMatrix4>::SetElement( + RowMajorParamMatrix4* param, + Matrix4* value) { + // set the data as floats in row major order. + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<RowMajorParamMatrix4>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const Matrix4* values) { + glUniformMatrix4fv(gl_param, count, GL_FALSE, + reinterpret_cast<const GLfloat*>(values_.get())); } template<> -void EffectParamArrayMatrix4HandlerGLES2<false>::SetElement( - CGparameter cg_element, - ParamMatrix4* param) { +void EffectParamArrayHandlerGLES2<ColumnMajorParamMatrix4>::SetElement( + ColumnMajorParamMatrix4* param, + Matrix4* value) { // set the data as floats in row major order. - Matrix4 mat = param->value(); - cgSetMatrixParameterfr(cg_element, &mat[0][0]); + *value = transpose(param->value()); } template<> -void EffectParamArrayMatrix4HandlerGLES2<true>::SetElement( - CGparameter cg_element, - ParamMatrix4* param) { - // set the data as floats in column major order. - Matrix4 mat = param->value(); - cgSetMatrixParameterfc(cg_element, &mat[0][0]); +void EffectParamArrayHandlerGLES2<ColumnMajorParamMatrix4>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const Matrix4* values) { + // NOTE: The transpose happens in the function above because GLES2 requires + // the transpose argument to this function to be GL_FALSE. + glUniformMatrix4fv(gl_param, count, GL_FALSE, + reinterpret_cast<const GLfloat*>(values_.get())); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamBoolean>::SetElement( + ParamBoolean* param, + bool* value) { + *value = param->value(); +} + +template<> +void EffectParamArrayHandlerGLES2<ParamBoolean>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const bool* values) { + scoped_array<GLint> local(new GLint[count]); + for (GLsizei ii = 0; ii < count; ++ii) { + local[ii] = values[ii]; + } + glUniform1iv(gl_param, count, local.get()); } template<> void EffectParamArrayHandlerGLES2<ParamInteger>::SetElement( - CGparameter cg_element, - ParamInteger* param) { - cgSetParameter1i(cg_element, param->value()); + ParamInteger* param, + int* value) { + *value = param->value(); } template<> -void EffectParamArrayHandlerGLES2<ParamBoolean>::SetElement( - CGparameter cg_element, - ParamBoolean* param) { - cgSetParameter1i(cg_element, param->value()); +void EffectParamArrayHandlerGLES2<ParamInteger>::SetElements( + GLES2Parameter gl_param, + GLsizei count, + const int* values) { + glUniform1iv(gl_param, count, values); } -static EffectParamHandlerGLES2::Ref GetHandlerFromParamAndCgType( +static EffectParamHandlerGLES2::Ref GetHandlerFromParamAndGLType( EffectGLES2* effect_gl, Param *param, - CGtype cg_type) { + GLenum gl_type, + GLsizei size) { EffectParamHandlerGLES2::Ref handler; if (param->IsA(ParamParamArray::GetApparentClass())) { ParamParamArray* param_param_array = down_cast<ParamParamArray*>(param); - switch (cg_type) { - case CG_FLOAT: - case CG_FLOAT1: + switch (gl_type) { + case GL_FLOAT: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamFloat>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamFloat>(param_param_array, + size)); break; - case CG_FLOAT2: + case GL_FLOAT_VEC2: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamFloat2>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamFloat2>(param_param_array, + size)); break; - case CG_FLOAT3: + case GL_FLOAT_VEC3: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamFloat3>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamFloat3>(param_param_array, + size)); break; - case CG_FLOAT4: + case GL_FLOAT_VEC4: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamFloat4>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamFloat4>(param_param_array, + size)); break; - case CG_FLOAT4x4: + case GL_FLOAT_MAT4: if (effect_gl->matrix_load_order() == Effect::COLUMN_MAJOR) { handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayMatrix4HandlerGLES2<true>(param_param_array)); + new EffectParamArrayHandlerGLES2<ColumnMajorParamMatrix4>( + param_param_array, size)); } else { handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayMatrix4HandlerGLES2<false>( - param_param_array)); + new EffectParamArrayHandlerGLES2<RowMajorParamMatrix4>( + param_param_array, size)); } break; - case CG_INT: - case CG_INT1: + case GL_INT: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamInteger>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamInteger>(param_param_array, + size)); break; - case CG_BOOL: - case CG_BOOL1: + case GL_BOOL: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArrayHandlerGLES2<ParamBoolean>(param_param_array)); + new EffectParamArrayHandlerGLES2<ParamBoolean>(param_param_array, + size)); break; - case CG_SAMPLER: - case CG_SAMPLER1D: - case CG_SAMPLER2D: - case CG_SAMPLER3D: - case CG_SAMPLERCUBE: + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: handler = EffectParamHandlerGLES2::Ref( - new EffectParamArraySamplerHandlerGLES2(param_param_array)); + new EffectParamArraySamplerHandlerGLES2(param_param_array, size)); break; default: break; } } else if (param->IsA(ParamMatrix4::GetApparentClass())) { - if (cg_type == CG_FLOAT4x4) { + if (gl_type == GL_FLOAT_MAT4) { if (effect_gl->matrix_load_order() == Effect::COLUMN_MAJOR) { // set the data as floats in column major order. handler = EffectParamHandlerGLES2::Ref( @@ -478,48 +537,44 @@ static EffectParamHandlerGLES2::Ref GetHandlerFromParamAndCgType( } } } else if (param->IsA(ParamFloat::GetApparentClass())) { - if (cg_type == CG_FLOAT || - cg_type == CG_FLOAT1) { + if (gl_type == GL_FLOAT) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamFloat>( down_cast<ParamFloat*>(param))); } } else if (param->IsA(ParamFloat2::GetApparentClass())) { - if (cg_type == CG_FLOAT2) { + if (gl_type == GL_FLOAT_VEC2) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamFloat2>( down_cast<ParamFloat2*>(param))); } } else if (param->IsA(ParamFloat3::GetApparentClass())) { - if (cg_type == CG_FLOAT3) { + if (gl_type == GL_FLOAT_VEC3) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamFloat3>( down_cast<ParamFloat3*>(param))); } } else if (param->IsA(ParamFloat4::GetApparentClass())) { - if (cg_type == CG_FLOAT4) { + if (gl_type == GL_FLOAT_VEC4) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamFloat4>( down_cast<ParamFloat4*>(param))); } } else if (param->IsA(ParamInteger::GetApparentClass())) { - if (cg_type == CG_INT || cg_type == CG_INT1) { + if (gl_type == GL_INT) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamInteger>( down_cast<ParamInteger*>(param))); } } else if (param->IsA(ParamBoolean::GetApparentClass())) { - if (cg_type == CG_BOOL || cg_type == CG_BOOL1) { + if (gl_type == GL_BOOL) { handler = EffectParamHandlerGLES2::Ref( new TypedEffectParamHandlerGLES2<ParamBoolean>( down_cast<ParamBoolean*>(param))); } } else if (param->IsA(ParamSampler::GetApparentClass())) { - if (cg_type == CG_SAMPLER || - cg_type == CG_SAMPLER1D || - cg_type == CG_SAMPLER2D || - cg_type == CG_SAMPLER3D || - cg_type == CG_SAMPLERCUBE) { + if (gl_type == GL_SAMPLER_2D || + gl_type == GL_SAMPLER_CUBE) { handler = EffectParamHandlerGLES2::Ref( new EffectParamHandlerForSamplersGLES2( down_cast<ParamSampler*>(param))); @@ -530,28 +585,23 @@ static EffectParamHandlerGLES2::Ref GetHandlerFromParamAndCgType( // Local helper function for scanning varying Cg parameters of a // program or effect and recording their entries into the varying map. -static void ScanVaryingParameters(CGprogram program, - CGenum name_space, +static void ScanVaryingParameters(GLuint gl_program, ParamCacheGLES2* param_cache_gl) { - CGparameter cg_param = cgGetFirstLeafParameter(program, name_space); - for (; cg_param; cg_param = cgGetNextLeafParameter(cg_param)) { - if (!cgIsParameterReferenced(cg_param)) - continue; - CGenum variability = cgGetParameterVariability(cg_param); - CGenum direction = cgGetParameterDirection(cg_param); - if (variability == CG_VARYING && direction == CG_IN) { - // Add a link between the parameter and no stream (index -1) - // NOTE: Stream indexes will be set later in - // InsertMissingVertexStreams(). - if (param_cache_gl->varying_map().find(cg_param) == - param_cache_gl->varying_map().end()) { - const char* cg_name = cgGetParameterName(cg_param); - param_cache_gl->varying_map().insert(std::make_pair(cg_param, -1)); - DLOG(INFO) << "ElementGLES2 Found CG_VARYING \"" - << cg_name << " : " - << cgGetParameterSemantic(cg_param) << "\""; - } - } + GLint num_attribs = 0; + GLint max_len = 0; + glGetProgramiv(gl_program, GL_ACTIVE_ATTRIBUTES, &num_attribs); + glGetProgramiv(gl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); + // TODO(gman): Should we check for error? + scoped_array<char> name_buffer(new char[max_len + 1]); + for (GLint ii = 0; ii < num_attribs; ++ii) { + GLsizei length; + GLsizei size; + GLenum type; + glGetActiveAttrib( + gl_program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); + // TODO(gman): Should we check for error? + GLint location = glGetAttribLocation(gl_program, name_buffer.get()); + param_cache_gl->varying_map().insert(std::make_pair(ii, location)); } } @@ -559,160 +609,94 @@ static void ScanVaryingParameters(CGprogram program, // program or effect and recording their entries into the parameter maps. static void ScanUniformParameters(SemanticManager* semantic_manager, Renderer* renderer, - CGprogram program, - CGenum name_space, + GLuint gl_program, ParamCacheGLES2* param_cache_gl, const ParamObjectList& param_objects, EffectGLES2* effect_gl) { - CGparameter cg_param = cgGetFirstParameter(program, name_space); - for (; cg_param; cg_param = cgGetNextParameter(cg_param)) { - if (!cgIsParameterReferenced(cg_param)) - continue; - CGenum direction = cgGetParameterDirection(cg_param); - if (direction != CG_IN) - continue; - CGtype cg_type = cgGetParameterType(cg_param); - CGenum variability = cgGetParameterVariability(cg_param); - const char* cg_name = cgGetParameterName(cg_param); - - if (variability == CG_UNIFORM) { - // We have a CGparameter to add, find a Param that matches it by name. - if (cg_type == CG_TEXTURE) { - // CG_TEXTURE objects are handled by CG_SAMPLER objects. - continue; - } - - // TODO(o3d): The following code block should be removed once we start - // creating sampler params for all effects coming in via the importer. For - // the time being, we keep an extra ParamTexture that does the job it used - // to do. If we are using a ParamSampler on the object then the - // ParamTexture will have no value and therefore its handler will have no - // side-effects. - if (cg_type == CG_SAMPLER || - cg_type == CG_SAMPLER1D || - cg_type == CG_SAMPLER2D || - cg_type == CG_SAMPLER3D || - cg_type == CG_SAMPLERCUBE) { - // Uniform is a Sampler object. Find the CG_TEXTURE object - // assigned to the CG_SAMPLER, then find a Param object with the - // same name as the CG_TEXTURE. This is the tricky bit! - if (param_cache_gl->sampler_map().find(cg_param) == - param_cache_gl->sampler_map().end()) { - ParamTexture* param = - effect_gl->GetTextureParamFromCgSampler(cg_param, - param_objects); - if (param) { - param_cache_gl->sampler_map().insert(std::make_pair(cg_param, - param)); - } - } + GLint num_uniforms = 0; + GLint max_len = 0; + glGetProgramiv(gl_program, GL_ACTIVE_UNIFORMS, &num_uniforms); + glGetProgramiv(gl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len); + // TODO(gman): Should we check for error? + scoped_array<char> name_buffer(new char[max_len + 1]); + for (GLint ii = 0; ii < num_uniforms; ++ii) { + GLsizei length; + GLsizei size; + GLenum gl_type; + glGetActiveUniform( + gl_program, ii, + max_len + 1, &length, &size, &gl_type, name_buffer.get()); + // TODO(gman): Should we check for error? + GLint location = glGetUniformLocation(gl_program, name_buffer.get()); + String name(name_buffer.get(), length); + + // Find a Param of the same name, and record the link. + if (param_cache_gl->uniform_map().find(ii) == + param_cache_gl->uniform_map().end()) { + const ObjectBase::Class *sem_class = NULL; + // Try looking by SAS class name. + String semantic = GetUniformSemantic(ii, name); + if (!semantic.empty()) { + sem_class = semantic_manager->LookupSemantic(semantic); } - - // Find a Param of the same name, and record the link. - if (param_cache_gl->uniform_map().find(cg_param) == - param_cache_gl->uniform_map().end()) { - const ObjectBase::Class *sem_class = NULL; - // Try looking by SAS class name. - const char* cg_semantic = cgGetParameterSemantic(cg_param); - if (cg_semantic != NULL && cg_semantic[0] != '\0') { - // NOTE: this semantic is not the regularised profile semantic - // output from the CGC compiler but the actual user supplied - // semantic from the shader source code, so this match is valid. - sem_class = semantic_manager->LookupSemantic(cg_semantic); + EffectParamHandlerGLES2::Ref handler; + // Look through all the param objects to find a matching param. + unsigned last = param_objects.size() - 1; + for (unsigned int i = 0; i < param_objects.size(); ++i) { + ParamObject *param_object = param_objects[i]; + Param *param = param_object->GetUntypedParam(name); + if (!param && sem_class) { + param = param_object->GetUntypedParam(sem_class->name()); } - EffectParamHandlerGLES2::Ref handler; - // Look through all the param objects to find a matching param. - unsigned last = param_objects.size() - 1; - for (unsigned int i = 0; i < param_objects.size(); ++i) { - ParamObject *param_object = param_objects[i]; - Param *param = param_object->GetUntypedParam(cg_name); - if (!param && sem_class) { - param = param_object->GetUntypedParam(sem_class->name()); - } - if (!param) { - // If this is the last param object and we didn't find a matching - // param then if it's a sampler use the error sampler - if (i == last) { - if (cg_type == CG_SAMPLER || - cg_type == CG_SAMPLER1D || - cg_type == CG_SAMPLER2D || - cg_type == CG_SAMPLER3D || - cg_type == CG_SAMPLERCUBE) { - param = - renderer->error_param_sampler(); - } - } - if (!param) { - continue; + if (!param) { + // If this is the last param object and we didn't find a matching + // param then if it's a sampler use the error sampler + if (i == last) { + if (gl_type == GL_SAMPLER_2D || gl_type == GL_SAMPLER_CUBE) { + param = renderer->error_param_sampler(); } } - if (cg_type == CG_ARRAY) { - // Substitute the first element's type for our type. - cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); - } - handler = GetHandlerFromParamAndCgType(effect_gl, param, cg_type); - if (!handler.IsNull()) { - param_cache_gl->uniform_map().insert( - std::make_pair(cg_param, handler)); - DLOG(INFO) << "ElementGLES2 Matched CG_PARAMETER \"" - << cg_name << "\" to Param \"" - << param->name() << "\" from \"" - << param_object->name() << "\""; - break; - } else { - // We found a param, but it didn't match the type. keep looking. - DLOG(ERROR) << "ElementGLES2 Param \"" - << param->name() << "\" type \"" - << param->GetClassName() << "\" from \"" - << param_object->name() - << "\" does not match CG_PARAMETER \"" - << cg_name << "\""; + if (!param) { + continue; } } - if (handler.IsNull()) { - DLOG(ERROR) << "No matching Param for CG_PARAMETER \"" - << cg_name << "\""; + handler = GetHandlerFromParamAndGLType(effect_gl, param, gl_type, size); + if (!handler.IsNull()) { + param_cache_gl->uniform_map().insert( + std::make_pair(location, handler)); + DLOG(INFO) << "ElementGLES2 Matched gl_paramETER \"" + << name << "\" to Param \"" + << param->name() << "\" from \"" + << param_object->name() << "\""; + break; + } else { + // We found a param, but it didn't match the type. keep looking. + DLOG(ERROR) << "ElementGLES2 Param \"" + << param->name() << "\" type \"" + << param->GetClassName() << "\" from \"" + << param_object->name() + << "\" does not match gl_paramETER \"" + << name << "\""; } } + if (handler.IsNull()) { + DLOG(ERROR) << "No matching Param for gl_paramETER \"" + << name << "\""; + } } } } -static void DoScanCgEffectParameters(SemanticManager* semantic_manager, +static void DoScanGLEffectParameters(SemanticManager* semantic_manager, Renderer* renderer, ParamCacheGLES2* param_cache_gl, - CGprogram cg_vertex, - CGprogram cg_fragment, + GLuint gl_program, EffectGLES2* effect_gl, const ParamObjectList& param_objects) { - ScanVaryingParameters(cg_vertex, CG_PROGRAM, param_cache_gl); - ScanVaryingParameters(cg_vertex, CG_GLOBAL, param_cache_gl); + ScanVaryingParameters(gl_program, param_cache_gl); ScanUniformParameters(semantic_manager, renderer, - cg_vertex, - CG_PROGRAM, - param_cache_gl, - param_objects, - effect_gl); - ScanUniformParameters(semantic_manager, - renderer, - cg_vertex, - CG_GLOBAL, - param_cache_gl, - param_objects, - effect_gl); - // Do not record varying inputs for a fragment program - ScanUniformParameters(semantic_manager, - renderer, - cg_fragment, - CG_PROGRAM, - param_cache_gl, - param_objects, - effect_gl); - ScanUniformParameters(semantic_manager, - renderer, - cg_fragment, - CG_GLOBAL, + gl_program, param_cache_gl, param_objects, effect_gl); @@ -720,32 +704,25 @@ static void DoScanCgEffectParameters(SemanticManager* semantic_manager, // Search the leaf parameters of a CGeffect and it's shaders for parameters // using cgGetFirstEffectParameter() / cgGetFirstLeafParameter() / -// cgGetNextLeafParameter(). Add the CGparameters found to the parameter +// cgGetNextLeafParameter(). Add the GLES2Parameters found to the parameter // maps on the DrawElement. -void ParamCacheGLES2::ScanCgEffectParameters(CGprogram cg_vertex, - CGprogram cg_fragment, +void ParamCacheGLES2::ScanGLEffectParameters(GLuint gl_program, ParamObject* draw_element, ParamObject* element, Material* material, ParamObject* override) { - DLOG(INFO) << "DrawElementGLES2 ScanCgEffectParameters"; + DLOG(INFO) << "DrawElementGLES2 ScanGLEffectParameters"; DLOG_ASSERT(material); DLOG_ASSERT(draw_element); DLOG_ASSERT(element); EffectGLES2* effect_gl = static_cast<EffectGLES2*>(material->effect()); DLOG_ASSERT(effect_gl); - if (cg_vertex == NULL) { - DLOG(ERROR) << "Can't scan an empty Vertex Program for Cg Parameters."; + if (gl_program == 0) { + DLOG(ERROR) << "Can't scan an empty Program for Parameters."; return; } - if (cg_fragment == NULL) { - DLOG(ERROR) << "Can't scan an empty Fragment Program for Cg Parameters."; - return; - } - uniform_map_.clear(); varying_map_.clear(); - sampler_map_.clear(); ParamObjectList param_object_list; param_object_list.push_back(override); param_object_list.push_back(draw_element); @@ -753,11 +730,10 @@ void ParamCacheGLES2::ScanCgEffectParameters(CGprogram cg_vertex, param_object_list.push_back(material); param_object_list.push_back(effect_gl); param_object_list.push_back(semantic_manager_->sas_param_object()); - DoScanCgEffectParameters(semantic_manager_, + DoScanGLEffectParameters(semantic_manager_, renderer_, this, - cg_vertex, - cg_fragment, + gl_program, effect_gl, param_object_list); } diff --git a/o3d/core/cross/gles2/param_cache_gles2.h b/o3d/core/cross/gles2/param_cache_gles2.h index 2d1d771..f3c193e 100644 --- a/o3d/core/cross/gles2/param_cache_gles2.h +++ b/o3d/core/cross/gles2/param_cache_gles2.h @@ -49,9 +49,10 @@ class ParamCacheGLES2 : public ParamCache { public: ParamCacheGLES2(SemanticManager* semantic_manager, Renderer* renderer); - typedef std::map<CGparameter, int> VaryingParameterMap; - typedef std::map<CGparameter, EffectParamHandlerGLES2::Ref> UniformParameterMap; - typedef std::map<CGparameter, ParamTexture*> SamplerParameterMap; + typedef std::map<GLES2Parameter, int> VaryingParameterMap; + typedef std::map<GLES2Parameter, + EffectParamHandlerGLES2::Ref> UniformParameterMap; + typedef std::map<GLES2Parameter, ParamTexture*> SamplerParameterMap; // Overridden from ParamCache. virtual void UpdateCache(Effect* effect, @@ -62,7 +63,6 @@ class ParamCacheGLES2 : public ParamCache { VaryingParameterMap& varying_map() { return varying_map_; } UniformParameterMap& uniform_map() { return uniform_map_; } - SamplerParameterMap& sampler_map() { return sampler_map_; } protected: // Overridden from ParamCache @@ -70,33 +70,25 @@ class ParamCacheGLES2 : public ParamCache { virtual bool ValidateEffect(Effect* effect); private: - SemanticManager* semantic_manager_; Renderer* renderer_; - // Records the last two shaders used on this cache, allowing us to rescan the - // shader parameters if the user changes the shader on an active cache. - CGprogram last_vertex_program_; - CGprogram last_fragment_program_; - - // Search the leaf parameters of a CGeffect and it's shaders for - // parameters using cgGetFirstEffectParameter() / - // cgGetFirstLeafParameter() / cgGetNextLeafParameter(). Add the - // CGparameters found to the parameter maps on the DrawElement. - void ScanCgEffectParameters(CGprogram cg_vertex, - CGprogram fragment, + // Used to track if the shader on the Effect has changed and + // therefore we need to rebuild our cache. + int last_compile_count_; + + // Search the leaf parameters of a GLSL program for parameters and add the + // parameters found to the parameter maps on the DrawElement. + void ScanGLEffectParameters(GLuint gl_program, ParamObject* draw_element, ParamObject* element, Material* material, ParamObject* override); - // A map of varying CGparameter to Stream index. + // A map of varying GLES2Parameter to Stream index. VaryingParameterMap varying_map_; - // A map of uniform CGparameter to Param objects. + // A map of uniform GLES2Parameter to Param objects. UniformParameterMap uniform_map_; - // A map of uniform CG_SAMPLER CGparameters to ParamTexture objects. - // TODO(o3d): remove this (OLD path for textures). - SamplerParameterMap sampler_map_; }; } // o3d diff --git a/o3d/core/cross/gles2/primitive_gles2.cc b/o3d/core/cross/gles2/primitive_gles2.cc index ec63fdb..e229833 100644 --- a/o3d/core/cross/gles2/primitive_gles2.cc +++ b/o3d/core/cross/gles2/primitive_gles2.cc @@ -43,7 +43,6 @@ #include "core/cross/gles2/draw_element_gles2.h" #include "core/cross/gles2/stream_bank_gles2.h" #include "core/cross/gles2/utils_gles2-inl.h" -#include "Cg/cgGL.h" // Someone defines min, conflicting with std::min #ifdef min @@ -94,7 +93,7 @@ void PrimitiveGLES2::PlatformSpecificRender(Renderer* renderer, // If this PrimitiveGLES2 has an effect we haven't seen before (or it's the // first time through), initalize the parameter lists before drawing with it. - if (effect_gl->cg_vertex_program() && effect_gl->cg_fragment_program()) { + if (effect_gl->gl_program()) { // Set up the current CGeffect. if (!param_cache_gl->ValidateAndCacheParams(effect_gl, draw_element_gl, @@ -102,17 +101,15 @@ void PrimitiveGLES2::PlatformSpecificRender(Renderer* renderer, stream_bank_gl, material, override)) { - Stream::Semantic missing_semantic; - int missing_semnatic_index; + String missing_stream; if (!stream_bank_gl->CheckForMissingVertexStreams( varying_map, - &missing_semantic, - &missing_semnatic_index)) { + effect_gl->gl_program(), + &missing_stream)) { param_cache_gl->ClearParamCache(); O3D_ERROR(service_locator()) << "Required Stream " - << Stream::GetSemanticDescription(missing_semantic) << ":" - << missing_semnatic_index << " missing on Primitive '" << name() + << missing_stream << " missing on Primitive '" << name() << "' using Material '" << material->name() << "' with Effect '" << effect_gl->name() << "'"; return; @@ -131,7 +128,9 @@ void PrimitiveGLES2::PlatformSpecificRender(Renderer* renderer, stream_bank_gl->UpdateStreams(); unsigned int max_vertices; - if (!stream_bank_gl->BindStreamsForRendering(varying_map, &max_vertices)) { + if (!stream_bank_gl->BindStreamsForRendering(varying_map, + effect_gl->gl_program(), + &max_vertices)) { return; } @@ -235,13 +234,14 @@ void PrimitiveGLES2::PlatformSpecificRender(Renderer* renderer, if (draw) { DCHECK_NE(gl_primitive_type, static_cast<unsigned int>(GL_NONE)); renderer->AddPrimitivesRendered(number_primitives_); - if (indexed()) + if (indexed()) { glDrawElements(gl_primitive_type, index_count, GL_UNSIGNED_INT, - BUFFER_OFFSET(start_index() * sizeof(uint32))); // NOLINT - else + BufferOffset(start_index() * sizeof(uint32))); // NOLINT + } else { glDrawArrays(gl_primitive_type, start_index(), index_count); + } } // Clean up the shaders. @@ -251,7 +251,7 @@ void PrimitiveGLES2::PlatformSpecificRender(Renderer* renderer, for (ParamCacheGLES2::VaryingParameterMap::iterator i = varying_map.begin(); i != varying_map.end(); ++i) { - cgGLDisableClientState(i->first); + glDisableVertexAttribArray(i->first); } CHECK_GL_ERROR(); } diff --git a/o3d/core/cross/gles2/renderer_gles2.cc b/o3d/core/cross/gles2/renderer_gles2.cc index da5f5e7..6d012e2 100644 --- a/o3d/core/cross/gles2/renderer_gles2.cc +++ b/o3d/core/cross/gles2/renderer_gles2.cc @@ -31,8 +31,7 @@ // This file contains the definition of the RendererGLES2 class that -// implements the abstract Renderer API using OpenGLES2 and the Cg -// Runtime. +// implements the abstract Renderer API using OpenGLES2. #include "core/cross/gles2/renderer_gles2.h" @@ -546,7 +545,6 @@ RendererGLES2::RendererGLES2(ServiceLocator* service_locator) mac_cgl_context_(0), #endif render_surface_framebuffer_(0), - cg_context_(NULL), alpha_function_ref_changed_(true), alpha_function_(GL_ALWAYS), alpha_ref_(0.f), @@ -714,33 +712,9 @@ Renderer::InitStatus RendererGLES2::InitCommonGLES2() { if (!GLEW_VERSION_2_0 && !GLEW_EXT_blend_equation_separate) { DLOG(ERROR) << "Separate blend function extension missing."; } - // create a Cg Runtime. - cg_context_ = cgCreateContext(); - DLOG_CG_ERROR("Creating Cg context"); - // NOTE: the first CGerror number after the recreation of a - // CGcontext (the second time through) seems to be trashed. Please - // ignore any "CG ERROR: Invalid context handle." message on this - // function - Invalid context handle isn't one of therror states of - // cgCreateContext(). DLOG(INFO) << "OpenGLES2 Vendor: " << ::glGetString(GL_VENDOR); DLOG(INFO) << "OpenGLES2 Renderer: " << ::glGetString(GL_RENDERER); DLOG(INFO) << "OpenGLES2 Version: " << ::glGetString(GL_VERSION); - DLOG(INFO) << "Cg Version: " << cgGetString(CG_VERSION); - cg_vertex_profile_ = cgGLGetLatestProfile(CG_GL_VERTEX); - cgGLSetOptimalOptions(cg_vertex_profile_); - DLOG(INFO) << "Best Cg vertex profile = " - << cgGetProfileString(cg_vertex_profile_); - cg_fragment_profile_ = cgGLGetLatestProfile(CG_GL_FRAGMENT); - cgGLSetOptimalOptions(cg_fragment_profile_); - DLOG(INFO) << "Best Cg fragment profile = " - << cgGetProfileString(cg_fragment_profile_); - // Set up all Cg State Assignments for OpenGLES2. - cgGLRegisterStates(cg_context_); - DLOG_CG_ERROR("Registering GLES2 StateAssignments"); - cgGLSetDebugMode(CG_FALSE); - // Enable the profiles we use. - cgGLEnableProfile(CG_PROFILE_ARBVP1); - cgGLEnableProfile(CG_PROFILE_ARBFP1); // get some limits for this profile. GLint max_vertex_attribs = 0; ::glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); @@ -770,11 +744,6 @@ void RendererGLES2::DestroyCommonGLES2() { if (render_surface_framebuffer_) { ::glDeleteFramebuffersEXT(1, &render_surface_framebuffer_); } - - if (cg_context_) { - cgDestroyContext(cg_context_); - cg_context_ = NULL; - } } #ifdef OS_WIN @@ -1000,7 +969,7 @@ Renderer::InitStatus RendererGLES2::InitPlatformSpecific( return SUCCESS; } -// Releases the Cg Context and deletes the GLES2 device. +// Deletes the GLES2 device. void RendererGLES2::Destroy() { DLOG(INFO) << "Destroy RendererGLES2"; DestroyCommonGLES2(); @@ -1497,7 +1466,7 @@ IndexBuffer::Ref RendererGLES2::CreateIndexBuffer() { Effect::Ref RendererGLES2::CreateEffect() { DLOG(INFO) << "RendererGLES2 CreateEffect"; MakeCurrentLazy(); - return Effect::Ref(new EffectGLES2(service_locator(), cg_context_)); + return Effect::Ref(new EffectGLES2(service_locator())); } Sampler::Ref RendererGLES2::CreateSampler() { diff --git a/o3d/core/cross/gles2/renderer_gles2.h b/o3d/core/cross/gles2/renderer_gles2.h index dcf04fa..eeb7260 100644 --- a/o3d/core/cross/gles2/renderer_gles2.h +++ b/o3d/core/cross/gles2/renderer_gles2.h @@ -31,8 +31,7 @@ // This file contains the definition of the RendererGLES2 class that provides -// low-level access for O3D to graphics hardware using the OpenGLES2 API -// and Cg Runtime. +// low-level access for O3D to graphics hardware using the OpenGLES2 API. #ifndef O3D_CORE_CROSS_GLES2_RENDERER_GLES2_H_ #define O3D_CORE_CROSS_GLES2_RENDERER_GLES2_H_ @@ -51,8 +50,7 @@ class Effect; class DrawEffect; class SemanticManager; -// Implements the genereric Renderer interface using OpenGLES2 and the Cg -// Runtime. +// Implements the genereric Renderer interface using OpenGLES2. class RendererGLES2 : public Renderer { public: // Creates a default Renderer. @@ -158,9 +156,20 @@ class RendererGLES2 : public Renderer { // Makes this renderer active on the current thread. bool MakeCurrent(); - inline CGcontext cg_context() const { return cg_context_; } - inline CGprofile cg_vertex_profile() const { return cg_vertex_profile_; } - inline CGprofile cg_fragment_profile() const { return cg_fragment_profile_; } + // Called by EffectGLES2::PrepareForDraw before setting any parameters. + void ResetTextureGroupSetCount() { + ++texture_unit_group_set_count_; + next_texture_unit_ = 0; + } + + int GetTextureGroupSetCount() { + return texture_unit_group_set_count_; + } + + // Samplers call this if their texture group set count is out of date. + GLenum GetNextTextureUnit() { + return next_texture_unit_++; + } protected: // Keep the constructor protected so only factory methods can create @@ -243,7 +252,6 @@ class RendererGLES2 : public Renderer { // Indicates we're rendering fullscreen rather than in the plugin region. bool fullscreen_; - #ifdef OS_WIN // Handle to the GLES2 device. HWND window_; @@ -266,11 +274,6 @@ class RendererGLES2 : public Renderer { // targets. GLuint render_surface_framebuffer_; - // Cg Runtime variables. - CGcontext cg_context_; - CGprofile cg_vertex_profile_; - CGprofile cg_fragment_profile_; - friend class AlphaReferenceHandler; bool alpha_function_ref_changed_; GLenum alpha_function_; @@ -330,6 +333,13 @@ class RendererGLES2 : public Renderer { // Sets the stencils states for either front, back or both facing polys. void SetStencilStates(GLenum face, const StencilStates& stencil_states); + + // Sampler test against this to see if their cached texture unit is valid. + int texture_unit_group_set_count_; + + // The next texture unit to use. This is reset with ResetTextureUnit + // and retrieved with GetNextTextureUnit. + GLenum next_texture_unit_; }; } // namespace o3d diff --git a/o3d/core/cross/gles2/sampler_gles2.cc b/o3d/core/cross/gles2/sampler_gles2.cc index 502389c..a52affa 100644 --- a/o3d/core/cross/gles2/sampler_gles2.cc +++ b/o3d/core/cross/gles2/sampler_gles2.cc @@ -42,7 +42,9 @@ namespace o3d { SamplerGLES2::SamplerGLES2(ServiceLocator* service_locator) : Sampler(service_locator), renderer_(static_cast<RendererGLES2*>( - service_locator->GetService<Renderer>())) { + service_locator->GetService<Renderer>())), + texture_unit_group_set_count_(renderer_->GetTextureGroupSetCount() - 1), + texture_unit_(0) { } SamplerGLES2::~SamplerGLES2() { @@ -131,7 +133,7 @@ GLenum GLTextureTarget(Texture* texture) { } // namespace -void SamplerGLES2::SetTextureAndStates(CGparameter cg_param) { +GLint SamplerGLES2::SetTextureAndStates(GLES2Parameter gl_param) { // Get the texture object associated with this sampler. Texture* texture_object = texture(); @@ -151,65 +153,65 @@ void SamplerGLES2::SetTextureAndStates(CGparameter cg_param) { texture_object = renderer_->error_texture(); } - GLuint handle = static_cast<GLuint>(reinterpret_cast<intptr_t>( + GLint handle = static_cast<GLint>(reinterpret_cast<intptr_t>( texture_object->GetTextureHandle())); if (handle) { - cgGLSetTextureParameter(cg_param, handle); - cgGLEnableTextureParameter(cg_param); - } else { - cgGLSetTextureParameter(cg_param, 0); - cgGLDisableTextureParameter(cg_param); - return; - } - - // TODO(o3d): this is a slow check and needs to be moved to initialization - // time. - GLenum target = GLTextureTarget(texture_object); - - if (target) { - GLenum texture_unit = cgGLGetTextureEnum(cg_param); - ::glActiveTextureARB(texture_unit); - glBindTexture(target, handle); - glTexParameteri(target, - GL_TEXTURE_WRAP_S, - GLAddressMode(address_mode_u(), GL_REPEAT)); - glTexParameteri(target, - GL_TEXTURE_WRAP_T, - GLAddressMode(address_mode_v(), GL_REPEAT)); - if (texture_object->IsA(TextureCUBE::GetApparentClass())) { + // TODO(o3d): this is a slow check and needs to be moved to initialization + // time. + GLenum target = GLTextureTarget(texture_object); + if (target) { + int renderer_texture_unit_group_set_count = + renderer_->GetTextureGroupSetCount(); + if (renderer_texture_unit_group_set_count != + texture_unit_group_set_count_) { + texture_unit_group_set_count_ = renderer_texture_unit_group_set_count; + texture_unit_ = renderer_->GetNextTextureUnit(); + } + ::glActiveTextureARB(GL_TEXTURE0 + texture_unit_); + glBindTexture(target, handle); glTexParameteri(target, - GL_TEXTURE_WRAP_R, - GLAddressMode(address_mode_w(), GL_REPEAT)); - } - glTexParameteri(target, - GL_TEXTURE_MIN_FILTER, - GLMinFilter(min_filter(), mip_filter())); - glTexParameteri(target, - GL_TEXTURE_MAG_FILTER, - GLMagFilter(mag_filter())); - - Float4 color = border_color(); - GLfloat gl_color[4] = {color[0], color[1], color[2], color[3]}; - glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, gl_color); - - // Check for anisotropic texture filtering. - if (GLEW_EXT_texture_filter_anisotropic) { - int gl_max_anisotropy = - (min_filter() == ANISOTROPIC) ? max_anisotropy() : 1; - glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); + GL_TEXTURE_WRAP_S, + GLAddressMode(address_mode_u(), GL_REPEAT)); + glTexParameteri(target, + GL_TEXTURE_WRAP_T, + GLAddressMode(address_mode_v(), GL_REPEAT)); + if (texture_object->IsA(TextureCUBE::GetApparentClass())) { + glTexParameteri(target, + GL_TEXTURE_WRAP_R, + GLAddressMode(address_mode_w(), GL_REPEAT)); + } + glTexParameteri(target, + GL_TEXTURE_MIN_FILTER, + GLMinFilter(min_filter(), mip_filter())); + glTexParameteri(target, + GL_TEXTURE_MAG_FILTER, + GLMagFilter(mag_filter())); + + Float4 color = border_color(); + GLfloat gl_color[4] = {color[0], color[1], color[2], color[3]}; + glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, gl_color); + + // Check for anisotropic texture filtering. + if (GLEW_EXT_texture_filter_anisotropic) { + int gl_max_anisotropy = + (min_filter() == ANISOTROPIC) ? max_anisotropy() : 1; + glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, + gl_max_anisotropy); + } } } + + return texture_unit_; } -void SamplerGLES2::ResetTexture(CGparameter cg_param) { +void SamplerGLES2::ResetTexture(GLES2Parameter gl_param) { Texture* the_texture = texture(); if (the_texture) { // TODO(o3d): this is a slow check and needs to be moved to initialization // time. GLenum target = GLTextureTarget(the_texture); if (target) { - GLenum texture_unit = cgGLGetTextureEnum(cg_param); - glActiveTextureARB(texture_unit); + glActiveTextureARB(GL_TEXTURE0 + texture_unit_); glBindTexture(target, 0); } } diff --git a/o3d/core/cross/gles2/sampler_gles2.h b/o3d/core/cross/gles2/sampler_gles2.h index d388473..1562c8c 100644 --- a/o3d/core/cross/gles2/sampler_gles2.h +++ b/o3d/core/cross/gles2/sampler_gles2.h @@ -41,6 +41,9 @@ namespace o3d { class RendererGLES2; +// TODO(gman): Replace. +typedef GLuint GLES2Parameter; + // SamplerGLES2 is an implementation of the Sampler object for GLES2. class SamplerGLES2 : public Sampler { public: @@ -48,15 +51,21 @@ class SamplerGLES2 : public Sampler { virtual ~SamplerGLES2(); // Sets the gl texture and sampler states. - void SetTextureAndStates(CGparameter cg_param); + // Returns the handle to the texture. + GLint SetTextureAndStates(GLES2Parameter gl_param); // Unbinds the GLES2 texture. - void ResetTexture(CGparameter cg_param); + void ResetTexture(GLES2Parameter gl_param); private: - RendererGLES2* renderer_; + // We compare this to RendererGLES2::GetTextureGroupSetCount to see + // if we need to update our texture unit + int texture_unit_group_set_count_; + + GLenum texture_unit_; + DISALLOW_COPY_AND_ASSIGN(SamplerGLES2); }; } // namespace o3d diff --git a/o3d/core/cross/gles2/stream_bank_gles2.cc b/o3d/core/cross/gles2/stream_bank_gles2.cc index 8808c6b..1501f35 100644 --- a/o3d/core/cross/gles2/stream_bank_gles2.cc +++ b/o3d/core/cross/gles2/stream_bank_gles2.cc @@ -42,7 +42,6 @@ #include "core/cross/gles2/renderer_gles2.h" #include "core/cross/gles2/draw_element_gles2.h" #include "core/cross/gles2/utils_gles2-inl.h" -#include "Cg/cgGL.h" // Someone defines min, conflicting with std::min #ifdef min @@ -67,6 +66,29 @@ GLenum GLDataType(const Field& field) { return GL_INVALID_ENUM; } +String GetAttribName(GLuint gl_program, GLES2Parameter gl_param) { + GLchar buffer[1024]; + GLsizei name_len; + GLint size; + GLenum type; + glGetActiveAttrib(gl_program, + gl_param, + sizeof(buffer), + &name_len, + &size, + &type, + &buffer[0]); + if (name_len == 0) { + return String("**NO INFO FOR ATTRIB**"); + } + return String(&buffer[0], name_len); +} + +String GetAttribSemantic(GLuint gl_program, GLES2Parameter gl_param) { + // GLES doesn't have semantics so for now we just return the names. + return GetAttribName(gl_program, gl_param); +} + } // anonymous namespace // Number of times to log a repeated event before giving up. @@ -85,27 +107,28 @@ StreamBankGLES2::~StreamBankGLES2() { bool StreamBankGLES2::CheckForMissingVertexStreams( ParamCacheGLES2::VaryingParameterMap& varying_map, - Stream::Semantic* missing_semantic, - int* missing_semantic_index) { - DCHECK(missing_semantic); - DCHECK(missing_semantic_index); + GLuint gl_program, + String* missing_stream) { + DCHECK(missing_stream); DLOG(INFO) << "StreamBankGLES2 InsertMissingVertexStreams"; - // Match CG_VARYING parameters to Buffers with the matching semantics. + // Match VARYING parameters to Buffers with the matching semantics. ParamCacheGLES2::VaryingParameterMap::iterator i; for (i = varying_map.begin(); i != varying_map.end(); ++i) { - CGparameter cg_param = i->first; - const char* semantic_string = cgGetParameterSemantic(cg_param); - int attr = SemanticNameToGLVertexAttribute(semantic_string); - int index = 0; - Stream::Semantic semantic = GLVertexAttributeToStream(attr, &index); - int stream_index = FindVertexStream(semantic, index); + GLES2Parameter gl_param = i->first; + String semantic_string(GetAttribSemantic(gl_program, gl_param)); + Stream::Semantic semantic; + int semantic_index; + if (!SemanticNameToSemantic(semantic_string, &semantic, &semantic_index)) { + *missing_stream = semantic_string; + } + int stream_index = FindVertexStream(semantic, semantic_index); if (stream_index >= 0) { // record the matched stream into the varying parameter map for later // use by StreamBankGLES2::Draw(). i->second = stream_index; DLOG(INFO) - << "StreamBankGLES2 Matched CG_PARAMETER \"" - << cgGetParameterName(cg_param) << " : " + << "StreamBankGLES2 Matched PARAMETER \"" + << GetAttribName(gl_program, gl_param) << " : " << semantic_string << "\" to stream " << stream_index << " \"" << vertex_stream_params_.at( @@ -113,8 +136,7 @@ bool StreamBankGLES2::CheckForMissingVertexStreams( << "\""; } else { // no matching stream was found. - *missing_semantic = semantic; - *missing_semantic_index = index; + *missing_stream = semantic_string; return false; } } @@ -124,6 +146,7 @@ bool StreamBankGLES2::CheckForMissingVertexStreams( bool StreamBankGLES2::BindStreamsForRendering( const ParamCacheGLES2::VaryingParameterMap& varying_map, + GLuint gl_program, unsigned int* max_vertices) { *max_vertices = UINT_MAX; // Loop over varying params setting up the streams. @@ -152,8 +175,8 @@ bool StreamBankGLES2::BindStreamsForRendering( if (element_count > 4) { element_count = 0; DLOG_FIRST_N(ERROR, kNumLoggedEvents) - << "Unable to find stream for CGparameter: " - << cgGetParameterName(i->first); + << "Unable to find stream for attrib: " + << GetAttribName(gl_program, i->first); } // In the num_elements = 1 case we want to do the D3D stride = 0 thing. @@ -169,15 +192,16 @@ bool StreamBankGLES2::BindStreamsForRendering( // stride at the API level, and instead maybe provide a way to pss a // constant value - but the DX version relies on being able to pass a 0 // stride, so the whole thing needs a bit of rewrite. - cgGLDisableClientState(i->first); + glDisableVertexAttribArray(i->first); } else { glBindBufferARB(GL_ARRAY_BUFFER, vbuffer->gl_buffer()); - cgGLSetParameterPointer(i->first, - element_count, - GLDataType(field), - vbuffer->stride(), - BUFFER_OFFSET(field.offset())); - cgGLEnableClientState(i->first); + glVertexAttribPointer(i->first, + element_count, + GLDataType(field), + false, + vbuffer->stride(), + BufferOffset(field.offset())); + glEnableVertexAttribArray(i->first); *max_vertices = std::min(*max_vertices, stream.GetMaxVertices()); } } diff --git a/o3d/core/cross/gles2/stream_bank_gles2.h b/o3d/core/cross/gles2/stream_bank_gles2.h index 1e81dc5..3582069 100644 --- a/o3d/core/cross/gles2/stream_bank_gles2.h +++ b/o3d/core/cross/gles2/stream_bank_gles2.h @@ -56,13 +56,14 @@ class StreamBankGLES2 : public StreamBank { // true if all streams were bound. bool BindStreamsForRendering( const ParamCacheGLES2::VaryingParameterMap& varying_map, + GLuint gl_program, unsigned int* max_vertices); // Checks for all required streams before rendering. bool CheckForMissingVertexStreams( ParamCacheGLES2::VaryingParameterMap& varying_map, - Stream::Semantic* missing_semantic, - int* missing_semantic_index); + GLuint gl_program, + String* missing_stream); private: int FindVertexStream(Stream::Semantic semantic, int index); diff --git a/o3d/core/cross/gles2/utils_gles2-inl.h b/o3d/core/cross/gles2/utils_gles2-inl.h index e69b552..2e15e4c 100644 --- a/o3d/core/cross/gles2/utils_gles2-inl.h +++ b/o3d/core/cross/gles2/utils_gles2-inl.h @@ -37,40 +37,17 @@ namespace o3d { -// Define this to debug GLES2 errors. This has a significant performance hit. -// #define GL_ERROR_DEBUGGING - // convert a byte offset into a Vertex Buffer Object into a GLvoid* for // use with glVertexPointer(), glNormalPointer(), glVertexAttribPointer(), // etc. after having used a glBindBuffer(). -#define BUFFER_OFFSET(i) (reinterpret_cast<char *>(NULL)+(i)) - -// Writes any Cg errors to the log with a descriptive message. -// NOTE: macros used to make sure the LOG calls note the correct -// line number and source file. -#define DLOG_CG_ERROR(message) { \ - CGerror error = cgGetError(); \ - DLOG_IF(INFO, error != CG_NO_ERROR) \ - << message << " : " \ - << cgGetErrorString(error); \ -} - -// Writes any Cg errors to the log with a descriptive message, along with -// the error messages from the CGC compiler. -#define DLOG_CG_COMPILER_ERROR(message, cg_context) { \ - CGerror error = cgGetError(); \ - DLOG_IF(ERROR, error == CG_NO_ERROR) \ - << message << " : " << cgGetErrorString(error); \ - if (error == CG_COMPILER_ERROR) { \ - DLOG(ERROR) << "CGC compiler output :\n" \ - << cgGetLastListing(cg_context); \ - } \ +inline GLvoid* BufferOffset(unsigned i) { + return static_cast<int8 *>(NULL)+(i); } #ifdef GL_ERROR_DEBUGGING #define CHECK_GL_ERROR() do { \ GLenum gl_error = glGetError(); \ - LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GLES2 Error :" << gl_error; \ + LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \ } while(0) #else // GL_ERROR_DEBUGGING #define CHECK_GL_ERROR() void(0) diff --git a/o3d/core/cross/gles2/utils_gles2.cc b/o3d/core/cross/gles2/utils_gles2.cc index 696a067..1d48958 100644 --- a/o3d/core/cross/gles2/utils_gles2.cc +++ b/o3d/core/cross/gles2/utils_gles2.cc @@ -43,187 +43,38 @@ namespace o3d { -typedef std::pair<String, int> SemanticMapElement; -typedef std::map<String, int> SemanticMap; - -// The map batween the semantics on vertex program varying parameters names -// and vertex attribute indices under the VP_30 profile. -SemanticMapElement semantic_map_vp_30[] = { - SemanticMapElement("POSITION", 0), - SemanticMapElement("ATTR0", 0), - SemanticMapElement("BLENDWEIGHT", 1), - SemanticMapElement("ATTR1", 1), - SemanticMapElement("NORMAL", 2), - SemanticMapElement("ATTR2", 2), - SemanticMapElement("COLOR0", 3), - SemanticMapElement("DIFFUSE", 3), - SemanticMapElement("ATTR3", 3), - SemanticMapElement("COLOR1", 4), - SemanticMapElement("SPECULAR", 4), - SemanticMapElement("ATTR4", 4), - SemanticMapElement("TESSFACTOR", 5), - SemanticMapElement("FOGCOORD", 5), - SemanticMapElement("ATTR5", 5), - SemanticMapElement("PSIZE", 6), - SemanticMapElement("ATTR6", 6), - SemanticMapElement("BLENDINDICES", 7), - SemanticMapElement("ATTR7", 7), - SemanticMapElement("TEXCOORD0", 8), - SemanticMapElement("ATTR8", 8), - SemanticMapElement("TEXCOORD1", 9), - SemanticMapElement("ATTR9", 9), - SemanticMapElement("TEXCOORD2", 10), - SemanticMapElement("ATTR10", 10), - SemanticMapElement("TEXCOORD3", 11), - SemanticMapElement("ATTR11", 11), - SemanticMapElement("TEXCOORD4", 12), - SemanticMapElement("ATTR12", 12), - SemanticMapElement("TEXCOORD5", 13), - SemanticMapElement("ATTR13", 13), - SemanticMapElement("TEXCOORD6", 14), - SemanticMapElement("TANGENT", 14), - SemanticMapElement("ATTR14", 14), - SemanticMapElement("TEXCOORD7", 15), - SemanticMapElement("BINORMAL", 15), - SemanticMapElement("ATTR15", 15), -}; - -// The map batween the semantics on vertex program varying parameters names -// and vertex attribute indices under the VP_40 profile. -SemanticMapElement semantic_map_vp_40[] = { - SemanticMapElement("POSITION", 0), - SemanticMapElement("POSITION0", 0), - SemanticMapElement("ATTR0", 0), - SemanticMapElement("BLENDWEIGHT", 1), - SemanticMapElement("BLENDWEIGHT0", 1), - SemanticMapElement("ATTR1", 1), - SemanticMapElement("NORMAL", 2), - SemanticMapElement("NORMAL0", 2), - SemanticMapElement("ATTR2", 2), - SemanticMapElement("COLOR", 3), - SemanticMapElement("COLOR0", 3), - SemanticMapElement("DIFFUSE", 3), - SemanticMapElement("ATTR3", 3), - SemanticMapElement("COLOR1", 4), - SemanticMapElement("SPECULAR", 4), - SemanticMapElement("ATTR4", 4), - SemanticMapElement("TESSFACTOR", 5), - SemanticMapElement("FOGCOORD", 5), - SemanticMapElement("TESSFACTOR0", 5), - SemanticMapElement("FOGCOORD0", 5), - SemanticMapElement("ATTR5", 5), - SemanticMapElement("PSIZE", 6), - SemanticMapElement("PSIZE0", 6), - SemanticMapElement("ATTR6", 6), - SemanticMapElement("BLENDINDICES", 7), - SemanticMapElement("BLENDINDICES0", 7), - SemanticMapElement("ATTR7", 7), - SemanticMapElement("TEXCOORD", 8), - SemanticMapElement("TEXCOORD0", 8), - SemanticMapElement("ATTR8", 8), - SemanticMapElement("TEXCOORD1", 9), - SemanticMapElement("ATTR9", 9), - SemanticMapElement("TEXCOORD2", 10), - SemanticMapElement("ATTR10", 10), - SemanticMapElement("TEXCOORD3", 11), - SemanticMapElement("ATTR11", 11), - SemanticMapElement("TEXCOORD4", 12), - SemanticMapElement("ATTR12", 12), - SemanticMapElement("TEXCOORD5", 13), - SemanticMapElement("ATTR13", 13), - SemanticMapElement("TANGENT", 14), - SemanticMapElement("TANGENT0", 14), - SemanticMapElement("TEXCOORD6", 14), - SemanticMapElement("ATTR14", 14), - SemanticMapElement("BINORMAL", 15), - SemanticMapElement("BINORMAL0", 15), - SemanticMapElement("TEXCOORD7", 15), - SemanticMapElement("ATTR15", 15), -}; - -// The map batween OpenGLES2 Vertex Attribute indexes under the VP_40 profile -// to Stream::Semantic identifiers (with index offsets). -struct AttrMapElement { - AttrMapElement(Stream::Semantic s, int i) : semantic(s), index(i) {} - Stream::Semantic semantic; - int index; -}; -AttrMapElement attr_map_vp_40[] = { - AttrMapElement(Stream::POSITION, 0), - AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), - AttrMapElement(Stream::NORMAL, 0), - AttrMapElement(Stream::COLOR, 0), - AttrMapElement(Stream::COLOR, 1), - AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), - AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), - AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), - AttrMapElement(Stream::TEXCOORD, 0), - AttrMapElement(Stream::TEXCOORD, 1), - AttrMapElement(Stream::TEXCOORD, 2), - AttrMapElement(Stream::TEXCOORD, 3), - AttrMapElement(Stream::TEXCOORD, 4), - AttrMapElement(Stream::TEXCOORD, 5), - AttrMapElement(Stream::TANGENT, 0), - AttrMapElement(Stream::BINORMAL, 0), -}; - -// TODO(o3d): make this choice a runtime decision in RendererGLES2 -// initialisation. -static SemanticMap semantic_map(semantic_map_vp_40, - semantic_map_vp_40 + - sizeof(semantic_map_vp_40) / - sizeof(SemanticMapElement) ); - -// Converts a semantic string to an OpenGLES2 vertex attribute number using the -// standard VP_40 shader semantic mappings. If the semantic is not -// recognised, it returns an index of -1. -int SemanticNameToGLVertexAttribute(const char* semantic) { - SemanticMap::const_iterator i = semantic_map.find(semantic); - if (i == semantic_map.end()) { - return -1; - } - return i->second; -} - -// Given a vertex attribute stream, convert it to a Stream::Semantic number -// and index. This is an imprecise operation. -Stream::Semantic GLVertexAttributeToStream(const unsigned int attr, - int *index) { - // kMaxAttrIndex is available from: - // glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); - // - // TODO(o3d): make this a runtime provided value discovered during - // Renderer creation. - const unsigned int kMaxAttrIndex = 15u; - if (attr > kMaxAttrIndex) { - //TODO(o3d): Figure out how to get errors out of here to the client. - DLOG(ERROR) << "Invalid vertex attribute index."; - *index = 0; - return Stream::UNKNOWN_SEMANTIC; - } - *index = attr_map_vp_40[attr].index; - return attr_map_vp_40[attr].semantic; -} - -#ifdef OS_WIN - -// Given a CGcontext object, check to see if any errors have occurred since -// the last Cg API call, and report the message and any compiler errors (if -// necessary). -inline void CheckForCgError(const String& logmessage, CGcontext cg_context) { - CGerror error = CG_NO_ERROR; - const char *error_string = cgGetLastErrorString(&error); - if (error == CG_NO_ERROR) { - return; - } else { - DLOG(ERROR) << logmessage << ": " << error_string; - if (error == CG_COMPILER_ERROR) { - DLOG(ERROR) << "Compiler message:\n" << cgGetLastListing(cg_context); +bool SemanticNameToSemantic( + const String& name, Stream::Semantic* semantic, int* semantic_index) { + struct NameToSemantic { + const char* const name; + size_t length; + const Stream::Semantic semantic; + }; + static const char kPosition[] = "POSITION"; + static const char kNormal[] = "NORMAL"; + static const char kTangent[] = "TANGENT"; + static const char kBinormal[] = "BINORMAL"; + static const char kColor[] = "COLOR"; + static const char kTexcoord[] = "TEXCOORD"; + + static const NameToSemantic lookup[] = { + { kPosition, sizeof(kPosition) - 1, Stream::POSITION, }, + { kNormal, sizeof(kNormal) - 1, Stream::NORMAL, }, + { kTangent, sizeof(kTangent) - 1, Stream::TANGENT, }, + { kBinormal, sizeof(kBinormal) - 1, Stream::BINORMAL, }, + { kColor, sizeof(kColor) - 1, Stream::COLOR, }, + { kTexcoord, sizeof(kTexcoord) - 1, Stream::TEXCOORD, }, + }; + for (unsigned ii = 0; ii < arraysize(lookup); ++ii) { + const NameToSemantic& info = lookup[ii]; + if (!_strnicmp(info.name, name.c_str(), info.length)) { + *semantic = info.semantic; + *semantic_index = atoi(name.c_str() + info.length); + return true; } } + return false; } -#endif - } // namespace o3d diff --git a/o3d/core/cross/gles2/utils_gles2.h b/o3d/core/cross/gles2/utils_gles2.h index 4eb71c0..113b857 100644 --- a/o3d/core/cross/gles2/utils_gles2.h +++ b/o3d/core/cross/gles2/utils_gles2.h @@ -38,9 +38,8 @@ namespace o3d { -bool GetGLProcedures(); -int SemanticNameToGLVertexAttribute(const char* semantic); -Stream::Semantic GLVertexAttributeToStream(const unsigned int attr, int *index); +bool SemanticNameToSemantic( + const String& name, Stream::Semantic* semantic, int* semantic_index); } // namespace o3d diff --git a/o3d/core/cross/renderer_test.cc b/o3d/core/cross/renderer_test.cc index 057e624..427777f 100644 --- a/o3d/core/cross/renderer_test.cc +++ b/o3d/core/cross/renderer_test.cc @@ -93,9 +93,7 @@ TEST_F(RendererTest, InitAndDestroyRenderer) { RendererGL* gl_renderer = down_cast<RendererGL*>(renderer.get()); EXPECT_TRUE(gl_renderer->cg_context() != NULL); #elif defined(RENDERER_GLES2) - // test that the Cg Context was correctly created RendererGLES2* gles2_renderer = down_cast<RendererGLES2*>(renderer.get()); - EXPECT_TRUE(gles2_renderer->cg_context() != NULL); #endif // destroy the renderer renderer->Destroy(); @@ -107,8 +105,6 @@ TEST_F(RendererTest, InitAndDestroyRenderer) { // check that the renderer no longer has a Cg Context. EXPECT_FALSE(gl_renderer->cg_context() != NULL); #elif defined(RENDERER_GLES2) - // check that the renderer no longer has a Cg Context. - EXPECT_FALSE(gles2_renderer->cg_context() != NULL); #endif } diff --git a/o3d/plugin/idl/client.idl b/o3d/plugin/idl/client.idl index f8810e7..5b1634f 100644 --- a/o3d/plugin/idl/client.idl +++ b/o3d/plugin/idl/client.idl @@ -119,7 +119,7 @@ class ClientInfo { For testing purposes you can force O3D to use the software renderer by setting the environment variable O3D_FORCE_SOFTWARE_RENDERER to - anything. + anything. \code set O3D_FORCE_SOFTWARE_RENDERER=foo @@ -130,9 +130,9 @@ class ClientInfo { \endcode You can set it at a system level if you want to set it for all - browser instances or set it from a command line and start your + browser instances or set it from a command line and start your browser from that same command line if you want to effect just - that instance of the browser. + that instance of the browser. Note that many browers require special command line options to run in a separate process, otherwise they default to finding @@ -151,6 +151,11 @@ class ClientInfo { a non power of 2 texture. %] [getter] bool non_power_of_two_textures; + + %[ + True if shaders need to be GLSL instead of Cg/HLSL. + %] + [getter] bool glsl; }; %[ diff --git a/o3d/plugin/plugin.gyp b/o3d/plugin/plugin.gyp index 1104157..48b9498 100644 --- a/o3d/plugin/plugin.gyp +++ b/o3d/plugin/plugin.gyp @@ -112,7 +112,6 @@ { 'dependencies': [ '../build/libs.gyp:gles2_libs', - '../build/libs.gyp:cg_libs', ], }, ], @@ -325,7 +324,6 @@ { 'dependencies': [ '../build/libs.gyp:gles2_libs', - '../build/libs.gyp:cg_libs', ], }, ], diff --git a/o3d/samples/hellocube-colors-glsl.html b/o3d/samples/hellocube-colors-glsl.html new file mode 100644 index 0000000..10cf735 --- /dev/null +++ b/o3d/samples/hellocube-colors-glsl.html @@ -0,0 +1,343 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. +--> + +<!-- +This sample creates an O3D area with a spinning cube. The user +can change the color of the cube by clicking on one of the buttons that appear +at the bottom of the page. This sample demonstrates how to use O3D Params +to change the values of uniform parameters used by shaders. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Hello Square Colors: Getting started with O3D, take 2. +</title> +<script type="text/javascript" src="o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page has is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing + +var g_cubeTransform; +var g_cubeColorParam; + +/** + * Changes the color of the cube. + * @param red red component of the color + * @param green green component of the color + * @param blue blue component of the color + */ +function changeColor(red, green, blue) { + // Set the rgb color values and alpha = 1 + g_cubeColorParam.value = [red, green, blue, 1]; +} + +/** + * Creates an O3D shape representing a cube. The shape consists of + * a single primitive with eight vertices and 12 triangles (two for each face + * of the cube). + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles. There's eight vertices in total which + // are shared between the face triangles. + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + cubePrimitive.numberVertices = 8; // 8 vertices in total + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 8 corners of the cube. + var positionArray = [ + -0.5, -0.5, 0.5, // vertex 0 + 0.5, -0.5, 0.5, // vertex 1 + -0.5, 0.5, 0.5, // vertex 2 + 0.5, 0.5, 0.5, // vertex 3 + -0.5, 0.5, -0.5, // vertex 4 + 0.5, 0.5, -0.5, // vertex 5 + -0.5, -0.5, -0.5, // vertex 6 + 0.5, -0.5, -0.5 // vertex 7 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 2, 1 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, // face 1 + 2, 1, 3, + 2, 3, 4, // face 2 + 4, 3, 5, + 4, 5, 6, // face 3 + 6, 5, 7, + 6, 7, 0, // face 4 + 0, 7, 1, + 1, 7, 3, // face 5 + 3, 7, 5, + 6, 0, 4, // face 6 + 4, 0, 2 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions Buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.util.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the cube and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var shaderString = document.getElementById('effect').value; + cubeEffect.loadFromFXString(shaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the color parameter from the material and set its value to red. + g_cubeColorParam = cubeMaterial.getParam('color'); + g_cubeColorParam.value = [1, 0, 0, 1]; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Parent the cube's transform to the client root. + g_cubeTransform.parent = g_client.root; + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + g_finished = true; // for selenium testing. +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Square: Colors</h1> +This example shows how to use parameters to the color output of a shader. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px;"></div> +<!-- End of O3D plugin --> + +<form name="default_form" action="#" method="get"> + <p> + Change color: + <input type="button" value="Red" onclick="changeColor(1,0,0);" /> + <input type="button" value="Green" onclick="changeColor(0,1,0);" /> + <input type="button" value="Blue" onclick="changeColor(0,0,1);" /> + <input type="button" value="Yellow" onclick="changeColor(1,1,0);" /> + <input type="button" value="Cyan" onclick="changeColor(0,1,1);" /> + <input type="button" value="Purple" onclick="changeColor(1,0,1);" /> + <input type="button" value="White" onclick="changeColor(1,1,1);" /> + </p> +</form> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="effect"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + } + +// This hack his here for now since O3D only allows one string +// to be passed to Effect::loadFromFXString +// #o3d SplitMarker +// #o3d MatrixLoadOrder RowMajor + + // Color to draw with. + uniform vec4 color; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = color; + } + + // Here we tell our effect file *which* functions are + // our vertex and pixel shaders. + +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/hellocube-glsl.html b/o3d/samples/hellocube-glsl.html new file mode 100644 index 0000000..ac89206 --- /dev/null +++ b/o3d/samples/hellocube-glsl.html @@ -0,0 +1,304 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. +--> + +<!-- +This sample shows how to place an O3D area in a page and draw simple +3D shape in it. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Hello Cube: Getting started with O3D +</title> +<script type="text/javascript" src="o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page has is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_client; +var g_pack; +var g_clock = 0; +var g_timeMult = 1; +var g_cubeTransform; +var g_finished = false; // for selenium testing + +/** + * Creates an O3D shape representing a cube. The shape consists of + * a single primitive with eight vertices and 12 triangles (two for each face + * of the cube). + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles. There's eight vertices in total which + // are shared between the face triangles. + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + cubePrimitive.numberVertices = 8; // 8 vertices in total + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 8 corners of the cube. + var positionArray = [ + -0.5, -0.5, 0.5, // vertex 0 + 0.5, -0.5, 0.5, // vertex 1 + -0.5, 0.5, 0.5, // vertex 2 + 0.5, 0.5, 0.5, // vertex 3 + -0.5, 0.5, -0.5, // vertex 4 + 0.5, 0.5, -0.5, // vertex 5 + -0.5, -0.5, -0.5, // vertex 6 + 0.5, -0.5, -0.5 // vertex 7 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 2, 1 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, // face 1 + 2, 1, 3, + 2, 3, 4, // face 2 + 4, 3, 5, + 4, 5, 6, // face 3 + 6, 5, 7, + 6, 7, 0, // face 4 + 0, 7, 1, + 1, 7, 3, // face 5 + 3, 7, 5, + 6, 0, 4, // face 6 + 4, 0, 2 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions Buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + + +/** + * Creates the client area. + */ +function init() { + o3djs.util.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the cube and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt([0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var redEffect = g_pack.createObject('Effect'); + var shaderString = document.getElementById('effect').value; + redEffect.loadFromFXString(shaderString); + + // Create a Material for the mesh. + var redMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + redMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + redMaterial.effect = redEffect; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(redMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Parent the cube's transform to the client root. + g_cubeTransform.parent = g_client.root; + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + g_finished = true; // for selenium testing. +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Cube</h1> +This example shows how to display a spinning red cube in O3D. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px;"></div> +<!-- End of O3D plugin --> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="effect"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + } + +// This hack his here for now since O3D only allows one string +// to be passed to Effect::loadFromFXString +// #o3d SplitMarker +// #o3d MatrixLoadOrder RowMajor + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = vec4(1, 0, 0, 1); // Red. + } + + // Here we tell our effect file *which* functions are + // our vertex and pixel shaders. + +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/hellocube-textures-glsl.html b/o3d/samples/hellocube-textures-glsl.html new file mode 100644 index 0000000..86b8857 --- /dev/null +++ b/o3d/samples/hellocube-textures-glsl.html @@ -0,0 +1,433 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. +--> + +<!-- +This sample creates an O3D area with a textured cube in the middle. The +user can specify the URL where the texture image will be picked from. +This sample is a simple demonstration of texture usage in O3D. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Hello Square Textures: Getting started with O3D, take 3. +</title> +<script type="text/javascript" src="o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.io'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_cubeTransform; +var g_sampler; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing +var g_textureLoadDenied = false; // also for selenium testing + +/** + * Creates an O3D shape representing a cube. + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles (6 faces x 2 triangles per face) + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + + // Vertices used by each triangle must specify both a position and texture + // coordinates. We cannot share vertices between adjacent cube faces since + // while their positions are the same, their texture coordinates are + // different. We therefore create 24 vertices, 4 for each of the cube's + // six faces. + cubePrimitive.numberVertices = 24; + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 24 vertices used by the cube. + var positionArray = [ + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, 0.5, 0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5 + ]; + + // The following array stores the texture coordinates (u, v) for each vertex. + // These coordinates are used by the shader when displaying the texture image + // on the mesh triangles. + var texCoordsArray = [ + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 0, 2 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, + 0, 2, 3, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 13, 14, + 12, 14, 15, + 16, 17, 18, + 16, 18, 19, + 20, 21, 22, + 20, 22, 23 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var texCoordsBuffer = g_pack.createObject('VertexBuffer'); + var texCoordsField = texCoordsBuffer.createField('FloatField', 2); + texCoordsBuffer.set(texCoordsArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the texture coordinates buffer with the primitive. + streamBank.setVertexStream( + g_o3d.Stream.TEXCOORD, // semantic + 0, // semantic index + texCoordsField, // field + 0); // start_index + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.util.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the quad and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Set the texture URL. + var path = window.location.href; + var index = path.lastIndexOf('/'); + path = path.substring(0, index+1) + 'assets/texture_b3.jpg'; + var url = document.getElementById("url").value = path; + + // Initialize global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var shaderString = document.getElementById('effect').value; + cubeEffect.loadFromFXString(shaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the material's sampler parameter so that we can set the texture value + // to it. + var samplerParam = cubeMaterial.getParam('texSampler0'); + + // Create a Sampler object and set the min filtering to ANISOTROPIC. This + // will improve the quality of the rendered texture when viewed at an angle. + g_sampler = g_pack.createObject('Sampler'); + g_sampler.minFilter = g_o3d.Sampler.ANISOTROPIC; + g_sampler.maxAnisotropy = 4; + samplerParam.value = g_sampler; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Note that we don't parent the transform until the texture is + // succesfully loaded because we don't want the system + // to try the draw the shape without its required texture. + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + // Set the initial texture. + changeTexture(); +} + +/** + * Fetches the bitmap pointed to by the URL supplied by the user, creates + * an O3D Texture object with it updates the Sampler used by the material + * to point to the newly created texture. + */ +function changeTexture() { + var textureUrl = document.getElementById('url').value; + o3djs.io.loadTexture(g_pack, textureUrl, function(texture, exception) { + // Remove the currently used texture from the pack so that when it's not + // referenced anymore, it can get destroyed. + if (g_sampler.texture) + g_pack.removeObject(g_sampler.texture); + + // Because the loading is asynchronous an exception is not thrown but is + // instead passed on failure. + if (exception) { + g_sampler.texture = null; + + g_textureLoadDenied = true; // for selenium testing. + } else { + // Set the texture on the sampler object to the newly created texture + // object returned by the request. + g_sampler.texture = texture; + + // We can now safely add the cube transform to the root of the + // scenegraph since it now has a valid texture. If the transform + // is already parented under the root, the call will have no effect. + g_cubeTransform.parent = g_client.root; + + g_finished = true; // for selenium testing. + } + }); +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Cube: Textures</h1> +This example shows how texture map a cube using an image fetched from a URL. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px;"></div> +<!-- End of O3D plugin --> +<br /> +Image URL: <input type="text" id="url" size="100"> +<input type="button" id="updateButton" onclick="changeTexture();" value="Update Texture"><BR> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="effect"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + attribute vec2 texcoords0; + + varying vec2 uvs; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + uvs = texcoords0; + } + +// This hack his here for now since O3D only allows one string +// to be passed to Effect::loadFromFXString +// #o3d SplitMarker +// #o3d MatrixLoadOrder RowMajor + + // Color to draw with. + uniform sampler2D texSampler0; + + varying vec2 uvs; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = texture2D(texSampler0, uvs); + } +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/o3djs/base.js b/o3d/samples/o3djs/base.js index e7765c4..281f5bc 100644 --- a/o3d/samples/o3djs/base.js +++ b/o3d/samples/o3djs/base.js @@ -533,6 +533,12 @@ o3djs.base = o3djs.base || {}; o3djs.base.o3d = null; /** + * Whether or not we need to use GLSL instead of HLSL. + * @type {boolean} + */ +o3djs.base.glsl = false; + +/** * Snapshots the current state of all provided namespaces. This state will be * used to initialize future V8 instances. It is automatically * called by o3djs.util.makeClients. @@ -577,6 +583,9 @@ o3djs.base.initV8 = function(clientObject) { // Make sure this points to the o3d namespace for this particular // instance of the plugin. o3djs.base.o3d = plugin.o3d; + + // Save off if we need GLSL. + o3djs.base.glsl = plugin.client.clientInfo.glsl; }; clientObject.eval(v8Init.toString())(o3djs.v8Initializer_, @@ -616,6 +625,8 @@ o3djs.base.init = function(clientObject) { // the properties of an NPObject. // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5743 o3djs.base.o3d = o3djs.base.o3d || clientObject.o3d; + // Save off if we need GLSL. + o3djs.base.glsl = clientObject.client.clientInfo.glsl; }; /** diff --git a/o3d/standalone/standalone.gyp b/o3d/standalone/standalone.gyp index 535debf..c5a5d5b 100644 --- a/o3d/standalone/standalone.gyp +++ b/o3d/standalone/standalone.gyp @@ -58,7 +58,6 @@ ['renderer == "gles2"', { 'dependencies': [ - '../build/libs.gyp:cg_libs', '../build/libs.gyp:gles2_libs', ], }, diff --git a/o3d/tests/tests.gyp b/o3d/tests/tests.gyp index 6593ec1..67118a2 100644 --- a/o3d/tests/tests.gyp +++ b/o3d/tests/tests.gyp @@ -105,7 +105,6 @@ ['renderer == "gles2"', { 'dependencies': [ - '../build/libs.gyp:cg_libs', '../build/libs.gyp:gles2_libs', ], }, |