Last active
January 25, 2016 21:48
-
-
Save andreasplesch/4b2742e6260cde371c31 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name VolumeData | |
// @namespace aplesch | |
// @description adjust steps for ray casting | |
// @version 0.13 | |
// @grant none | |
// ==/UserScript== | |
/** @namespace x3dom.nodeTypes */ | |
/* | |
* MEDX3DOM JavaScript Library | |
* http://medx3dom.org | |
* | |
* (C)2011 Vicomtech Research Center, | |
* Donostia - San Sebastian | |
* Dual licensed under the MIT and GPL. | |
* | |
* Based on code originally provided by | |
* http://www.x3dom.org | |
*/ | |
/* ### X3DVolumeDataNode ### */ | |
x3dom.registerNodeType( | |
"X3DVolumeDataNode", | |
"VolumeRendering", | |
defineClass(x3dom.nodeTypes.X3DShapeNode, // changed inheritance! | |
/** | |
* Constructor for X3DVolumeDataNode | |
* @constructs x3dom.nodeTypes.X3DVolumeDataNode | |
* @x3d x.x | |
* @component VolumeRendering | |
* @status experimental | |
* @extends x3dom.nodeTypes.X3DShapeNode | |
* @param {Object} [ctx=null] - context object, containing initial settings like namespace | |
* @classdesc (Abstract) class for volume data. | |
*/ | |
function (ctx) { | |
x3dom.nodeTypes.X3DVolumeDataNode.superClass.call(this, ctx); | |
/** | |
* Specifies the size of of the bounding box for the volume data. | |
* @var {x3dom.fields.SFVec3f} dimensions | |
* @memberof x3dom.nodeTypes.X3DVolumeDataNode | |
* @initvalue 1,1,1 | |
* @field x3dom | |
* @instance | |
*/ | |
this.addField_SFVec3f(ctx, 'dimensions', 1, 1, 1); | |
/** | |
* The voxels field is an ImageTextureAtlas node containing the volume data. | |
* @var {x3dom.fields.SFNode} voxels | |
* @memberof x3dom.nodeTypes.X3DVolumeDataNode | |
* @initvalue x3dom.nodeTypes.Texture | |
* @field x3dom | |
* @instance | |
*/ | |
this.addField_SFNode('voxels', x3dom.nodeTypes.Texture); | |
//this.addField_MFNode('voxels', x3dom.nodeTypes.X3DTexture3DNode); | |
//this.addField_SFBool(ctx, 'swapped', false); | |
//this.addField_SFVec3f(ctx, 'sliceThickness', 1, 1, 1); | |
/** | |
* Allow to locate the viewpoint inside the volume. | |
* @var {x3dom.fields.SFBool} allowViewpointInside | |
* @memberof x3dom.nodeTypes.X3DVolumeDataNode | |
* @initvalue true | |
* @field x3dom | |
* @instance | |
*/ | |
this.addField_SFBool(ctx, 'allowViewpointInside', true) | |
//Neccesary for counting the textures which are added on each style, number of textures can be variable | |
this._textureID = 0; | |
this._first = true; | |
this._styleList = []; | |
this.surfaceNormalsNeeded = false; | |
this.normalTextureProvided = false; | |
this.fragmentPreamble = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n" + | |
" precision highp float;\n" + | |
"#else\n" + | |
" precision mediump float;\n" + | |
"#endif\n\n"; | |
}, | |
{ | |
getTextureSize: function(texture) { | |
var size = { w: 0, h: 0, valid: false }; | |
var texBag = this._webgl ? this._webgl.texture : null; | |
var t, n = (texture && texBag) ? texBag.length : 0; | |
for (t=0; t<n; t++) { | |
if (texture == texBag[t].node && texBag[t].texture) { | |
size.w = texBag[t].texture.width; | |
size.h = texBag[t].texture.height; | |
if (size.w && size.h) { | |
size.valid = true; | |
} | |
break; | |
} | |
} | |
return size; | |
}, | |
//Common vertex shader text for all volume data nodes | |
vertexShaderText: function(needEyePosition){ | |
var shader = | |
"attribute vec3 position;\n"+ | |
"uniform vec3 dimensions;\n"+ | |
"uniform mat4 modelViewProjectionMatrix;\n"+ | |
"varying vec4 vertexPosition;\n"+ | |
"varying vec4 pos;\n"; | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0 || (needEyePosition===true)){ | |
shader += "uniform mat4 modelViewMatrix;\n"+ | |
"varying vec4 position_eye;\n"; | |
} | |
shader += "\n" + | |
"void main()\n"+ | |
"{\n"+ | |
" vertexPosition = modelViewProjectionMatrix * vec4(position, 1.0);\n"; | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0 || (needEyePosition===true)){ | |
shader += " position_eye = modelViewMatrix * vec4(position, 1.0);\n"; | |
} | |
shader += | |
" pos = vec4((position/dimensions)+0.5, 1.0);\n"+ | |
" gl_Position = vertexPosition;\n"+ | |
"}"; | |
return shader; | |
}, | |
defaultUniformsShaderText: function(numberOfSlices, slicesOverX, slicesOverY, needEyePosition){ | |
var uniformsText = | |
"uniform sampler2D uVolData;\n"+ | |
"uniform vec3 dimensions;\n"+ | |
"uniform vec3 offset;\n"+ | |
"uniform mat4 modelViewMatrix;\n"+ | |
"uniform mat4 modelViewMatrixInverse;\n"+ | |
"varying vec4 vertexPosition;\n"+ | |
"varying vec4 pos;\n"; | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0 || (needEyePosition===true)){ | |
uniformsText += "varying vec4 position_eye;\n"; | |
} | |
//LIGHTS | |
for(var l=0; l<x3dom.nodeTypes.X3DLightNode.lightID; l++) { | |
uniformsText += "uniform float light"+l+"_On;\n" + | |
"uniform float light"+l+"_Type;\n" + | |
"uniform vec3 light"+l+"_Location;\n" + | |
"uniform vec3 light"+l+"_Direction;\n" + | |
"uniform vec3 light"+l+"_Color;\n" + | |
"uniform vec3 light"+l+"_Attenuation;\n" + | |
"uniform float light"+l+"_Radius;\n" + | |
"uniform float light"+l+"_Intensity;\n" + | |
"uniform float light"+l+"_AmbientIntensity;\n" + | |
"uniform float light"+l+"_BeamWidth;\n" + | |
"uniform float light"+l+"_CutOffAngle;\n" + | |
"uniform float light"+l+"_ShadowIntensity;\n"; | |
} | |
uniformsText += | |
"const float Steps = 120.0;\n"+ | |
"const float numberOfSlices = "+ numberOfSlices.toPrecision(5)+";\n"+ | |
"const float slicesOverX = " + slicesOverX.toPrecision(5) +";\n"+ | |
"const float slicesOverY = " + slicesOverY.toPrecision(5) +";\n"; | |
return uniformsText; | |
}, | |
texture3DFunctionShaderText: "vec4 cTexture3D(sampler2D vol, vec3 volpos, float nS, float nX, float nY)\n"+ | |
"{\n"+ | |
" float s1,s2;\n"+ | |
" float dx1,dy1;\n"+ | |
" float dx2,dy2;\n"+ | |
" vec2 texpos1,texpos2;\n"+ | |
" s1 = floor(volpos.z*nS);\n"+ | |
" s2 = s1+1.0;\n"+ | |
" dx1 = fract(s1/nX);\n"+ | |
" dy1 = floor(s1/nY)/nY;\n"+ | |
" dx2 = fract(s2/nX);\n"+ | |
" dy2 = floor(s2/nY)/nY;\n"+ | |
" texpos1.x = dx1+(volpos.x/nX);\n"+ | |
" texpos1.y = dy1+(volpos.y/nY);\n"+ | |
" texpos2.x = dx2+(volpos.x/nX);\n"+ | |
" texpos2.y = dy2+(volpos.y/nY);\n"+ | |
" return mix( texture2D(vol,texpos1), texture2D(vol,texpos2), (volpos.z*nS)-s1);\n"+ | |
"}\n"+ | |
"\n", | |
normalFunctionShaderText: function(){ | |
if (this.surfaceNormalsNeeded){ | |
return "vec4 getNormalFromTexture(sampler2D sampler, vec3 pos, float nS, float nX, float nY) {\n"+ | |
" vec4 n = (2.0*cTexture3D(sampler, pos, nS, nX, nY)-1.0);\n"+ | |
" return vec4(normalize(n.xyz), length(n.xyz));\n"+ | |
"}\n"+ | |
"\n"+ | |
"vec4 getNormalOnTheFly(sampler2D sampler, vec3 voxPos, float nS, float nX, float nY){\n"+ | |
" float v0 = cTexture3D(sampler, voxPos + vec3(offset.x, 0, 0), nS, nX, nY).r;\n"+ | |
" float v1 = cTexture3D(sampler, voxPos - vec3(offset.x, 0, 0), nS, nX, nY).r;\n"+ | |
" float v2 = cTexture3D(sampler, voxPos + vec3(0, offset.y, 0), nS, nX, nY).r;\n"+ | |
" float v3 = cTexture3D(sampler, voxPos - vec3(0, offset.y, 0), nS, nX, nY).r;\n"+ | |
" float v4 = cTexture3D(sampler, voxPos + vec3(0, 0, offset.z), nS, nX, nY).r;\n"+ | |
" float v5 = cTexture3D(sampler, voxPos - vec3(0, 0, offset.z), nS, nX, nY).r;\n"+ | |
" vec3 grad = vec3(v0-v1, v2-v3, v4-v5)*0.5;\n"+ | |
" return vec4(normalize(grad), length(grad));\n"+ | |
"}\n"+ | |
"\n"; | |
}else{ | |
return ""; | |
} | |
}, | |
defaultLoopFragmentShaderText: function(inlineShaderText, inlineLightAssigment, initializeValues){ | |
initializeValues = typeof initializeValues !== 'undefined' ? initializeValues : ""; //default value, empty string | |
var shaderLoop = "void main()\n"+ | |
"{\n"+ | |
" vec3 cam_pos = vec3(modelViewMatrixInverse[3][0], modelViewMatrixInverse[3][1], modelViewMatrixInverse[3][2]);\n"+ | |
" vec3 cam_cube = cam_pos/dimensions+0.5;\n"+ | |
" vec3 dir = normalize(pos.xyz-cam_cube);\n"; | |
if(this._vf.allowViewpointInside){ | |
shaderLoop += | |
" float cam_inside = float(all(bvec2(all(lessThan(cam_cube, vec3(1.0))),all(greaterThan(cam_cube, vec3(0.0))))));\n"+ | |
" vec3 ray_pos = mix(pos.xyz, cam_cube, cam_inside);\n"; | |
}else{ | |
shaderLoop += " vec3 ray_pos = pos.xyz;\n"; | |
} | |
shaderLoop += | |
" vec4 accum = vec4(0.0, 0.0, 0.0, 0.0);\n"+ | |
" vec4 sample = vec4(0.0, 0.0, 0.0, 0.0);\n"+ | |
" vec4 value = vec4(0.0, 0.0, 0.0, 0.0);\n"+ | |
" float cont = 0.0;\n"+ | |
" vec3 step_size = dir/Steps;\n"; | |
//Light init values | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0){ | |
shaderLoop += | |
" vec3 ambient = vec3(0.0, 0.0, 0.0);\n"+ | |
" vec3 diffuse = vec3(0.0, 0.0, 0.0);\n"+ | |
" vec3 specular = vec3(0.0, 0.0, 0.0);\n"+ | |
" vec4 step_eye = modelViewMatrix * vec4(step_size, 0.0);\n"+ | |
" vec4 positionE = position_eye;\n"+ | |
" float lightFactor = 1.0;\n"; | |
}else{ | |
shaderLoop += " float lightFactor = 1.2;\n"; | |
} | |
shaderLoop += initializeValues+ | |
" float opacityFactor = 10.0;\n"+ | |
" float t_near;\n"+ | |
" float t_far;\n"+ | |
" for(float i = 0.0; i < Steps; i+=1.0)\n"+ | |
" {\n"+ | |
" value = cTexture3D(uVolData, ray_pos, numberOfSlices, slicesOverX, slicesOverY);\n"+ | |
" value = value.rgbr;\n"; | |
if(this.surfaceNormalsNeeded){ | |
if(this.normalTextureProvided){ | |
shaderLoop += " vec4 gradEye = getNormalFromTexture(uSurfaceNormals, ray_pos, numberOfSlices, slicesOverX, slicesOverY);\n"; | |
}else{ | |
shaderLoop += " vec4 gradEye = getNormalOnTheFly(uVolData, ray_pos, numberOfSlices, slicesOverX, slicesOverY);\n"; | |
} | |
shaderLoop += " vec4 grad = vec4((modelViewMatrix * vec4(gradEye.xyz, 0.0)).xyz, gradEye.a);\n"; | |
} | |
shaderLoop += inlineShaderText; | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0){ | |
shaderLoop += inlineLightAssigment; | |
} | |
shaderLoop += | |
//Composite the volume sample | |
" sample.a = value.a * opacityFactor * (1.0/Steps);\n"+ | |
" sample.rgb = value.rgb * sample.a * lightFactor;\n"+ | |
" accum.rgb += (1.0 - accum.a) * sample.rgb;\n"+ | |
" accum.a += (1.0 - accum.a) * sample.a;\n"+ | |
//Advance the current ray position | |
" ray_pos.xyz += step_size;\n"; | |
if(x3dom.nodeTypes.X3DLightNode.lightID>0){ | |
shaderLoop +=" positionE += step_eye;\n"; | |
} | |
shaderLoop += | |
//Early ray termination and Break if the position is greater than <1, 1, 1> | |
" if(accum.a >= 1.0 || ray_pos.x < 0.0 || ray_pos.y < 0.0 || ray_pos.z < 0.0 || ray_pos.x > 1.0 || ray_pos.y > 1.0 || ray_pos.z > 1.0)\n"+ | |
" break;\n"+ | |
" }\n"+ | |
" gl_FragColor = accum;\n"+ | |
"}"; | |
return shaderLoop; | |
} | |
} | |
) | |
); | |
/** @namespace x3dom.nodeTypes */ | |
/* | |
* MEDX3DOM JavaScript Library | |
* http://medx3dom.org | |
* | |
* (C)2011 Vicomtech Research Center, | |
* Donostia - San Sebastian | |
* Dual licensed under the MIT and GPL. | |
* | |
* Based on code originally provided by | |
* http://www.x3dom.org | |
*/ | |
/* ### VolumeData ### */ | |
x3dom.registerNodeType( | |
"VolumeData", | |
"VolumeRendering", | |
defineClass(x3dom.nodeTypes.X3DVolumeDataNode, | |
/** | |
* Constructor for VolumeData | |
* @constructs x3dom.nodeTypes.VolumeData | |
* @x3d x.x | |
* @component VolumeRendering | |
* @status experimental | |
* @extends x3dom.nodeTypes.X3DVolumeDataNode | |
* @param {Object} [ctx=null] - context object, containing initial settings like namespace | |
* @classdesc The VolumeData node specifies a non-segmented volume data to be rendered with a volume rendering style. | |
*/ | |
function (ctx) { | |
x3dom.nodeTypes.VolumeData.superClass.call(this, ctx); | |
/** | |
* Specifies the render style to be applied on the volume data. | |
* @var {x3dom.fields.SFNode} renderStyle | |
* @memberof x3dom.nodeTypes.VolumeData | |
* @initvalue x3dom.nodeTypes.X3DVolumeRenderStyleNode | |
* @field x3dom | |
* @instance | |
*/ | |
this.addField_SFNode('renderStyle', x3dom.nodeTypes.X3DVolumeRenderStyleNode); | |
this.vrcMultiTexture = new x3dom.nodeTypes.MultiTexture(ctx); | |
this.vrcVolumeTexture = null; | |
this.vrcSinglePassShader = new x3dom.nodeTypes.ComposedShader(ctx); | |
this.vrcSinglePassShaderVertex = new x3dom.nodeTypes.ShaderPart(ctx); | |
this.vrcSinglePassShaderFragment = new x3dom.nodeTypes.ShaderPart(ctx); | |
this.vrcSinglePassShaderFieldBackCoord = new x3dom.nodeTypes.Field(ctx); | |
this.vrcSinglePassShaderFieldVolData = new x3dom.nodeTypes.Field(ctx); | |
this.vrcSinglePassShaderFieldOffset = new x3dom.nodeTypes.Field(ctx); | |
this.vrcSinglePassShaderFieldDimensions = new x3dom.nodeTypes.Field(ctx); | |
}, | |
{ | |
initializeValues: function() { | |
var initialValues =""; | |
if(this._cf.renderStyle.node.initializeValues != undefined){ | |
initialValues += this._cf.renderStyle.node.initializeValues(); | |
} | |
return initialValues; | |
}, | |
styleUniformsShaderText: function(){ | |
var styleUniformsText = this._cf.renderStyle.node.styleUniformsShaderText(); | |
this.surfaceNormalsNeeded = true; | |
if(this._cf.renderStyle.node._cf.surfaceNormals && this._cf.renderStyle.node._cf.surfaceNormals.node != null){ | |
styleUniformsText += "uniform sampler2D uSurfaceNormals;\n"; //Neccessary when gradient is provided | |
this.normalTextureProvided = true; | |
this.surfaceNormals = this._cf.renderStyle.node._cf.surfaceNormals.node; | |
}else if(x3dom.isa(this._cf.renderStyle.node, x3dom.nodeTypes.OpacityMapVolumeStyle)){ //OpacityMap does not use surface normals | |
this.surfaceNormalsNeeded = false; | |
this.normalTextureProvided = false; | |
} | |
return styleUniformsText; | |
}, | |
styleShaderText: function(){ | |
var styleText = ""; | |
if(this._cf.renderStyle.node.styleShaderText != undefined){ | |
styleText += this._cf.renderStyle.node.styleShaderText(); | |
} | |
return styleText; | |
}, | |
inlineStyleShaderText: function(){ | |
return this._cf.renderStyle.node.inlineStyleShaderText(); | |
}, | |
lightAssigment: function(){ | |
return this._cf.renderStyle.node.lightAssigment(); | |
}, | |
//Obtain the light equation from the render style child node | |
lightEquationShaderText: function(){ | |
return this._cf.renderStyle.node.lightEquationShaderText(); | |
}, | |
// nodeChanged is called after subtree is parsed and attached in DOM | |
nodeChanged: function() | |
{ | |
// uhhhh, manually build backend-graph scene-subtree, | |
// therefore, try to mimic depth-first parsing scheme | |
if (!this._cf.appearance.node) | |
{ | |
var i; | |
this.addChild(new x3dom.nodeTypes.Appearance()); | |
// create shortcut to volume data set | |
this.vrcVolumeTexture = this._cf.voxels.node; | |
this.vrcVolumeTexture._vf.repeatS = false; | |
this.vrcVolumeTexture._vf.repeatT = false; | |
this.vrcMultiTexture._nameSpace = this._nameSpace; | |
this.vrcMultiTexture.addChild(this.vrcVolumeTexture, 'texture'); | |
this.vrcVolumeTexture.nodeChanged(); | |
// textures from styles | |
if (this._cf.renderStyle.node.textures) { | |
var styleTextures = this._cf.renderStyle.node.textures(); | |
for (i = 0; i<styleTextures.length; i++) | |
{ | |
this.vrcMultiTexture.addChild(styleTextures[i], 'texture'); | |
this.vrcVolumeTexture.nodeChanged(); | |
} | |
} | |
this._cf.appearance.node.addChild(this.vrcMultiTexture); | |
this.vrcMultiTexture.nodeChanged(); | |
//Update child node private properties | |
this._cf.renderStyle.node.updateProperties(this); | |
// here goes the volume shader | |
this.vrcSinglePassShaderVertex._vf.type = 'vertex'; | |
this.vrcSinglePassShaderVertex._vf.url[0]=this.vertexShaderText(x3dom.isa(this._cf.renderStyle.node, x3dom.nodeTypes.RadarVolumeStyle)); | |
this.vrcSinglePassShaderFragment._vf.type = 'fragment'; | |
var shaderText = ""; | |
if (x3dom.isa(this._cf.renderStyle.node, x3dom.nodeTypes.X3DComposableVolumeRenderStyleNode)) { | |
shaderText += this.fragmentPreamble+ | |
this.defaultUniformsShaderText(this.vrcVolumeTexture._vf.numberOfSlices, this.vrcVolumeTexture._vf.slicesOverX, this.vrcVolumeTexture._vf.slicesOverY)+ | |
this.styleUniformsShaderText()+ | |
this.styleShaderText()+ | |
this.texture3DFunctionShaderText+ | |
this.normalFunctionShaderText()+ | |
this.lightEquationShaderText()+ | |
this.defaultLoopFragmentShaderText(this.inlineStyleShaderText(), this.lightAssigment(), this.initializeValues()); | |
}else{ | |
shaderText += this._cf.renderStyle.node.fragmentShaderText( | |
this.vrcVolumeTexture._vf.numberOfSlices, | |
this.vrcVolumeTexture._vf.slicesOverX, | |
this.vrcVolumeTexture._vf.slicesOverY); | |
} | |
this.vrcSinglePassShaderFragment._vf.url[0]= shaderText; | |
this.vrcSinglePassShader.addChild(this.vrcSinglePassShaderVertex, 'parts'); | |
this.vrcSinglePassShaderVertex.nodeChanged(); | |
this.vrcSinglePassShader.addChild(this.vrcSinglePassShaderFragment, 'parts'); | |
this.vrcSinglePassShaderFragment.nodeChanged(); | |
this.vrcSinglePassShaderFieldVolData._vf.name = 'uVolData'; | |
this.vrcSinglePassShaderFieldVolData._vf.type = 'SFInt32'; | |
this.vrcSinglePassShaderFieldVolData._vf.value = this._textureID++; | |
this.vrcSinglePassShaderFieldDimensions._vf.name = 'dimensions'; | |
this.vrcSinglePassShaderFieldDimensions._vf.type = 'SFVec3f'; | |
this.vrcSinglePassShaderFieldDimensions._vf.value = this._vf.dimensions; | |
this.vrcSinglePassShaderFieldOffset._vf.name = 'offset'; | |
this.vrcSinglePassShaderFieldOffset._vf.type = 'SFVec3f'; | |
this.vrcSinglePassShaderFieldOffset._vf.value = "0.01 0.01 0.01"; //Default initial value | |
this.vrcSinglePassShader.addChild(this.vrcSinglePassShaderFieldVolData, 'fields'); | |
this.vrcSinglePassShaderFieldVolData.nodeChanged(); | |
this.vrcSinglePassShader.addChild(this.vrcSinglePassShaderFieldDimensions, 'fields'); | |
this.vrcSinglePassShaderFieldDimensions.nodeChanged(); | |
this.vrcSinglePassShader.addChild(this.vrcSinglePassShaderFieldOffset, 'fields'); | |
//Take volume texture size for the ComposableRenderStyles offset parameter | |
this.offsetInterval = window.setInterval((function(aTex, obj) { | |
return function() { | |
x3dom.debug.logInfo('[VolumeRendering][VolumeData] Looking for Volume Texture size...'); | |
var s = obj.getTextureSize(aTex); | |
if(s.valid){ | |
clearInterval(obj.offsetInterval); | |
obj.vrcSinglePassShaderFieldOffset._vf.value = new x3dom.fields.SFVec3f(1.0/(s.w/aTex._vf.slicesOverX), 1.0/(s.h/aTex._vf.slicesOverY), 1.0/aTex._vf.numberOfSlices); | |
obj.vrcSinglePassShader.nodeChanged(); | |
x3dom.debug.logInfo('[VolumeRendering][VolumeData] Volume Texture size obtained'); | |
} | |
} | |
})(this.vrcVolumeTexture, this), 1000); | |
var ShaderUniforms = this._cf.renderStyle.node.uniforms(); | |
for (i = 0; i<ShaderUniforms.length; i++) | |
{ | |
this.vrcSinglePassShader.addChild(ShaderUniforms[i], 'fields'); | |
} | |
this._cf.appearance.node.addChild(this.vrcSinglePassShader); | |
this.vrcSinglePassShader.nodeChanged(); | |
this._cf.appearance.node.nodeChanged(); | |
} | |
if (!this._cf.geometry.node) { | |
this.addChild(new x3dom.nodeTypes.Box()); | |
this._cf.geometry.node._vf.solid = false; | |
this._cf.geometry.node._vf.hasHelperColors = false; | |
this._cf.geometry.node._vf.size = new x3dom.fields.SFVec3f( | |
this._vf.dimensions.x, this._vf.dimensions.y, this._vf.dimensions.z); | |
// workaround to trigger field change... | |
this._cf.geometry.node.fieldChanged("size"); | |
} | |
} | |
} | |
) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment