layer0/ShaderPrg.cpp (360 lines of code) (raw):
/*
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* copyright Schrodinger, LLC.
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information.
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-*
-*
-*
Z* -------------------------------------------------------------------
*/
#include "os_gl.h"
#include "os_python.h"
#include <string.h>
#include <iostream>
#include "ShaderMgr.h"
#include "OOMac.h"
#include "ListMacros.h"
#include "PyMOLOptions.h"
#include "Feedback.h"
#include "MemoryDebug.h"
#include "Setting.h"
#include "Scene.h"
#include "Color.h"
#include "Vector.h"
#include "Util.h"
#include "Util2.h"
#include "Texture.h"
#include "File.h"
#include "Matrix.h"
#include "Scene.h"
#include "MacPyMOL.h"
#include "Parse.h"
#ifdef _WEBGL
#include "Matrix.h"
#include "WebPyMOLLibrary.h"
#endif
#define MAX_LOG_LEN 1024
#include <sstream>
#include <stack>
#include <vector>
#define SCENEGETIMAGESIZE SceneGetWidthHeight
using namespace std;
const float mat3identity[] = { 1., 0., 0., 0., 1., 0., 0., 0., 1. };
#ifdef _PYMOL_ARB_SHADERS
GLboolean ProgramStringIsNative(PyMOLGlobals * G,
GLenum target, GLenum format,
const string& shaderstr)
{
GLint errorPos, isNative;
glProgramStringARB(target, format, shaderstr.length(), shaderstr.c_str());
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
glGetProgramivARB(target, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative);
if((errorPos == -1) && (isNative == 1))
return GL_TRUE;
else if(errorPos >= 0) {
if(Feedback(G, FB_OpenGL, FB_Errors)) {
printf("OpenGL-Error: ARB shader error at char %d\n---->%s\n", errorPos,
((char *) shaderstr.c_str()) + errorPos);
}
}
return GL_FALSE;
}
CShaderPrg *CShaderPrg::NewARB(PyMOLGlobals * G, const char * name, const string& vert, const string& frag) {
/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
if(!(glGenProgramsARB && glBindProgramARB &&
glDeleteProgramsARB && glProgramStringARB && glProgramEnvParameter4fARB)) {
glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) wglGetProcAddress("glGenProgramsARB");
glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) wglGetProcAddress("glBindProgramARB");
glDeleteProgramsARB =
(PFNGLDELETEPROGRAMSARBPROC) wglGetProcAddress("glDeleteProgramsARB");
glProgramStringARB =
(PFNGLPROGRAMSTRINGARBPROC) wglGetProcAddress("glProgramStringARB");
glProgramEnvParameter4fARB =
(PFNGLPROGRAMENVPARAMETER4FARBPROC) wglGetProcAddress("glProgramEnvParameter4fARB");
glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) wglGetProcAddress("glGetProgramivARB");
}
if(glGenProgramsARB && glBindProgramARB &&
glDeleteProgramsARB && glProgramStringARB && glProgramEnvParameter4fARB)
#endif
{
/* END PROPRIETARY CODE SEGMENT */
int ok = true;
GLuint programs[2];
glGenProgramsARB(2, programs);
/* load the vertex program */
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, programs[0]);
ok = ok && (ProgramStringIsNative(G, GL_VERTEX_PROGRAM_ARB,
GL_PROGRAM_FORMAT_ASCII_ARB, vert));
if(Feedback(G, FB_OpenGL, FB_Debugging))
PyMOLCheckOpenGLErr("loading vertex program");
/* load the fragment program */
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, programs[1]);
ok = ok && (ProgramStringIsNative(G, GL_FRAGMENT_PROGRAM_ARB,
GL_PROGRAM_FORMAT_ASCII_ARB, frag));
if(Feedback(G, FB_OpenGL, FB_Debugging))
PyMOLCheckOpenGLErr("loading fragment program");
if(ok) {
CShaderPrg * I = new CShaderPrg(G, name, "", "");
I->G = G;
I->vid = programs[0];
I->fid = programs[1];
G->ShaderMgr->AddShaderPrg(I);
return I;
} else {
glDeleteProgramsARB(2, programs);
}
}
return NULL;
}
int CShaderPrg::DisableARB() {
G->ShaderMgr->current_shader = 0;
glDisable(GL_FRAGMENT_PROGRAM_ARB);
glDisable(GL_VERTEX_PROGRAM_ARB);
return 1;
}
#endif
// -----------------------------------------------------------------------------
// Uniforms and Attributes
int CShaderPrg::Enable() {
if (!id) return 0;
/* linked? */
if (!IsLinked() && !Link()) {
// Link prints error message
return 0;
}
/* if so, use the program */
glUseProgram(id);
// uniform
Set1i("isPicking", SettingGetGlobal_b(G, cSetting_pick_shading) ? 1 : G->ShaderMgr->is_picking);
return 1;
}
int CShaderPrg::Disable() {
glUseProgram(0);
G->ShaderMgr->current_shader = 0;
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
return 1;
}
int CShaderPrg::IsLinked() {
GLint status = GL_FALSE;
if (is_linked) {
glGetProgramiv(id, GL_LINK_STATUS, &status);
}
return status==GL_TRUE;
}
int CShaderPrg::Link() {
int howLong;
glLinkProgram(id);
is_linked = true;
if (!IsLinked()) {
if (G && G->Option && !G->Option->quiet) {
GLint maxVarFloats;
int infoLogLength = 0;
#ifndef PURE_OPENGL_ES_2
glGetIntegerv(GL_MAX_VARYING_FLOATS, &maxVarFloats);
#endif
PRINTFB(G, FB_ShaderMgr, FB_Errors)
" CShaderPrg_Link-Error: Shader program failed to link name='%s'; GL_MAX_VARYING_FLOATS=%d log follows.\n", name.c_str(), maxVarFloats
ENDFB(G);
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLogLength);
if (!glGetError() && infoLogLength>0){
char *infoLog = Alloc(char, infoLogLength);
glGetProgramInfoLog(id, infoLogLength, &howLong, infoLog);
PRINTFB(G, FB_ShaderMgr, FB_Errors)
"%s\n", infoLog
ENDFB(G);
FreeP(infoLog);
}
}
return 0;
}
uniforms.clear();
return 1;
}
/*
* Get a uniform location id by name. Caches ids for faster lookup (avoiding
* expensive `glGetUniformLocation` calls).
*/
int CShaderPrg::GetUniformLocation(const char * name)
{
if (!id)
return -1;
auto uloc = uniforms.find(name);
if (uloc != uniforms.end())
return uloc->second;
GLint loc = glGetUniformLocation(id, name);
uniforms[name] = loc;
return loc;
}
/* accessors/mutators/uniform setters */
int CShaderPrg::Set1i(const char * name, int i)
{
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniform1i(loc, i);
return 1;
}
int CShaderPrg::Set3f(const char * name, float f1, float f2, float f3)
{
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniform3f(loc, f1, f2, f3);
return 1;
}
int CShaderPrg::Set2f(const char * name, float f1, float f2)
{
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniform2f(loc, f1, f2);
return 1;
}
/*
* Set column major 3x3 matrix
*/
int CShaderPrg::SetMat3fc(const char * name, const GLfloat * m){
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniformMatrix3fv(loc, 1, GL_FALSE, m);
return 1;
}
/*
* Set column major 4x4 matrix
*/
int CShaderPrg::SetMat4fc(const char * name, const GLfloat * m){
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniformMatrix4fv(loc, 1, GL_FALSE, m);
return 1;
}
int CShaderPrg::Set4fv(const char * name, const float *f){
return (Set4f(name, f[0], f[1], f[2], f[3]));
}
int CShaderPrg::Set3fv(const char * name, const float *f){
return (Set3f(name, f[0], f[1], f[2]));
}
int CShaderPrg::Set4f(const char * name, float f1, float f2, float f3, float f4)
{
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniform4f(loc, f1, f2, f3, f4);
return 1;
}
int CShaderPrg::Set1f(const char * name, float f)
{
GLint loc = GetUniformLocation(name);
if (loc < 0)
return 0;
glUniform1f(loc, f);
return 1;
}
int CShaderPrg::GetAttribLocation(const char * name)
{
GLint loc = -1;
if (id && name) {
loc = glGetAttribLocation(id, name);
if (loc < 0)
return -1;
}
return loc;
}
void CShaderPrg::SetAttrib4fLocation(const char * name, float f1, float f2, float f3, float f4){
if (id){
int attr = GetAttribLocation(name);
if (attr>=0){
glVertexAttrib4f(attr, f1, f2, f3, f4);
}
}
}
void CShaderPrg::SetAttrib1fLocation(const char * name, float f1){
if (id){
int attr = GetAttribLocation(name);
if (attr>=0){
glVertexAttrib1f(attr, f1);
}
}
}
void CShaderPrg::Invalidate() {
if (!id)
return;
if (gid){
glDetachShader(id, gid);
glDeleteShader(gid);
gid = 0;
}
if (vid){
glDetachShader(id, vid);
glDeleteShader(vid);
vid = 0;
}
if (fid){
glDetachShader(id, fid);
glDeleteShader(fid);
fid = 0;
}
glDeleteProgram(id);
id = 0;
}
void CShaderPrg::Set_Matrices() {
if (!(uniform_set & 2) &&
SettingGetGlobal_b(G, cSetting_precomputed_lighting)) {
Set1i("lightingTex", 1);
uniform_set |= 2;
}
const float * mvm = SceneGetModelViewMatrix(G);
// normalmatrix = transpose(inverse(mvm))
// Since we only support orthogonal normal matrices (e.g. with our sphere
// shader which uses a uniform radius), scaling by the length^2 of one row
// is sufficient.
float normalmatrix[9];
copy44f33f(mvm, normalmatrix);
float len_sq = lengthsq3f(normalmatrix);
for (int i = 0; i < 9; ++i)
normalmatrix[i] /= len_sq;
SetMat3fc("g_NormalMatrix", normalmatrix);
SetMat4fc("g_ModelViewMatrix", mvm);
SetMat4fc("g_ProjectionMatrix", SceneGetProjectionMatrix(G));
}
int CShaderPrg::SetLightingEnabled(int lighting_enabled){
return Set1i("lighting_enabled", lighting_enabled);
}
void CShaderPrg::Set_AnaglyphMode(int mode) {
extern float anaglyphR_constants[6][9];
extern float anaglyphL_constants[6][9];
/** Coefficients from: http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx */
/** anaglyph[R|L]_constants are found in Scene.c b/c of ray tracing */
SetMat3fc("matL", G->ShaderMgr->stereo_flag < 0 ?
anaglyphL_constants[mode] :
anaglyphR_constants[mode]);
Set1f("gamma", SettingGetGlobal_f(G, cSetting_gamma));
}
void CShaderPrg::Set_Stereo_And_AnaglyphMode() {
int stereo, stereo_mode;
stereo = SettingGetGlobal_i(G, cSetting_stereo);
stereo_mode = SettingGetGlobal_i(G, cSetting_stereo_mode);
if (stereo && stereo_mode==cStereo_anaglyph){
Set_AnaglyphMode(SettingGetGlobal_i(G, cSetting_anaglyph_mode));
} else {
SetMat3fc("matL", (GLfloat*)mat3identity);
Set1f("gamma", 1.0);
}
// for using one draw buffer, switch on which_pass between color and weights
if (TM3_IS_ONEBUF)
Set1f("which_pass", G->ShaderMgr->stereo_draw_buffer_pass ? 1.f : 0.f);
}
void CShaderPrg::Set_Specular_Values() {
auto trans_oblique = SettingGet<float>(G, cSetting_ray_transparency_oblique);
if (trans_oblique > R_SMALL4) {
Set1f("trans_oblique", trans_oblique);
Set1f("oblique_power", SettingGetGlobal_f(G, cSetting_ray_transparency_oblique_power));
}
if (SettingGetGlobal_b(G, cSetting_precomputed_lighting)) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, G->ShaderMgr->lightingTexture);
return;
}
SceneProgramLighting(G, this);
float settingSpecReflect, settingSpecDirect, settingSpecDirectPower, settingSpecPower;
SceneGetAdjustedLightValues(G,
&settingSpecReflect,
&settingSpecPower,
&settingSpecDirect,
&settingSpecDirectPower);
Set1f("spec_value_0", settingSpecDirect);
Set1f("shininess_0", settingSpecDirectPower);
Set1f("spec_value", settingSpecReflect);
Set1f("shininess", settingSpecPower);
}
void CShaderPrg::SetBgUniforms() {
auto bg_image_tilesize = SettingGet<const float*>(G, cSetting_bg_image_tilesize);
int bg_width, bg_height;
int scene_width, scene_height;
Set3fv("bgSolidColor", ColorGet(G, SettingGet_color(G, NULL, NULL, cSetting_bg_rgb)));
SceneGetWidthHeight(G, &scene_width, &scene_height);
OrthoGetBackgroundSize(G, &bg_width, &bg_height);
Set2f("tiledSize", bg_image_tilesize[0]/(float)scene_width, bg_image_tilesize[1]/(float)scene_height);
Set2f("tileSize", 1.f/(float)bg_image_tilesize[0], 1.f/(float)bg_image_tilesize[1]);
Set2f("viewImageSize", bg_width/(float)scene_width, bg_height/(float)scene_height);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, OrthoGetBackgroundTextureID(G));
if (!(uniform_set & 4)){
Set1i("bgTextureMap", 4);
uniform_set |= 4;
}
SceneSetFogUniforms(G, this);
if (SettingGet<bool>(G, cSetting_chromadepth) &&
!SettingGet<bool>(G, cSetting_orthoscopic)) {
Set2f("clippingplanes",
SceneGetCurrentFrontSafe(G),
SceneGetCurrentBackSafe(G));
}
}