diff --git a/src/js/GLmol.js b/src/js/GLmol.js
index cfe3914..88fc97c 100644
--- a/src/js/GLmol.js
+++ b/src/js/GLmol.js
@@ -1,5 +1,5 @@
/*
- GLmol - Molecular Viewer on WebGL/Javascript (0.40)
+ GLmol - Molecular Viewer on WebGL/Javascript (0.40 + VTF)
(C) Copyright 2011-2012, biochem_fan
License: dual license of MIT or LGPL3
@@ -31,6 +31,38 @@ THREE.Matrix4.prototype.isIdentity = function() {
return true;
};
+function hookShader() { // Lambert Shader and Line Shader ONLY
+// For texture-coordinates, see
+// http://stackoverflow.com/questions/5879403/opengl-texture-coordinates-in-pixel-space/5879551#5879551
+// This is the heart of this trick.
+// FaceColors, VertexColors: USE_COLOR is defined, color is passed as "color" attribute
+// Other cases: color is passed as "diffuse" uniform
+
+// USE_MAP has to be disabled; otherwise, mesh without uv will not be displayed.
+// Inject Color picker into vertex shader
+ THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace("void main", "#undef USE_MAP\n uniform sampler2D map; \n \n\nvec3 pickColor(vec3 color){\n int serial = int(color.b * 255.0 + color.g * 65280.0 + color.r * 16711680.0 + 0.01); float x = ((mod(float(serial), 512.0))* 2.0 + 1.0) / 1024.0, y = (float(serial / 512) * 2.0 + 1.0) / 1024.0;\n \n return texture2D(map, vec2(x, y)).rgb;\n} \n\n\nvoid main");
+// Hook Material color ("diffuse" uniform)
+ THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace(/\* diffuse/g, "* diffuse2");
+ THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace("vLightFront = vLightFront *", "///////////////////////////////////////\n\n#ifndef USE_COLOR\nvec3 diffuse2 = pickColor(diffuse).rgb;\n#else\nvec3 diffuse2 = diffuse;\n#endif\nvLightFront = vLightFront *");
+// Hook vertex color & face color ("color" attribute)
+ THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace("vColor = color;", "vColor = pickColor(color);");
+ THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace("vColor = color * color;", "vColor = pickColor(color) * pickColor(color);");
+// Disable normal texture mapping
+ THREE.ShaderLib.lambert.fragmentShader = THREE.ShaderLib.lambert.fragmentShader.replace("void main", "#undef USE_MAP\n void main");
+ THREE.ShaderLib.lambert.fragmentShader = THREE.ShaderLib.lambert.fragmentShader.replace("gl_FragColor = gl_FragColor * texture2D( map, vUv );", "");
+ THREE.ShaderLib.lambert.fragmentShader = THREE.ShaderLib.lambert.fragmentShader.replace("gl_FragColor = gl_FragColor * texelColor;", "a1");
+
+// Same for 'basic' shader
+// For this to work, WebGLRenderer.js has to be patched beforehand.
+ THREE.ShaderLib.basic.vertexShader = THREE.ShaderLib.basic.vertexShader.replace("void main", "uniform sampler2D map; \n \n\nvec3 pickColor(vec3 color){\n int serial = int(color.b * 255.0 + color.g * 65280.0 + color.r * 16711680.0 + 0.01); float x = ((mod(float(serial), 512.0))* 2.0 + 1.0) / 1024.0, y = (float(serial / 512) * 2.0 + 1.0) / 1024.0;\n \n return texture2D(map, vec2(x, y)).rgb;\n} \n\n\nvoid main");
+ THREE.ShaderLib.basic.vertexShader = THREE.ShaderLib.basic.vertexShader.replace(/\* diffuse/g, "* diffuse2");
+ THREE.ShaderLib.basic.vertexShader = THREE.ShaderLib.basic.vertexShader.replace("vLightFront = vLightFront *", "///////////////////////////////////////\n\n#ifndef USE_COLOR\nvec3 diffuse2 = pickColor(diffuse).rgb;\n#else\nvec3 diffuse2 = diffuse;\n#endif\nvLightFront = vLightFront *");
+ THREE.ShaderLib.basic.vertexShader = THREE.ShaderLib.basic.vertexShader.replace("vColor = color;", "vColor = pickColor(color);");
+ THREE.ShaderLib.basic.fragmentShader = THREE.ShaderLib.basic.fragmentShader.replace("gl_FragColor = gl_FragColor * texture2D( map, vUv );", "");
+}
+
+hookShader();
+
var GLmol = (function() {
function GLmol(id, suppressAutoload) {
this.Nucleotides = [' G', ' A', ' T', ' C', ' U', ' DG', ' DA', ' DT', ' DC', ' DU'];
@@ -113,11 +145,31 @@ function GLmol(id, suppressAutoload) {
this.currentModelPos = 0;
this.cz = 0;
this.enableMouse();
+ this.colortable = new Uint8Array(512 * 512 * 3);
+ this.colormap = new THREE.DataTexture(this.colortable, 512, 512, THREE.RGBFormat);
+// this.colormap.generateMipmaps = false;
+ this.colormap.onUpdate = function() {console.log('texture updated');};
+ this.colormap.magFilter = this.colormap.minFilter = THREE.NearestFilter;
+ this.colormap.generateMipmaps = false;
+ for (var i = 0; i < 512 * 512; i++) {
+ this.setColor(i, 0x0000ff);
+ }
if (suppressAutoload) return;
this.loadMolecule();
}
+GLmol.prototype.setColor = function(serial, color) {
+ var b = color % 256;
+ var g = Math.floor((color % 65536) / 256);
+ var r = Math.floor(color / 65536);
+ this.colortable[serial * 3] = r;
+ this.colortable[serial * 3 + 1] = g;
+ this.colortable[serial * 3 + 2] = b;
+ this.colormap.needsUpdate = true;
+}
+
+
GLmol.prototype.setupLights = function(scene) {
var directionalLight = new THREE.DirectionalLight(0xFFFFFF);
directionalLight.position = new TV3(0.2, 0.2, -1).normalize();
@@ -198,7 +250,7 @@ GLmol.prototype.parsePDB2 = function(str) {
else hetflag = false;
atoms[serial] = {'resn': resn, 'x': x, 'y': y, 'z': z, 'elem': elem,
'hetflag': hetflag, 'chain': chain, 'resi': resi, 'serial': serial, 'atom': atom,
- 'bonds': [], 'ss': 'c', 'color': 0xFFFFFF, 'bonds': [], 'bondOrder': [], 'b': b /*', altLoc': altLoc*/};
+ 'bonds': [], 'ss': 'c', 'color': serial, 'bonds': [], 'bondOrder': [], 'b': b /*', altLoc': altLoc*/};
} else if (recordName == 'SHEET ') {
var startChain = line.substr(21, 1);
var startResi = parseInt(line.substr(22, 4));
@@ -325,11 +377,12 @@ GLmol.prototype.subdivide = function(_points, DIV) { // points as Vector3
GLmol.prototype.drawAtomsAsSphere = function(group, atomlist, defaultRadius, forceDefault) {
var sphereGeometry = new THREE.SphereGeometry(1, this.sphereQuality, this.sphereQuality); // r, seg, ring
+ sphereGeometry.faceVertexUvs = [];
for (var i = 0; i < atomlist.length; i++) {
var atom = this.atoms[atomlist[i]];
if (atom == undefined) continue;
- var sphereMaterial = new THREE.MeshLambertMaterial({color: atom.color});
+ var sphereMaterial = new THREE.MeshLambertMaterial({color: atom.color, map:this.colormap});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
group.add(sphere);
var r = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius
@@ -347,7 +400,7 @@ GLmol.prototype.drawAtomsAsIcosahedron = function(group, atomlist, defaultRadius
var atom = this.atoms[atomlist[i]];
if (atom == undefined) continue;
- var mat = new THREE.MeshLambertMaterial({color: atom.color});
+ var mat = new THREE.MeshLambertMaterial({color: atom.color, map:this.colormap});
var sphere = new THREE.Mesh(geo, mat);
sphere.scale.x = sphere.scale.y = sphere.scale.z = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius;
group.add(sphere);
@@ -408,7 +461,7 @@ GLmol.prototype.drawBondsAsStick = function(group, atomlist, bondR, atomR, ignor
this.drawCylinder(group, new TV3(atom2.x, atom2.y, atom2.z), mp, bondR, atom2.color);
}
if (!atom1.connected) continue;
- var sphereMaterial = new THREE.MeshLambertMaterial({color: atom1.color});
+ var sphereMaterial = new THREE.MeshLambertMaterial({color: atom1.color, map:this.colormap});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.scale.x = sphere.scale.y = sphere.scale.z = atomR;
group.add(sphere);
@@ -449,7 +502,7 @@ GLmol.prototype.drawUnitcell = function(group) {
for (var i = 0; i < edges.length; i++) {
geo.vertices.push(new TV3(vertices[edges[i]][0], vertices[edges[i]][1], vertices[edges[i]][2]));
}
- var lineMaterial = new THREE.LineBasicMaterial({linewidth: 1, color: 0xcccccc});
+ var lineMaterial = new THREE.LineBasicMaterial({linewidth: 1, color: 0xcccccc}); // TODO: fix color
var line = new THREE.Line(geo, lineMaterial);
line.type = THREE.LinePieces;
group.add(line);
@@ -556,7 +609,8 @@ GLmol.prototype.drawBondsAsLine = function(group, atomlist, lineWidth) {
}
var lineMaterial = new THREE.LineBasicMaterial({linewidth: lineWidth});
lineMaterial.vertexColors = true;
-
+ lineMaterial.map = this.colormap;
+ console.log(lineMaterial);
var line = new THREE.Line(geo, lineMaterial);
line.type = THREE.LinePieces;
group.add(line);
@@ -574,7 +628,7 @@ GLmol.prototype.drawSmoothCurve = function(group, _points, width, colors, div) {
geo.vertices.push(points[i]);
geo.colors.push(new TCo(colors[(i == 0) ? 0 : Math.round((i - 1) / div)]));
}
- var lineMaterial = new THREE.LineBasicMaterial({linewidth: width});
+ var lineMaterial = new THREE.LineBasicMaterial({linewidth: width, map: this.colormap});
lineMaterial.vertexColors = true;
var line = new THREE.Line(geo, lineMaterial);
line.type = THREE.LineStrip;
@@ -663,7 +717,7 @@ GLmol.prototype.drawSmoothTube = function(group, _points, colors, radii) {
}
geo.computeFaceNormals();
geo.computeVertexNormals(false);
- var mat = new THREE.MeshLambertMaterial();// mat.wireframe = true;
+ var mat = new THREE.MeshLambertMaterial({map:this.colormap});// mat.wireframe = true;
mat.vertexColors = THREE.FaceColors;
var mesh = new THREE.Mesh(geo, mat);
mesh.doubleSided = true;
@@ -726,7 +780,7 @@ GLmol.prototype.drawStrip = function(group, p1, p2, colors, div, thickness) {
div = div || this.axisDIV;
p1 = this.subdivide(p1, div);
p2 = this.subdivide(p2, div);
- if (!thickness) this.drawThinStrip(group, p1, p2, colors, div)
+ if (!thickness) {this.drawThinStrip(group, p1, p2, colors, div); return;}
var geo = new THREE.Geometry();
var vs = geo.vertices, fs = geo.faces;
@@ -761,7 +815,7 @@ GLmol.prototype.drawStrip = function(group, p1, p2, colors, div, thickness) {
fs.push(new THREE.Face4(vsize + 1, vsize + 5, vsize + 7, vsize + 3, undefined, fs[fs.length - 3].color));
geo.computeFaceNormals();
geo.computeVertexNormals(false);
- var material = new THREE.MeshLambertMaterial();
+ var material = new THREE.MeshLambertMaterial({map: this.colormap});
material.vertexColors = THREE.FaceColors;
var mesh = new THREE.Mesh(geo, material);
mesh.doubleSided = true;
@@ -782,7 +836,7 @@ GLmol.prototype.drawThinStrip = function(group, p1, p2, colors, div) {
}
geo.computeFaceNormals();
geo.computeVertexNormals(false);
- var material = new THREE.MeshLambertMaterial();
+ var material = new THREE.MeshLambertMaterial({map:this.colormap});
material.vertexColors = THREE.FaceColors;
var mesh = new THREE.Mesh(geo, material);
mesh.doubleSided = true;
@@ -806,7 +860,7 @@ GLmol.prototype.drawCylinder = function(group, from, to, radius, color, cap) {
this.cylinderGeometry.faceUvs = [];
this.faceVertexUvs = [];
}
- var cylinderMaterial = new THREE.MeshLambertMaterial({color: color.getHex()});
+ var cylinderMaterial = new THREE.MeshLambertMaterial({color: color.getHex(), map:this.colormap});
var cylinder = new THREE.Mesh(this.cylinderGeometry, cylinderMaterial);
cylinder.position = midpoint;
cylinder.lookAt(from);
@@ -952,7 +1006,7 @@ GLmol.prototype.drawNucleicAcidLadder = function(group, atomlist) {
}
this.drawNucleicAcidLadderSub(geo, lineGeo, currentComponent, color);
geo.computeFaceNormals();
- var mat = new THREE.MeshLambertMaterial();
+ var mat = new THREE.MeshLambertMaterial({map:this.colormap});
mat.vertexColors = THREE.VertexColors;
var mesh = new THREE.Mesh(geo, mat);
mesh.doubleSided = true;
@@ -1257,7 +1311,7 @@ GLmol.prototype.colorByAtom = function(atomlist, colors) {
var c = colors[atom.elem];
if (c == undefined) c = this.ElementColors[atom.elem];
if (c == undefined) c = this.defaultColor;
- atom.color = c;
+ this.setColor(atom.serial, c);
}
};
@@ -1268,8 +1322,8 @@ GLmol.prototype.colorByStructure = function(atomlist, helixColor, sheetColor, co
var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
if (!colorSidechains && (atom.atom != 'CA' || atom.hetflag)) continue;
- if (atom.ss[0] == 's') atom.color = sheetColor;
- else if (atom.ss[0] == 'h') atom.color = helixColor;
+ if (atom.ss[0] == 's') this.setColor(atom.serial, sheetColor);
+ else if (atom.ss[0] == 'h') this.setColor(atom.serial, helixColor);
}
};
@@ -1300,7 +1354,7 @@ GLmol.prototype.colorByBFactor = function(atomlist, colorSidechains) {
color.setHSV(0.667, (mid - atom.b) / range, 1);
else
color.setHSV(0, (atom.b - mid) / range, 1);
- atom.color = color.getHex();
+ this.setColor(atom.serial, color.getHex());
}
}
};
@@ -1313,7 +1367,7 @@ GLmol.prototype.colorByChain = function(atomlist, colorSidechains) {
if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') {
var color = new TCo(0);
color.setHSV((atom.chain.charCodeAt(0)) % 15 / 15.0, 1, 0.9);
- atom.color = color.getHex();
+ this.setColor(atom.serial, color.getHex());
}
}
};
@@ -1323,7 +1377,7 @@ GLmol.prototype.colorByResidue = function(atomlist, residueColors) {
var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
c = residueColors[atom.resn]
- if (c != undefined) atom.color = c;
+ if (c != undefined) this.setColor(atom.serial, c);
}
};
@@ -1331,7 +1385,7 @@ GLmol.prototype.colorAtoms = function(atomlist, c) {
for (var i in atomlist) {
var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
- atom.color = c;
+ this.setColor(atom.serial, c);
}
};
@@ -1341,7 +1395,7 @@ GLmol.prototype.colorByPolarity = function(atomlist, polar, nonpolar) {
var colorMap = {};
for (var i in polarResidues) colorMap[polarResidues[i]] = polar;
for (i in nonPolarResidues) colorMap[nonPolarResidues[i]] = nonpolar;
- this.colorByResidue(atomlist, colorMap);
+ this.colorByResidue(atomlist, colorMap);
};
// TODO: Add near(atomlist, neighbor, distanceCutoff)
@@ -1365,7 +1419,7 @@ GLmol.prototype.colorChainbow = function(atomlist, colorSidechains) {
if ((colorSidechains || atom.atom != 'CA' || atom.atom != 'O3\'') && !atom.hetflag) {
var color = new TCo(0);
color.setHSV(240.0 / 360 * (1 - cnt / total), 1, 0.9);
- atom.color = color.getHex();
+ this.setColor(atom.serial, color.getHex());
cnt++;
}
}
diff --git a/src/js/Three49custom.js b/src/js/Three49custom.js
index 01abdec..76c13df 100644
--- a/src/js/Three49custom.js
+++ b/src/js/Three49custom.js
@@ -206,7 +206,7 @@ c.near;k.fogFar.value=c.far}else if(c instanceof THREE.FogExp2)k.fogDensity.valu
j.color;p=j.intensity;s=j.distance;if(j instanceof THREE.AmbientLight)if(D.gammaInput){l=l+m.r*m.r;n=n+m.g*m.g;o=o+m.b*m.b}else{l=l+m.r;n=n+m.g;o=o+m.b}else if(j instanceof THREE.DirectionalLight){s=Z*3;if(D.gammaInput){y[s]=m.r*m.r*p*p;y[s+1]=m.g*m.g*p*p;y[s+2]=m.b*m.b*p*p}else{y[s]=m.r*p;y[s+1]=m.g*p;y[s+2]=m.b*p}Ca.copy(j.matrixWorld.getPosition());Ca.subSelf(j.target.matrixWorld.getPosition());Ca.normalize();A[s]=Ca.x;A[s+1]=Ca.y;A[s+2]=Ca.z;Z=Z+1}else if(j instanceof THREE.PointLight){la=I*3;
if(D.gammaInput){B[la]=m.r*m.r*p*p;B[la+1]=m.g*m.g*p*p;B[la+2]=m.b*m.b*p*p}else{B[la]=m.r*p;B[la+1]=m.g*p;B[la+2]=m.b*p}m=j.matrixWorld.getPosition();K[la]=m.x;K[la+1]=m.y;K[la+2]=m.z;N[I]=s;I=I+1}else if(j instanceof THREE.SpotLight){la=R*3;if(D.gammaInput){Y[la]=m.r*m.r*p*p;Y[la+1]=m.g*m.g*p*p;Y[la+2]=m.b*m.b*p*p}else{Y[la]=m.r*p;Y[la+1]=m.g*p;Y[la+2]=m.b*p}m=j.matrixWorld.getPosition();O[la]=m.x;O[la+1]=m.y;O[la+2]=m.z;ba[R]=s;Ca.copy(m);Ca.subSelf(j.target.matrixWorld.getPosition());Ca.normalize();
ja[la]=Ca.x;ja[la+1]=Ca.y;ja[la+2]=Ca.z;ca[R]=Math.cos(j.angle);Q[R]=j.exponent;R=R+1}}}c=Z*3;for(i=y.length;c
About
GLmol -- Molecular Viewer on WebGL/Javascript
-Version 0.40 (20120429)
This program is written by biochem_fan and released under LGPL. Please visit my project page for the details and source code distribution.
Comments and Suggestions are welcome. Please mail to biochem_fan at users.sourceforge.jp or write in the forum.