#################################################################################################### # # Invoking X3D model self-test: # # $ python MaterialChoicePrototype.py # # Python package x3d.py package is available on PyPI for import. # This approach simplifies Python X3D deployment and use. # https://pypi.org/project/x3d # # Installation: # pip install x3d # or # python -m pip install x3d # # Developer options for loading x3d package in other Python programs: # # from x3d import * # preferred approach, terser source that avoids x3d.* class prefixes # # or # import x3d # traditional way to subclass x3d package, all classes require x3d.* prefix, # # but python source is very verbose, for example x3d.Material x3d.Shape etc. # # X3dToPython.xslt stylesheet insertPackagePrefix=true supports this option. # #################################################################################################### from x3d import * newModel=X3D(profile='Immersive',version='3.0', head=head( children=[ meta(content='MaterialChoicePrototype.x3d',name='title'), meta(content='Prototype definition for toggle-able Material node that can choose among an array of material nodes, and is switchable at run time. This prototype also demonstrates why it is important that only the first node in a ProtoBody can render: so that it can serve as a special node type (such as Material).',name='description'), meta(content='Don Brutzman and MV4205 class',name='creator'), meta(content='29 April 2004',name='created'), meta(content='28 November 2019',name='modified'), meta(content='MaterialChoiceExample.x3d',name='reference'), meta(content='https://savage.nps.edu/Savage/Tools/Animation/MaterialChoicePrototype.x3d',name='identifier'), meta(content='X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit',name='generator'), meta(content='../../license.html',name='license')]), Scene=Scene( children=[ WorldInfo(title='MaterialChoicePrototype.x3d'), ProtoDeclare(appinfo='MaterialChoice selects one of several different Material values',name='MaterialChoice', ProtoInterface=ProtoInterface( field=[ field(accessType='inputOnly',name='set_index',type='SFInt32'), field(accessType='initializeOnly',appinfo='which Material node is chosen, with array index starting at 0',name='index',type='SFInt32',value=0), field(accessType='outputOnly',name='index_changed',type='SFInt32'), field(accessType='inputOnly',name='set_fraction',type='SFFloat'), field(accessType='initializeOnly',appinfo='fraction to interpolate between current and next Material node (if any) across all field values.',name='fraction',type='SFFloat',value=0), field(accessType='inputOnly',appinfo='Trigger next Material node',name='next',type='SFBool'), field(accessType='inputOnly',appinfo='Trigger previous Material node',name='previous',type='SFBool'), field(accessType='initializeOnly',appinfo='array of available Material nodes defaults to zeroth Material',name='materials',type='MFNode', # no default initialization node since it crashes Xj3D ), field(accessType='inputOnly',appinfo='append another Material node to materials array',name='appendMaterial',type='SFNode'), field(accessType='inputOnly',appinfo='delete Material node indicated by input index value',name='deleteMaterial',type='SFInt32'), field(accessType='inputOnly',appinfo='deletes all Material nodes resets scene to default Material values defined in X3D Specification',name='deleteAllMaterials',type='SFBool')]), ProtoBody=ProtoBody( # first node is node type for this prototype, other nodes are active but do not render children=[ Material(DEF='ViewedMaterialNode'), Script(DEF='MaterialAnimationScript',directOutput=True, field=[ field(accessType='initializeOnly',name='traceEnabled',type='SFBool',value=False), field(accessType='inputOnly',name='set_index',type='SFInt32'), field(accessType='initializeOnly',name='index',type='SFInt32'), field(accessType='outputOnly',name='index_changed',type='SFInt32'), field(accessType='inputOnly',name='set_fraction',type='SFFloat'), field(accessType='initializeOnly',name='fraction',type='SFFloat'), field(accessType='inputOnly',name='next',type='SFBool'), field(accessType='inputOnly',name='previous',type='SFBool'), field(accessType='initializeOnly',name='viewedMaterial',type='SFNode', children=[ Material(USE='ViewedMaterialNode')]), field(accessType='initializeOnly',name='defaultMaterial',type='SFNode', children=[ Material(),]), field(accessType='initializeOnly',name='materials',type='MFNode'), field(accessType='inputOnly',appinfo='append another Material node to materials array',name='appendMaterial',type='SFNode'), field(accessType='inputOnly',appinfo='delete Material node indicated by input index value',name='deleteMaterial',type='SFInt32'), field(accessType='inputOnly',appinfo='deletes all Material nodes resets scene to default Material values defined in X3D Specification',name='deleteAllMaterials',type='SFBool'), field(accessType='initializeOnly',name='priorBadIndex',type='SFInt32',value=-1)], IS=IS( connect=[ connect(nodeField='set_index',protoField='set_index'), connect(nodeField='index',protoField='index'), connect(nodeField='index_changed',protoField='index_changed'), connect(nodeField='set_fraction',protoField='set_fraction'), connect(nodeField='fraction',protoField='fraction'), connect(nodeField='next',protoField='next'), connect(nodeField='previous',protoField='previous'), connect(nodeField='materials',protoField='materials'), connect(nodeField='appendMaterial',protoField='appendMaterial'), connect(nodeField='deleteMaterial',protoField='deleteMaterial'), connect(nodeField='deleteAllMaterials',protoField='deleteAllMaterials')]), sourceCode=""" ecmascript: function initialize () { tracePrint ('initialize() begin...'); if (materials[index] != null) { viewedMaterial.diffuseColor = materials[index].diffuseColor; viewedMaterial.emissiveColor = materials[index].emissiveColor; viewedMaterial.specularColor = materials[index].specularColor; viewedMaterial.shininess = materials[index].shininess; viewedMaterial.ambientIntensity = materials[index].ambientIntensity; viewedMaterial.transparency = materials[index].transparency; interpolateMaterialValues(); } else alwaysPrint ('warning: no initialization Material node provided for materials[' + index + ']' + ', materials.length=' + materials.length); tracePrint ('initialize() complete'); } function set_index (value, timestamp) { if (value == index) return; else if ((value >= 0) && (value < materials.length)) { index = value; viewedMaterial.diffuseColor = materials[index].diffuseColor; viewedMaterial.emissiveColor = materials[index].emissiveColor; viewedMaterial.specularColor = materials[index].specularColor; viewedMaterial.shininess = materials[index].shininess; viewedMaterial.ambientIntensity = materials[index].ambientIntensity; viewedMaterial.transparency = materials[index].transparency; interpolateMaterialValues(); index_changed = value; // chain input event to output event tracePrint ('set_index(' + value + ') complete'); } else if (value == -1) { tracePrint ('set_index(' + value + ') out of range, use default Material values'); index = value; viewedMaterial.diffuseColor = defaultMaterial.diffuseColor; viewedMaterial.emissiveColor = defaultMaterial.emissiveColor; viewedMaterial.specularColor = defaultMaterial.specularColor; viewedMaterial.shininess = defaultMaterial.shininess; viewedMaterial.ambientIntensity = defaultMaterial.ambientIntensity; viewedMaterial.transparency = defaultMaterial.transparency; index_changed = value; // chain input event to output event } else if (priorBadIndex != value) { alwaysPrint ('set_index(' + value + ') out of range (only ' + materials.length + ' Material nodes)'); priorBadIndex = value; } } function set_fraction(value, timestamp) { tracePrint ('set_fraction(' + value + ')'); if (fraction != value) // only update if value is changed { fraction = value; interpolateMaterialValues(); } } function interpolateMaterialValues() { f = fraction % 1; // nonzero remainder (modulo 1) within range [0..1] tracePrint ('interpolateMaterialValues(index=' + index + ', fraction=' + fraction + ', f=' + f + ')'); // reset Material index if fraction is above 1 if (Math.floor(fraction) >= 1) { index = Math.floor(fraction); tracePrint ('index=' + index + ' (reset from fraction)'); } fraction = f; // no need to retain integral part of fraction once index is set correspondingly // check OK to proceed if (index < 0) return; // avoid index underflow if (index >= materials.length - 1) return; // avoid index overflow if (f == 0.0) return; // avoid zero interpolation viewedMaterial.diffuseColor.r = materials[index].diffuseColor.r + f * (materials[index+1].diffuseColor.r - materials[index].diffuseColor.r); viewedMaterial.diffuseColor.g = materials[index].diffuseColor.g + f * (materials[index+1].diffuseColor.g - materials[index].diffuseColor.g); viewedMaterial.diffuseColor.b = materials[index].diffuseColor.r + f * (materials[index+1].diffuseColor.b - materials[index].diffuseColor.b); tracePrint ('viewedMaterial.diffuseColor=' + viewedMaterial.diffuseColor); viewedMaterial.emissiveColor.r = materials[index].emissiveColor.r + f * (materials[index+1].emissiveColor.r - materials[index].emissiveColor.r); viewedMaterial.emissiveColor.g = materials[index].emissiveColor.r + f * (materials[index+1].emissiveColor.g - materials[index].emissiveColor.g); viewedMaterial.emissiveColor.b = materials[index].emissiveColor.b + f * (materials[index+1].emissiveColor.b - materials[index].emissiveColor.b); viewedMaterial.specularColor.r = materials[index].specularColor.r + f * (materials[index+1].specularColor.r - materials[index].specularColor.r); viewedMaterial.specularColor.g = materials[index].specularColor.g + f * (materials[index+1].specularColor.g - materials[index].specularColor.g); viewedMaterial.specularColor.b = materials[index].specularColor.b + f * (materials[index+1].specularColor.b - materials[index].specularColor.b); viewedMaterial.shininess = materials[index].shininess + f * (materials[index+1].shininess - materials[index].shininess); viewedMaterial.ambientIntensity = materials[index].ambientIntensity + f * (materials[index+1].ambientIntensity - materials[index].ambientIntensity); viewedMaterial.transparency = materials[index].transparency + f * (materials[index+1].transparency - materials[index].transparency); } function next(trigger) { tracePrint ('next(' + trigger + ')'); if ((trigger == true) && (materials.length > 0)) { value = index + 1; if (value >= materials.length) value = 0; set_index (value); // update Material, test, etc. } } function previous(trigger) { tracePrint ('previous(' + trigger + ')'); if ((trigger == true) && (materials.length > 0)) { value = index - 1; if (value < 0) value = materials.length - 1; set_index (value); // update Material, test, etc. } } function appendMaterial(newMaterial) { tracePrint ('appendMaterial(' + newMaterial + ')'); newMaterialCopy = new SFNode ('Material {}'); tracePrint ('newMaterial.diffuseColor=' + newMaterial.diffuseColor); newMaterialCopy.diffuseColor = newMaterial.diffuseColor; newMaterialCopy.emissiveColor = newMaterial.emissiveColor; newMaterialCopy.specularColor = newMaterial.specularColor; newMaterialCopy.shininess = newMaterial.shininess; newMaterialCopy.ambientIntensity = newMaterial.ambientIntensity; newMaterialCopy.transparency = newMaterial.transparency; materials[materials.length] = newMaterialCopy; set_index (materials.length-1); // update Material, test, etc. } function deleteAllMaterials(trigger) { if ((trigger == true) && (materials.length > 0)) { materials.length = 0; // deletes all objects in materials MFNode array index = 0; // must be different to trigger node reset by set_index() set_index (-1); tracePrint ('deleteAllMaterials() materials.length=' + materials.length); } } function deleteMaterial(materialIndex) { if ((materialIndex >= materials.length) || materialIndex < 0) alwaysPrint ('deleteMaterial(materialIndex=' + materialIndex + ') out of range, materials.length=' + materials.length); else { for (i = materialIndex; i <= materials.length - 2; i++) { materials[i] = materials[i+1]; } materials.length = materials.length - 1; if ((index == 0) && (materialIndex == 0) && (materials.length > 0)) { index = -1; // allow reset back to 0 set_index (0); } else if (index >= materialIndex) set_index (index-1); // decrement to remain same tracePrint ('deleteMaterial(materialIndex=' + materialIndex + ') complete, materials.length=' + materials.length); } } function tracePrint(outputString) { if (traceEnabled) Browser.println ('[MaterialChoice] ' + outputString); } function alwaysPrint(outputString) { Browser.println ('[MaterialChoice] ' + outputString); } """)])), # ==================== Anchor(description='MaterialChoiceExample',url=["MaterialChoiceExample.x3d","https://savage.nps.edu/Savage/Tools/Animation/MaterialChoiceExample.x3d","MaterialChoiceExample.wrl","https://savage.nps.edu/Savage/Tools/Animation/MaterialChoiceExample.wrl"], children=[ Shape( geometry=Text(string=["MaterialChoicePrototype","defines a prototype","","Click text to see example scene","MaterialChoiceExample"], fontStyle=FontStyle(justify=["MIDDLE","MIDDLE"],size=0.9)), appearance=Appearance( material=Material(diffuseColor=(1,1,0.2))))])]) ) # X3D model complete #################################################################################################### # Self-test diagnostics #################################################################################################### print('Self-test diagnostics for MaterialChoicePrototype.py:') if metaDiagnostics(newModel): # built-in utility method in X3D class print(metaDiagnostics(newModel)) # display meta info, hint, warning, error, TODO values in this model # print('check newModel.XML() serialization...') newModelXML= newModel.XML() # test export method XML() for exceptions during export newModel.XMLvalidate() # print(newModelXML) # diagnostic try: # print('check newModel.VRML() serialization...') newModelVRML=newModel.VRML() # test export method VRML() for exceptions during export # print(prependLineNumbers(newModelVRML)) # debug print("Python-to-VRML export of VRML output successful", flush=True) except Exception as err: # usually BaseException # https://stackoverflow.com/questions/18176602/how-to-get-the-name-of-an-exception-that-was-caught-in-python print("*** Python-to-VRML export of VRML output failed:", type(err).__name__, err) if newModelVRML: # may have failed to generate print(prependLineNumbers(newModelVRML, err.lineno)) try: # print('check newModel.JSON() serialization...') newModelJSON=newModel.JSON() # test export method JSON() for exceptions during export # print(prependLineNumbers(newModelJSON)) # debug print("Python-to-JSON export of JSON output successful (under development)") except Exception as err: # usually SyntaxError print("*** Python-to-JSON export of JSON output failed:", type(err).__name__, err) if newModelJSON: # may have failed to generate print(prependLineNumbers(newModelJSON,err.lineno)) print("python MaterialChoicePrototype.py load and self-test diagnostics complete.")