/* X3D Shape Component */ /**************************************************************************** This file is part of the FreeWRL/FreeX3D Distribution. Copyright 2009 CRC Canada. (http://www.crc.gc.ca) FreeWRL/FreeX3D is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. FreeWRL/FreeX3D is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FreeWRL/FreeX3D. If not, see . ****************************************************************************/ #include #include #include #include #include #include "../vrml_parser/Structs.h" #include "../main/headers.h" #include "../opengl/Frustum.h" #include "../opengl/Material.h" #include "../opengl/OpenGL_Utils.h" #include "../opengl/Textures.h" #include "Component_ProgrammableShaders.h" #include "Component_Shape.h" #include "RenderFuncs.h" #include "LinearAlgebra.h" #define NOTHING 0 enum { MAT_NONE = 0, MAT_UNLIT = 1, MAT_REGULAR = 2, MAT_PHYSICAL = 3, }; typedef struct pComponent_Shape{ struct matpropstruct appearanceProperties; /* pointer for a TextureTransform type of node */ struct X3D_Node * this_textureTransform; /* do we have some kind of textureTransform? */ int isBackMaterial; /* for doing shader material properties */ //struct X3D_TwoSidedMaterial *material_twoSided; //struct X3D_Material *material_oneSided; /* Any user defined shaders here? */ struct X3D_Node * userShaderNode; int modulation; int passes; //1 do front&back in onepass 2) do front, then back in separate passes int sides; //0 frnot 1 back 2 both }* ppComponent_Shape; static void *Component_Shape_constructor(){ void *v = MALLOCV(sizeof(struct pComponent_Shape)); memset(v,0,sizeof(struct pComponent_Shape)); return v; } void Component_Shape_init(struct tComponent_Shape *t){ //public //private t->prv = Component_Shape_constructor(); { ppComponent_Shape p = (ppComponent_Shape)t->prv; p->modulation = 1; //0 per specs 1 blend texture and mat 2 blend mat x cpv x texture p->isBackMaterial = 0; p->passes = 2; p->sides = 2; } } void fwl_set_modulation(int modulation){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; p->modulation = modulation; //0 per specs 1 blend texture and mat 2 blend mat x cpv x texture } int fwl_get_modulation(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; return p->modulation; //0 per specs 1 blend texture and mat 2 blend mat x cpv x texture } void fwl_set_material_passes(int passes){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; p->passes = passes; //1 single pass do both sides, 2 do front, back in separate passes } int fwl_get_material_passes(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; return p->passes; //1 single pass do both sides, 2 do front, back in separate passes } //getters struct matpropstruct *getAppearanceProperties(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; return &p->appearanceProperties; } // see if the Appearance node has a valid Shader node as a child. static int hasUserDefinedShader(struct X3D_Node *node) { #define NO_SHADER 99999 unsigned int rv = NO_SHADER; if (node==NULL) return 0; //ConsoleMessage ("hasUserDeginedShader, node ptr %p",node); //ConsoleMessage ("hasUserDefinedShader, nodeType %s",stringNodeType(node->_nodeType)); if (node->_nodeType == NODE_Appearance) { struct X3D_Appearance *ap; POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance *, node, ap); //ConsoleMessage ("appearance node shaders is %d %p",ap->shaders.n, ap->shaders.p); if (ap && ap->shaders.n != 0) { struct X3D_ComposedShader *cps; /* ok - what kind of node is here, and are there more than two? */ if (ap->shaders.n > 1) { ConsoleMessage ("warning, Appearance->shaders has more than 1 node, using only first one"); } POSSIBLE_PROTO_EXPANSION(struct X3D_ComposedShader *, ap->shaders.p[0], cps); //ConsoleMessage ("node type of shaders is :%s:",stringNodeType(cps->_nodeType)); if(cps){ if (cps->_nodeType == NODE_ComposedShader) { // might be set in compile_Shader already //ConsoleMessage ("cps->_initialized %d",cps->_initialized); //ConsoleMessage ("cps->_retrievedURLData %d",cps->_retrievedURLData); if (cps->_retrievedURLData) { if (cps->_shaderUserNumber == -1) cps->_shaderUserNumber = getNextFreeUserDefinedShaderSlot(); rv = cps->_shaderUserNumber; } } else if (cps->_nodeType == NODE_PackagedShader) { // might be set in compile_Shader already if (X3D_PACKAGEDSHADER(cps)->_retrievedURLData) { if (X3D_PACKAGEDSHADER(cps)->_shaderUserNumber == -1) X3D_PACKAGEDSHADER(cps)->_shaderUserNumber = getNextFreeUserDefinedShaderSlot(); rv = X3D_PACKAGEDSHADER(cps)->_shaderUserNumber; } } else if (cps->_nodeType == NODE_ProgramShader) { // might be set in compile_Shader already if (X3D_PROGRAMSHADER(cps)->_retrievedURLData) { if (X3D_PROGRAMSHADER(cps)->_shaderUserNumber == -1) X3D_PROGRAMSHADER(cps)->_shaderUserNumber = getNextFreeUserDefinedShaderSlot(); rv = X3D_PROGRAMSHADER(cps)->_shaderUserNumber; } } else { ConsoleMessage ("shader field of Appearance is a %s, ignoring",stringNodeType(cps->_nodeType)); } } } } //ConsoleMessage ("and ste is %d",ste); // ok - did we find an integer between 0 and some MAX_SUPPORTED_USER_SHADERS value? if (rv == NO_SHADER) rv = 0; //else rv = USER_DEFINED_SHADER_START * (rv+1); //ConsoleMessage ("rv is going to be %x",rv); //ConsoleMessage ("hasUserDefinedShader, returning %p",*shaderTableEntry); return rv; } int hasGeneratedCubeMapTexture(struct X3D_Appearance *appearance){ int ret = FALSE; struct X3D_Appearance *tmpN; POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance *, X3D_NODE(appearance),tmpN) if(tmpN && tmpN->texture) if(tmpN->texture->_nodeType == NODE_GeneratedCubeMapTexture) ret = TRUE; return ret; } // Save the shader node from the render_*Shader so that we can send in parameters when required void setUserShaderNode(struct X3D_Node *me) { ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; p->userShaderNode = me; } struct X3D_Node *getThis_textureTransform(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; return p->this_textureTransform; } void clear_bound_textures(); void push_isBackMaterial(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; p->isBackMaterial += 1; } void pop_isBackMaterial(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; p->isBackMaterial -= 1; } int get_isBackMaterial(){ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; return p->isBackMaterial; } void child_Appearance (struct X3D_Appearance *node) { struct X3D_Node *tmpN; ttglobal tg = gglobal(); /* printf ("in Appearance, this %d, nodeType %d\n",node, node->_nodeType); printf (" vp %d geom %d light %d sens %d blend %d prox %d col %d\n", render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */ /* Render the material node... */ RENDER_MATERIAL_SUBNODES(node->material); if(node->backMaterial){ push_isBackMaterial(); RENDER_MATERIAL_SUBNODES(node->backMaterial); pop_isBackMaterial(); } if (node->fillProperties) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->fillProperties,tmpN); render_node(tmpN); } /* set line widths - if we have line a lineProperties node */ if (node->lineProperties) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->lineProperties,tmpN); render_node(tmpN); } if(node->texture) { /* we have to do a glPush, then restore, later */ /* glPushAttrib(GL_ENABLE_BIT); */ ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; /* is there a TextureTransform? if no texture, fugutaboutit */ POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->textureTransform,p->this_textureTransform); /* now, render the texture */ POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->texture,tmpN); tg->RenderFuncs.texturenode = (void*)tmpN; render_node(tmpN); } /* shaders here/supported?? */ if (node->shaders.n !=0) { int count; int foundGoodShader = FALSE; for (count=0; countshaders.n; count++) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->shaders.p[count], tmpN); /* have we found a valid shader yet? */ if(tmpN){ if (foundGoodShader) { /* printf ("skipping shader %d of %d\n",count, node->shaders.n); */ /* yes, just tell other shaders that they are not selected */ SET_SHADER_SELECTED_FALSE(tmpN); } else { /* render this node; if it is valid, then we call this one the selected one */ SET_FOUND_GOOD_SHADER(tmpN); DEBUG_SHADER("running shader (%s) %d of %d\n", stringNodeType(X3D_NODE(tmpN)->_nodeType),count, node->shaders.n); render_node(tmpN); } } } } /* castle Effects here/supported?? */ if (node->effects.n !=0) { //int count; //int foundGoodShader = FALSE; prep_sibAffectors(X3D_NODE(node),&node->effects); //for (count=0; counteffects.n; count++) { // POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->effects.p[count], tmpN); // // /* have we found a valid shader yet? */ // if(tmpN){ // //if (foundGoodShader) { // // /* printf ("skipping shader %d of %d\n",count, node->shaders.n); */ // // /* yes, just tell other shaders that they are not selected */ // // SET_SHADER_SELECTED_FALSE(tmpN); // //} else { // // /* render this node; if it is valid, then we call this one the selected one */ // // SET_FOUND_GOOD_SHADER(tmpN); // DEBUG_SHADER("running shader (%s) %d of %d\n", // stringNodeType(X3D_NODE(tmpN)->_nodeType),count, node->effects.n); // //render_node(tmpN); // prep_sibAffectors(node,&node->effects); // //} // } //} } } void render_Material (struct X3D_Material *node) { COMPILE_IF_REQUIRED { ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; /* record this node for OpenGL-ES and OpenGL-3.1 operation */ //p->material_oneSided = node; //if(get_isBackMaterial()){ // p->material_twoSided = node; //} if (node != NULL) { if(get_isBackMaterial()){ memcpy (&p->appearanceProperties.fw_BackMaterial, node->_material, sizeof (struct fw_MaterialParameters)); }else{ memcpy (&p->appearanceProperties.fw_FrontMaterial, node->_material, sizeof (struct fw_MaterialParameters)); } } } } //struct X3D_Material *get_material_oneSided(){ // ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; // return p->material_oneSided; //} //struct X3D_TwoSidedMaterial *get_material_twoSided(){ // ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; // return p->material_twoSided; //} /* bounds check the material node fields */ void compile_Material (struct X3D_Material *node) { struct X3D_Node **tnodes; struct fw_MaterialParameters *q; /* verify that the numbers are within range */ node->ambientIntensity = fclamp(node->ambientIntensity,0.0f,1.0f); node->shininess = fclamp(node->shininess,0.0f,1.0f); node->transparency = fclamp(node->transparency,0.0f,1.0f); fvecclamp3f(node->diffuseColor.c,0.0f,1.0f); fvecclamp3f(node->emissiveColor.c,0.0f,1.0f); fvecclamp3f(node->specularColor.c,0.0f,1.0f); if(!node->_material){ node->_material = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_material); } memset(node->_material,0,sizeof(struct fw_MaterialParameters)); q = (struct fw_MaterialParameters *)node->_material; veccopy3f(q->diffuse,node->diffuseColor.c); veccopy3f(q->emissive,node->emissiveColor.c); veccopy3f(q->specular,node->specularColor.c); q->ambient = node->ambientIntensity; q->shininess = node->shininess; q->transparency = node->transparency; q->type = MAT_REGULAR; //new v4 textures // [0] normal [1] emissive [2] diffuse OR baseColor [3] specular/shiny OR metallic/roughness [4] ambient tnodes = q->textures; memset(tnodes,0,5*sizeof(void *)); if(node->normalTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->normalTexture,tnodes[0]); } if(node->emissiveTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->emissiveTexture,tnodes[1]); } if(node->diffuseTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->diffuseTexture,tnodes[2]); } if(node->specularShininessTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->specularShininessTexture,tnodes[3]); } if(node->ambientTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->ambientTexture,tnodes[4]); } int *cindex = q->cindex; cindex[0] = node->normalTextureChannel; cindex[1] = node->emissiveTextureChannel; cindex[2] = node->diffuseTextureChannel; cindex[3] = node->specularShininessTextureChannel; cindex[4] = node->ambientTextureChannel; q->nt = 0; //assume no material.texturexxx to start for(int i=0;i<5;i++){ q->tcount[i] = 0; //default: no texture for this material function q->tstart[i] = q->nt; //shader: start looping over tindex where we left off, for tcount loops if(tnodes[i]){ if(tnodes[i]->_nodeType == NODE_MultiTexture) { struct X3D_MultiTexture *mt = (struct X3D_MultiTexture*)tnodes[i]; q->tcount[i] = mt->texture.n; q->nt += mt->texture.n; q->mt++; }else{ //single texture q->nt++; q->tcount[i] = 1; } } } MARK_NODE_COMPILED } #define CHECK_COLOUR_FIELD(aaa) \ case NODE_##aaa: { \ struct X3D_##aaa *me = (struct X3D_##aaa *)realNode; \ if (me->color == NULL) return NOTHING; /* do not add any properties here */\ else return COLOUR_MATERIAL_SHADER; \ break; \ } #define CHECK_FOGCOORD_FIELD(aaa) \ case NODE_##aaa: { \ struct X3D_##aaa *me = (struct X3D_##aaa *)realNode; \ if (me->fogCoord == NULL) return NOTHING; /* do not add any properties here */\ else return HAVE_FOG_COORDS; \ break; \ } /* if this is a LineSet, PointSet, etc... */ // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/lighting.html#Lightingoff // 'shapes that represent points or lines are unlit static bool getIfLinePoints(struct X3D_Node *realNode) { if (realNode == NULL) return false; switch (realNode->_nodeType) { case NODE_IndexedLineSet: case NODE_LineSet: case NODE_PointSet: case NODE_Polyline2D: case NODE_Polypoint2D: case NODE_Circle2D: case NODE_Arc2D: return true; } return false; // do not add any capabilites here. } /* is the texCoord field a TextureCoordinateGenerator or not? */ struct X3D_Node *getGeomTexCoordField(struct X3D_Node *realGeomNode){ struct X3D_Node *tc = NULL; int *fieldOffsetsPtr = NULL; //ConsoleMessage ("getShapeTextureCoordGen, node type %s\n",stringNodeType(realNode->_nodeType)); if (realGeomNode == NULL) return tc; fieldOffsetsPtr = (int *)NODE_OFFSETS[realGeomNode->_nodeType]; /*go thru all field*/ while (*fieldOffsetsPtr != -1) { if (*fieldOffsetsPtr == FIELDNAMES_texCoord) { // get the pointer stored here... memcpy(&tc,offsetPointer_deref(void*, realGeomNode,*(fieldOffsetsPtr+1)),sizeof(struct X3D_Node *)); break; } fieldOffsetsPtr += FIELDOFFSET_LENGTH; } return tc; } static int getShapeTextureCoordGen(struct X3D_Node *realNode) { struct X3D_Node *tc = NULL; tc = getGeomTexCoordField(realNode); if (tc != NULL) { if (tc->_nodeType == NODE_TextureCoordinateGenerator) return HAVE_TEXTURECOORDINATEGENERATOR; } return 0; } /* Some shapes have Color nodes - if so, then we have other shaders */ static int getShapeColourShader (struct X3D_Node *myGeom) { struct X3D_Node *realNode; POSSIBLE_PROTO_EXPANSION(struct X3D_Node *,myGeom,realNode); if (realNode == NULL) return NOTHING; /* go through each node type that can have a Color node, and if it is not NULL we know we have a Color node */ switch (realNode->_nodeType) { CHECK_COLOUR_FIELD(IndexedFaceSet); CHECK_COLOUR_FIELD(IndexedLineSet); CHECK_COLOUR_FIELD(IndexedTriangleFanSet); CHECK_COLOUR_FIELD(IndexedTriangleSet); CHECK_COLOUR_FIELD(IndexedTriangleStripSet); CHECK_COLOUR_FIELD(LineSet); CHECK_COLOUR_FIELD(PointSet); CHECK_COLOUR_FIELD(TriangleFanSet); CHECK_COLOUR_FIELD(TriangleStripSet); CHECK_COLOUR_FIELD(TriangleSet); CHECK_COLOUR_FIELD(ElevationGrid); CHECK_COLOUR_FIELD(GeoElevationGrid); CHECK_COLOUR_FIELD(QuadSet); CHECK_COLOUR_FIELD(IndexedQuadSet); } /* if we are down here, we KNOW we do not have a color field */ return NOTHING; /* do not add any capabilites here */ } /* Some shapes have Color nodes - if so, then we have other shaders */ static int getShapeFogShader (struct X3D_Node *myGeom) { struct X3D_Node *realNode; POSSIBLE_PROTO_EXPANSION(struct X3D_Node *,myGeom,realNode); if (realNode == NULL) return NOTHING; /* go through each node type that can have a Color node, and if it is not NULL we know we have a Color node */ switch (realNode->_nodeType) { CHECK_FOGCOORD_FIELD(IndexedFaceSet); CHECK_FOGCOORD_FIELD(IndexedLineSet); CHECK_FOGCOORD_FIELD(IndexedTriangleFanSet); CHECK_FOGCOORD_FIELD(IndexedTriangleSet); CHECK_FOGCOORD_FIELD(IndexedTriangleStripSet); CHECK_FOGCOORD_FIELD(LineSet); CHECK_FOGCOORD_FIELD(PointSet); CHECK_FOGCOORD_FIELD(TriangleFanSet); CHECK_FOGCOORD_FIELD(TriangleStripSet); CHECK_FOGCOORD_FIELD(TriangleSet); CHECK_FOGCOORD_FIELD(ElevationGrid); //CHECK_FOGCOORD_FIELD(GeoElevationGrid); CHECK_FOGCOORD_FIELD(QuadSet); CHECK_FOGCOORD_FIELD(IndexedQuadSet); } /* if we are down here, we KNOW we do not have a color field */ return NOTHING; /* do not add any capabilites here */ } void compile_material_if_required(struct X3D_Node *node){ COMPILE_IF_REQUIRED(node); } static int getAppearanceShader (struct X3D_Node *myApp) { struct X3D_Appearance *realAppearanceNode; struct X3D_Node *realMaterialNode, *realBackMaterialNode; ppComponent_Shape pp = (ppComponent_Shape)gglobal()->Component_Shape.prv; // p->passes; //1 single pass do both sides, 2 do front, back in separate passes int retval = NOTHING; /* if there is no appearance node... */ if (myApp == NULL) return retval; POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance *, myApp,realAppearanceNode); if (!realAppearanceNode || realAppearanceNode->_nodeType != NODE_Appearance) return retval; // v4 Appearance.backMaterial (vs v3.3-- TwoSidedMaterial) realMaterialNode = realBackMaterialNode = NULL; if (realAppearanceNode->backMaterial != NULL) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, realAppearanceNode->backMaterial,realBackMaterialNode); } if (realAppearanceNode->material != NULL) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, realAppearanceNode->material,realMaterialNode); } if(realMaterialNode || realBackMaterialNode) { if(1){ struct fw_MaterialParameters *p, *q; int material, texture, multitex, twosided, physical, unlit; material = texture = multitex = twosided = physical = unlit = FALSE; p = q = NULL; if(realMaterialNode && pp->sides % 2 == 1 ){ compile_material_if_required(realMaterialNode); switch(realMaterialNode->_nodeType){ case NODE_Material: { struct X3D_Material *mnode = (struct X3D_Material*)realMaterialNode; p = (struct fw_MaterialParameters*)mnode->_material; material = TRUE; } break; case NODE_PhysicalMaterial: { struct X3D_PhysicalMaterial *mnode = (struct X3D_PhysicalMaterial*)realMaterialNode; p = (struct fw_MaterialParameters*)mnode->_material; physical = TRUE; } break; case NODE_UnlitMaterial: { struct X3D_UnlitMaterial *mnode = (struct X3D_UnlitMaterial*)realMaterialNode; p = (struct fw_MaterialParameters*)mnode->_material; unlit = TRUE; } break; case NODE_TwoSidedMaterial: { struct X3D_TwoSidedMaterial *mnode = (struct X3D_TwoSidedMaterial*)realMaterialNode; p = (struct fw_MaterialParameters*)mnode->_material; q = (struct fw_MaterialParameters*)mnode->_backMaterial; material = TRUE; if(pp->passes = 1) twosided = TRUE; } break; default: break; } } if(realBackMaterialNode && pp->sides / 2 == 1){ compile_material_if_required(realBackMaterialNode); switch(realBackMaterialNode->_nodeType){ case NODE_Material: { struct X3D_Material *mnode = (struct X3D_Material*)realBackMaterialNode; q = (struct fw_MaterialParameters*)mnode->_material; material = TRUE; } break; case NODE_PhysicalMaterial: { struct X3D_PhysicalMaterial *mnode = (struct X3D_PhysicalMaterial*)realBackMaterialNode; q = (struct fw_MaterialParameters*)mnode->_material; physical = TRUE; } break; case NODE_UnlitMaterial: { struct X3D_UnlitMaterial *mnode = (struct X3D_UnlitMaterial*)realBackMaterialNode; q = (struct fw_MaterialParameters*)mnode->_material; unlit = TRUE; } break; case NODE_TwoSidedMaterial: { //not permitted //struct X3D_TwoSidedMaterial *mnode = (struct X3D_TwoSidedMaterial*)realMaterialNode; //p = (struct fw_MaterialParameters*)mnode->_material; //q = (struct fw_MaterialParameters*)mnode->_backMaterial; //unlit = TRUE; } break; default: break; } } if(p && pp->sides % 2 == 1){ if(p->nt) texture = TRUE; if(p->mt) multitex = TRUE; } if(q && pp->sides / 2 == 1){ if(q->nt) texture = TRUE; if(q->mt) multitex = TRUE; if(pp->passes == 1) twosided = TRUE; //not sure it makes sense what we are doing with TWO, should it be pipeline culling CULL_FACE GL_BACK, GL_FRONT? but need now for Appearance.backMaterial } if(twosided) retval |= TWO_MATERIAL_APPEARANCE_SHADER; if(material) retval |= MATERIAL_APPEARANCE_SHADER; if(physical) retval |= PHYSICAL_MATERIAL_APPEARANCE_SHADER; if(unlit) retval |= UNLIT_MATERIAL_APPEARANCE_SHADER; // need to learn | vs xor etc so this isn't un-applied below if there's a regular appearance.texture if(texture) retval |= ONE_TEX_APPEARANCE_SHADER; if(multitex) retval |= MULTI_TEX_APPEARANCE_SHADER; } else { if(realBackMaterialNode || realMaterialNode->_nodeType == NODE_TwoSidedMaterial ) { retval |= TWO_MATERIAL_APPEARANCE_SHADER; } if (realMaterialNode->_nodeType == NODE_Material || (realBackMaterialNode && realBackMaterialNode->_nodeType == NODE_Material)) { retval |= MATERIAL_APPEARANCE_SHADER; } if (realMaterialNode->_nodeType == NODE_PhysicalMaterial || (realBackMaterialNode && realBackMaterialNode->_nodeType == NODE_PhysicalMaterial)) { retval |= PHYSICAL_MATERIAL_APPEARANCE_SHADER; } if (realMaterialNode->_nodeType == NODE_UnlitMaterial || (realBackMaterialNode && realBackMaterialNode->_nodeType == NODE_UnlitMaterial)) { retval |= UNLIT_MATERIAL_APPEARANCE_SHADER; } } } if (realAppearanceNode->fillProperties != NULL) { struct X3D_Node *fp; POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, realAppearanceNode->fillProperties,fp); if(fp){ if (fp->_nodeType != NODE_FillProperties) { ConsoleMessage("getAppearanceShader, fillProperties has a node type of %s",stringNodeType(fp->_nodeType)); } else { // is this a FillProperties node, but is it enabled? if (X3D_FILLPROPERTIES(fp)->_enabled) retval |= FILL_PROPERTIES_SHADER; } } } if (realAppearanceNode->texture != NULL) { //printf ("getAppearanceShader - rap node is %s\n",stringNodeType(realAppearanceNode->texture->_nodeType)); struct X3D_Node *tex; POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, realAppearanceNode->texture,tex); if(tex){ if ((tex->_nodeType == NODE_ImageTexture) || (tex->_nodeType == NODE_MovieTexture) || (tex->_nodeType == NODE_PixelTexture) ){ retval |= ONE_TEX_APPEARANCE_SHADER; } else if( (tex->_nodeType == NODE_PixelTexture3D) || (tex->_nodeType == NODE_ComposedTexture3D) || (tex->_nodeType == NODE_ImageTexture3D) ) { retval |= ONE_TEX_APPEARANCE_SHADER; retval |= TEX3D_SHADER; //VOLUME by default if(tex->_nodeType == NODE_ComposedTexture3D) retval |= TEX3D_LAYER_SHADER; //else VOLUME } else if (tex->_nodeType == NODE_MultiTexture) { retval |= MULTI_TEX_APPEARANCE_SHADER; } else if ((tex->_nodeType == NODE_ComposedCubeMapTexture) || (tex->_nodeType == NODE_ImageCubeMapTexture) || (tex->_nodeType == NODE_GeneratedCubeMapTexture)) { retval |= HAVE_CUBEMAP_TEXTURE; } else { ConsoleMessage ("getAppearanceShader, texture field %s not supported yet\n", stringNodeType(tex->_nodeType)); } } } //#ifdef SHAPE_VERBOSE if(retval == NOTHING) printf("no material appearance\n"); else printf ("getAppearanceShader, returning %x\n",retval); //#endif //SHAPE_VERBOSE return retval; } /* find info on the appearance of this Shape and create a shader */ /* The possible sequence of a properly constructed appearance field is: Shape.appearance -> Appearance Appearance.fillProperties -> FillProperties Appearance.lineProperties -> LineProperties Appearance.material -> Material -> TwoSidedMaterial Appearance.shaders -> ComposedShader Appearance.shaders -> PackagedShader Appearance.shaders -> ProgramShader Appearance.texture -> Texture Appearance.texture -> MultiTexture Appearance.textureTransform -> */ /* now works with our pushing matricies (norm, proj, modelview) but not for complete shader appearance replacement */ void render_FillProperties (struct X3D_FillProperties *node) { GLfloat hatchX; GLfloat hatchY; GLint algor; GLint hatched; GLint filled; struct matpropstruct *me= getAppearanceProperties(); hatchX = 0.80f; hatchY = 0.80f; algor = node->hatchStyle; filled = node->filled; hatched = node->hatched; switch (node->hatchStyle) { case 0: break; /* bricking - not standard X3D */ case 1: hatchX = 1.0f; break; /* horizontal lines */ case 2: hatchY = 1.0f; break; /* vertical lines */ case 3: hatchY=1.0f; break; /* positive sloped lines */ case 4: hatchY=1.0f; break; /* negative sloped lines */ case 5: break; /* square pattern */ case 6: hatchY = 1.0f; break; /* diamond pattern */ default :{ node->hatched = FALSE; /* woops - something wrong here disable */ } } me->filledBool = filled; me->hatchedBool = hatched; me->hatchPercent[0] = hatchX; me->hatchPercent[1] = hatchY; me->hatchScale[0] = node->_hatchScale.c[0]; me->hatchScale[1] = node->_hatchScale.c[1]; me->algorithm = algor; me->hatchColour[0]=node->hatchColor.c[0]; me->hatchColour[1]=node->hatchColor.c[1]; me->hatchColour[2] = node->hatchColor.c[2]; me->hatchColour[3] = 1.0; } void render_LineProperties (struct X3D_LineProperties *node) { #ifdef NEED_TO_ADD_TO_SHADER much of this was working in older versions of FreeWRL, before we went to 100% shader based code. Check FreeWRL from (say) 2011 to see what the shader code looked like GLushort pat; #endif if (node->applied) { //ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; if (node->linewidthScaleFactor > 1.0) { struct matpropstruct *me; glLineWidth(node->linewidthScaleFactor); me= getAppearanceProperties(); me->pointSize = node->linewidthScaleFactor; } #ifdef NEED_TO_ADD_TO_SHADER if (node->linetype > 1) { pat = 0xffff; /* can not support fancy line types - this is the default */ switch (node->linetype) { case 2: pat = 0xff00; break; /* dashed */ case 3: pat = 0x4040; break; /* dotted */ case 4: pat = 0x04ff; break; /* dash dot */ case 5: pat = 0x44fe; break; /* dash dot dot */ case 6: pat = 0x0100; break; /* optional */ case 7: pat = 0x0100; break; /* optional */ case 10: pat = 0xaaaa; break; /* optional */ case 11: pat = 0x0170; break; /* optional */ case 12: pat = 0x0000; break; /* optional */ case 13: pat = 0x0000; break; /* optional */ default: {} } } #endif } } textureTableIndexStruct_s *getTableTableFromTextureNode(struct X3D_Node *textureNode); int getImageChannelCountFromTTI(struct X3D_Node *appearanceNode ){ //int channels, imgalpha, isLit, isUnlitGeometry, hasColorNode, whichShapeColorShader; int channels, imgalpha, haveTexture; struct X3D_Appearance *appearance; channels = 0; imgalpha = 0; haveTexture = 0; POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance*,appearanceNode,appearance); if(appearance){ //do I need possible proto expansione of texture, or will compile_appearance have done that? if(appearance->texture){ //mutli-texture? should loop over multitexture->texture nodes? // --to get to get max channels or hasAlpha, or need channels for each one? // H0: if nay of the multitextures has an alpha, then its alpha replaces material alpha // H1: multitexture alpha is only for composing textures, assumed to take material alpha if(appearance->texture->_nodeType == NODE_MultiTexture || appearance->texture->_nodeType == NODE_ComposedTexture3D ){ int k; struct Multi_Node * mtex = NULL; switch(appearance->texture->_nodeType){ case NODE_MultiTexture: mtex = &((struct X3D_MultiTexture*)appearance->texture)->texture; break; case NODE_ComposedTexture3D: mtex = &((struct X3D_ComposedTexture3D*)appearance->texture)->texture; break; } channels = 0; imgalpha = 0; if(mtex) for(k=0;kn;k++){ textureTableIndexStruct_s *tti = getTableTableFromTextureNode(mtex->p[k]); haveTexture = 1; if(tti){ //new Aug 6, 2016, check LoadTextures.c for your platform channel counting //NoImage=0, Luminance=1, LuminanceAlpha=2, RGB=3, RGBA=4 //PROBLEM: if tti isn't loaded -with #channels, alpha set-, we don't want to compile child channels = max(channels,tti->channels); imgalpha = max(tti->hasAlpha,imgalpha); //if(tti->status < TEX_NEEDSBINDING) // printf("."); //should Unmark node compiled } } }else if(appearance->texture->_nodeType == NODE_ComposedCubeMapTexture){ int k; struct X3D_Node* p[6]; struct X3D_ComposedCubeMapTexture * ccmt; ccmt = (struct X3D_ComposedCubeMapTexture *)appearance->texture; p[0] = ccmt->top; p[1] = ccmt->left; p[2] = ccmt->front; p[3] = ccmt->right; p[4] = ccmt->back; p[5] = ccmt->bottom; for(k=0;k<6;k++){ if(p[k]){ textureTableIndexStruct_s *tti = getTableTableFromTextureNode(p[k]); haveTexture = 1; if(tti){ //new Aug 6, 2016, check LoadTextures.c for your platform channel counting //NoImage=0, Luminance=1, LuminanceAlpha=2, RGB=3, RGBA=4 //PROBLEM: if tti isn't loaded -with #channels, alpha set-, we don't want to compile child channels = max(channels,tti->channels); imgalpha = max(tti->hasAlpha,imgalpha); } } } }else{ //single texture: textureTableIndexStruct_s *tti = getTableTableFromTextureNode(appearance->texture); haveTexture = 1; if(tti){ //new Aug 6, 2016, check LoadTextures.c for your platform channel counting //NoImage=0, Luminance=1, LuminanceAlpha=2, RGB=3, RGBA=4 //PROBLEM: if tti isn't loaded -with #channels, alpha set-, we don't want to compile child channels = tti->channels; imgalpha = tti->hasAlpha; //if(tti->status < TEX_NEEDSBINDING) // printf("."); //should Unmark node compiled } } } } channels = haveTexture ? channels : 0; //-1; return channels; } void initialize_fw_MaterialParameters(struct fw_MaterialParameters *mat){ memset(mat,0,sizeof(struct fw_MaterialParameters)); mat->ambient = .2f; mat->shininess = .2f; mat->type = MAT_NONE; } void initialize_front_and_back_material_params(){ ppComponent_Shape p; ttglobal tg = gglobal(); p = (ppComponent_Shape)tg->Component_Shape.prv; initialize_fw_MaterialParameters(&p->appearanceProperties.fw_FrontMaterial); initialize_fw_MaterialParameters(&p->appearanceProperties.fw_BackMaterial); } //unsigned int getShaderFlags(); shaderflagsstruct getShaderFlags(); struct X3D_Node *getFogParams(); void update_effect_uniforms(); bool setupShaderB(int sides); void textureTransform_start(); void reallyDraw(); void resend_textureprojector_matrix(); void child_Shape (struct X3D_Shape *node) { struct X3D_Node *tmpNG; //int channels; struct X3D_Virt *v; ppComponent_Shape p; ttglobal tg = gglobal(); p = (ppComponent_Shape)tg->Component_Shape.prv; glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); p->passes = 2; p->sides = 3; //3 is bitwise 11 for(int ipass =0; ipass < p->passes; ipass++){ if(p->passes == 2) p->sides = 1 << ipass; //1 or 2 node->_ichange++; COMPILE_IF_REQUIRED glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); if(ipass == 1) glFrontFace(GL_CW); //if(ipass == 1) glCullFace(GL_FRONT); if(node->_shaderflags_base == NOTHING) continue; // in big pass loop /* JAS - if not collision, and render_geom is not set, no need to go further */ /* printf ("child_Shape vp %d geom %d light %d sens %d blend %d prox %d col %d\n", render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */ if(!(node->geometry)) { return; } RECORD_DISTANCE if((renderstate()->render_collision) || (renderstate()->render_sensitive)) { /* only need to forward the call to the child */ POSSIBLE_PROTO_EXPANSION(struct X3D_Node *,node->geometry,tmpNG); render_node(tmpNG); return; } /* initialization. This will get overwritten if there is a texture in an Appearance node in this shape (see child_Appearance) */ tg->RenderFuncs.last_texture_type = NOTEXTURE; tg->RenderFuncs.shapenode = node; /* copy the material stuff in preparation for copying all to the shader */ initialize_front_and_back_material_params(); if((renderstate()->render_cube) && hasGeneratedCubeMapTexture((struct X3D_Appearance*)node->appearance)) return; //don't draw if this node uses a generatedcubemaptexture and its a cubemaptexture generation pass; is there more optimal place to do this? /* now, are we rendering blended nodes or normal nodes?*/ if (renderstate()->render_blend == (node->_renderFlags & VF_Blend)) { int isUserShader; //colorSource, isLit, alphaSource, s_shader_capabilities_t *scap; //unsigned int shader_requirements; shaderflagsstruct shader_requirements; memset(&shader_requirements,0,sizeof(shaderflagsstruct)); //prep_Appearance RENDER_MATERIAL_SUBNODES(node->appearance); //child_Appearance /* enable the shader for this shape */ //ConsoleMessage("turning shader on %x",node->_shaderTableEntry); POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->geometry,tmpNG); shader_requirements.base = node->_shaderflags_base; //_shaderTableEntry; shader_requirements.effects = node->_shaderflags_effects; shader_requirements.usershaders = node->_shaderflags_usershaders; isUserShader = shader_requirements.usershaders ? TRUE : FALSE; // >= USER_DEFINED_SHADER_START ? TRUE : FALSE; //if(!p->userShaderNode || !(shader_requirements >= USER_DEFINED_SHADER_START)){ if(!isUserShader){ //for Luminance and Luminance-Alpha images, we have to tinker a bit in the Vertex shader // New concept of operations Aug 26, 2016 // in the specs there are some things that can replace other things (but not the reverse) // Texture can repace CPV, diffuse and 111 // CPV can replace diffuse and 111 // diffuse can replace 111 // Texture > CPV > Diffuse > (1,1,1) // so there's a kind of order / sequence to it. // There can be a flag at each step saying if you want to replace the prior value (otherwise modulate) // Diffuse replacing or modulating (111) is the same thing, no flag needed // Therefore we need at most 2 flags for color: // TEXTURE_REPLACE_PRIOR and CPV_REPLACE_PRIOR. // and other flag for alpha: ALPHA_REPLACE_PRIOR (same as ! WANT_TEXALPHA) // if all those are false, then its full modulation. // our WANT_LUMINANCE is really == ! TEXTURE_REPLACE_PRIOR // we are missing a CPV_REPLACE_PRIOR, or more precisely this is a default burned into the shader int channels; //modulation: //- for Castle-style full-modulation of texture x CPV x mat.diffuse // and texalpha x (1-mat.trans), set 2 //- for specs table 17-2 RGB Tex replaces CPV with modulation // of table 17-2 entries with mat.diffuse and (1-mat.trans) set 1 //- for specs table 17-3 as written and ignoring modulation sentences // so CPV replaces diffuse, texture replaces CPV and diffuse- set 0 // testing: KelpForest SharkLefty.x3d has CPV, ImageTexture RGB, and mat.diffuse // 29C.wrl has mat.transparency=1 and LumAlpha image, modulate=0 shows sphere, 1,2 inivisble // test all combinations of: modulation {0,1,2} x shadingStyle {gouraud,phong}: 0 looks bright texture only, 1 texture and diffuse, 2 T X C X D int modulation = p->modulation; //freewrl default 1 (dug9 Aug 27, 2016 interpretation of Lighting specs) channels = getImageChannelCountFromTTI(node->appearance); if(modulation == 0) shader_requirements.base |= MAT_FIRST; //strict use of table 17-3, CPV can replace mat.diffuse, so texture > cpv > diffuse > 111 if(shader_requirements.base & COLOUR_MATERIAL_SHADER){ //printf("has a color node\n"); //lets turn it off, and see if we get texture //shader_requirements &= ~(COLOUR_MATERIAL_SHADER); if(modulation == 0) shader_requirements.base |= CPV_REPLACE_PRIOR; } if(channels && (channels == 3 || channels == 4) && modulation < 2) shader_requirements.base |= TEXTURE_REPLACE_PRIOR; //if the image has a real alpha, we may want to turn off alpha modulation, // see comment about modulate in Compositing_Shaders.c if(channels && (channels == 2 || channels == 4) && modulation == 0) shader_requirements.base |= TEXALPHA_REPLACE_PRIOR; //getShaderFlags() are from non-leaf-node shader influencers: // fog, local_lights, clipplane, Effect/EffectPart (for CastlePlugs) ... // - as such they may be different for the same shape node DEF/USEd in different branches of the scenegraph // - so they are ORd here before selecting a shader permutation shader_requirements.base |= getShaderFlags().base; shader_requirements.effects |= getShaderFlags().effects; //if(shader_requirements & FOG_APPEARANCE_SHADER) // printf("fog in child_shape\n"); } //printf("child_shape shader_requirements base %d effects %d user %d\n",shader_requirements.base,shader_requirements.effects,shader_requirements.usershaders); scap = getMyShaders(shader_requirements); enableGlobalShader(scap); //enableGlobalShader (getMyShader(shader_requirements)); //node->_shaderTableEntry)); //see if we have to set up a TextureCoordinateGenerator type here if (tmpNG && tmpNG->_intern) { if (tmpNG->_intern->tcoordtype == NODE_TextureCoordinateGenerator) { getAppearanceProperties()->texCoordGeneratorType = tmpNG->_intern->texgentype; //ConsoleMessage("shape, matprop val %d, geom val %d",getAppearanceProperties()->texCoordGeneratorType, node->geometry->_intern->texgentype); } } //userDefined = (whichOne >= USER_DEFINED_SHADER_START) ? TRUE : FALSE; //if (p->userShaderNode != NULL && shader_requirements >= USER_DEFINED_SHADER_START) { if(isUserShader && p->userShaderNode){ //we come in here right after a COMPILE pass in APPEARANCE which renders the shader, which sets p->userShaderNode //if nothing changed with appearance -no compile pass- we don't come in here again //ConsoleMessage ("have a shader of type %s",stringNodeType(p->userShaderNode->_nodeType)); switch (p->userShaderNode->_nodeType) { case NODE_ComposedShader: if (X3D_COMPOSEDSHADER(p->userShaderNode)->isValid) { if (!X3D_COMPOSEDSHADER(p->userShaderNode)->_initialized) { sendInitialFieldsToShader(p->userShaderNode); } } break; case NODE_ProgramShader: if (X3D_PROGRAMSHADER(p->userShaderNode)->isValid) { if (!X3D_PROGRAMSHADER(p->userShaderNode)->_initialized) { sendInitialFieldsToShader(p->userShaderNode); } } break; case NODE_PackagedShader: if (X3D_PACKAGEDSHADER(p->userShaderNode)->isValid) { if (!X3D_PACKAGEDSHADER(p->userShaderNode)->_initialized) { sendInitialFieldsToShader(p->userShaderNode); } } break; } } //update effect field uniforms if(shader_requirements.effects){ update_effect_uniforms(); } #ifdef SHAPEOCCLUSION beginOcclusionQuery((struct X3D_VisibilitySensor*)node,renderstate()->render_geom); //BEGINOCCLUSIONQUERY; #endif //call stack to get from child_shape to sendMaterialsToShader and sendLightInfo as of Aug 13, 2016 //child_shape //- render_node //-- render_indexedfaceset (or other specific shape) //--- render_polyrep //---- sendArraysToGPU (or sendElementsToGPU) //----- setupShader //------ sendMaterialsToShader // Uniforms being sent for materials and hatching //--------- sendLightInfo // Uniforms sent for lights //----- glDrawArrays/glDrawElements //we have a shader, now start sending it data //clear_bound_textures(); //testing only clear_textureUnit_used(); //appearance.texture material.textureXXX, PTMs.texture all need TEXTURE0+ XXX, where xxx starts from 0 clear_material_samplers(); //PTM and material.textureXXX share frag shader sampler2D textureUnit[16] array textureTransform_start(); //send regular appearance.textures to shader resend_textureprojector_matrix(); setupShaderB(p->sides); //send materials, fill patters miscalaneous to shader //print_bound_textures("s"); //testing only, uncomment clear_bound_textues too render_node(tmpNG); //printf("%s",stringNodeType(tmpNG->_nodeType)); reallyDraw(); FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0); FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0); textureTransform_end(); #ifdef SHAPEOCCLUSION endOcclusionQuery((struct X3D_VisibilitySensor*)node,renderstate()->render_geom); //ENDOCCLUSIONQUERY; #endif //fin_Appearance if(node->appearance){ struct X3D_Appearance *tmpA; POSSIBLE_PROTO_EXPANSION(struct X3D_Appearance *,node->appearance,tmpA); if(tmpA->effects.n) fin_sibAffectors(X3D_NODE(tmpA),&tmpA->effects); } } /* any shader turned on? if so, turn it off */ //ConsoleMessage("turning shader off"); finishedWithGlobalShader(); //p->material_twoSided = NULL; //p->material_oneSided = NULL; p->userShaderNode = NULL; } // material passes glFrontFace(GL_CCW); tg->RenderFuncs.shapenode = NULL; /* load the identity matrix for textures. This is necessary, as some nodes have TextureTransforms and some don't. So, if we have a TextureTransform, loadIdentity */ if (p->this_textureTransform) { p->this_textureTransform = NULL; FW_GL_MATRIX_MODE(GL_TEXTURE); FW_GL_LOAD_IDENTITY(); FW_GL_MATRIX_MODE(GL_MODELVIEW); } /* LineSet, PointSets, set the width back to the original. */ { float gl_linewidth = tg->Mainloop.gl_linewidth; glLineWidth(gl_linewidth); p->appearanceProperties.pointSize = gl_linewidth; } /* did the lack of an Appearance or Material node turn lighting off? */ LIGHTING_ON; /* turn off face culling */ DISABLE_CULL_FACE; } void compile_Shape (struct X3D_Shape *node) { int whichAppearanceShader = 0; int whichShapeColorShader = 0; int whichShapeFogShader = 0; bool isUnlitGeometry = false; int hasTextureCoordinateGenerator = 0; int whichUnlitGeometry = 0; struct X3D_Node *tmpN = NULL; struct X3D_Node *tmpG = NULL; // struct X3D_Appearance *appearance = NULL; int userDefinedShader = 0; // int colorSource, alphaSource, channels, isLit; // ConsoleMessage ("**** Compile Shape ****"); POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->geometry,tmpG); whichShapeColorShader = getShapeColourShader(tmpG); whichShapeFogShader = getShapeFogShader(tmpG); isUnlitGeometry = getIfLinePoints(tmpG); hasTextureCoordinateGenerator = getShapeTextureCoordGen(tmpG); POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->appearance,tmpN); /* first - does appearance have a shader node? */ userDefinedShader = hasUserDefinedShader(tmpN); //Aug 26 2016 change: don't fiddle around with flags here, rather report the state of the node // - then do any fiddling in child_shape, when we have the channel count and modulation flag if(isUnlitGeometry) whichUnlitGeometry = HAVE_LINEPOINTS_COLOR; whichAppearanceShader = getAppearanceShader(tmpN); /* in case we had no appearance, etc, we do the bland NO_APPEARANCE_SHADER */ //node->_shaderTableEntry= node->_shaderflags_base = (whichShapeColorShader | whichShapeFogShader | whichAppearanceShader | hasTextureCoordinateGenerator | whichUnlitGeometry ); //| userDefinedShader); node->_shaderflags_usershaders = userDefinedShader; node->_shaderflags_effects = 0; //if (node->_shaderTableEntry == NOTHING) // node->_shaderTableEntry = NO_APPEARANCE_SHADER; if (node->_shaderflags_base == NOTHING) node->_shaderflags_base = NO_APPEARANCE_SHADER; //printf ("compile_Shape, node->_shaderTableEntry is %x\n",node->_shaderTableEntry); MARK_NODE_COMPILED } //void register_node_gc(void *node, void *p); void compile_TwoSidedMaterial (struct X3D_TwoSidedMaterial *node) { float *p; struct fw_MaterialParameters *q; /* verify that the numbers are within range */ node->ambientIntensity = fclamp(node->ambientIntensity,0.0f,1.0f); node->shininess = fclamp(node->shininess,0.0f,1.0f); node->transparency = fclamp(node->transparency,0.0f,1.0f); fvecclamp3f(node->diffuseColor.c,0.0f,1.0f); fvecclamp3f(node->emissiveColor.c,0.0f,1.0f); fvecclamp3f(node->specularColor.c,0.0f,1.0f); if(!node->_material){ node->_material = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_material); } memset(node->_material,0,sizeof(struct fw_MaterialParameters)); q = (struct fw_MaterialParameters *)node->_material; veccopy3f(q->diffuse,node->diffuseColor.c); veccopy3f(q->emissive,node->emissiveColor.c); veccopy3f(q->specular,node->specularColor.c); q->ambient = node->ambientIntensity; q->shininess = node->shininess; q->transparency = node->transparency; q->type = MAT_REGULAR; if (node->separateBackColor) { node->backAmbientIntensity = fclamp(node->backAmbientIntensity,0.0f,1.0f); node->backShininess = fclamp(node->backShininess,0.0f,1.0f); node->backTransparency = fclamp(node->backTransparency,0.0f,1.0f); fvecclamp3f(node->backDiffuseColor.c,0.0f,1.0f); fvecclamp3f(node->backEmissiveColor.c,0.0f,1.0f); fvecclamp3f(node->backSpecularColor.c,0.0f,1.0f); if(!node->_backMaterial){ node->_backMaterial = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_backMaterial); } memset(node->_backMaterial,0,sizeof(struct fw_MaterialParameters)); q = (struct fw_MaterialParameters *)node->_backMaterial; veccopy3f(q->diffuse,node->backDiffuseColor.c); veccopy3f(q->emissive,node->backEmissiveColor.c); veccopy3f(q->specular,node->backSpecularColor.c); q->ambient = node->backAmbientIntensity; q->shininess = node->backShininess; q->transparency = node->backTransparency; q->type = MAT_REGULAR; } else { /* just copy the front materials to the back */ if(!node->_backMaterial){ node->_backMaterial = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_backMaterial); } memset(node->_backMaterial,0,sizeof(struct fw_MaterialParameters)); memcpy(node->_backMaterial,node->_material,sizeof(struct fw_MaterialParameters)); } MARK_NODE_COMPILED } void render_TwoSidedMaterial (struct X3D_TwoSidedMaterial *node) { COMPILE_IF_REQUIRED { ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; if (node != NULL) { memcpy (&p->appearanceProperties.fw_FrontMaterial, node->_material, sizeof (struct fw_MaterialParameters)); memcpy (&p->appearanceProperties.fw_BackMaterial, node->_backMaterial, sizeof (struct fw_MaterialParameters)); } } } void compile_UnlitMaterial (struct X3D_UnlitMaterial *node) { struct X3D_Node **tnodes; struct fw_MaterialParameters *q; /* verify that the numbers are within range */ node->transparency = fclamp(node->transparency,0.0f,1.0f); fvecclamp3f(node->emissiveColor.c,0.0f,1.0f); if(!node->_material){ node->_material = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_material); } memset(node->_material,0,sizeof(struct fw_MaterialParameters)); q = (struct fw_MaterialParameters *)node->_material; veccopy3f(q->emissive,node->emissiveColor.c); q->transparency = node->transparency; q->type = MAT_UNLIT; //new v4 textures // [0] normal [1] emissive [2] diffuse OR baseColor [3] specular/shiny OR metallic/roughness [4] ambient tnodes = q->textures; memset(tnodes,0,5*sizeof(void *)); if(node->normalTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->normalTexture,tnodes[0]); } if(node->emissiveTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->emissiveTexture,tnodes[1]); } int *cindex = q->cindex; cindex[0] = node->normalTextureChannel; cindex[1] = node->emissiveTextureChannel; q->nt = 0; //assume no material.texturexxx to start for(int i=0;i<5;i++){ q->tcount[i] = 0; //default: no texture for this material function q->tstart[i] = q->nt; //shader: start looping over tindex where we left off, for tcount loops if(tnodes[i]){ if(tnodes[i]->_nodeType == NODE_MultiTexture) { struct X3D_MultiTexture *mt = (struct X3D_MultiTexture*)tnodes[i]; q->tcount[i] = mt->texture.n; q->nt += mt->texture.n; q->mt++; }else{ //single texture q->nt++; q->tcount[i] = 1; } } } MARK_NODE_COMPILED } void render_UnlitMaterial (struct X3D_UnlitMaterial *node) { COMPILE_IF_REQUIRED { ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; if (node != NULL) { memcpy (&p->appearanceProperties.fw_FrontMaterial, node->_material, sizeof (struct fw_MaterialParameters)); } } } /* PBR Physics Based Rendering https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materials - describes how to do BRDF calculations (don't I have a book on BRDF? with shaders?) https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-b-brdf-implementation - Appendix B shows the BRDF math, and link to example viewer implementation: https://github.com/KhronosGroup/glTF-Sample-Viewer/ https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/metallic-roughness.frag - implements BRDF in frag, including ifdefs for 'maps' vs scalars. https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf - Schlick BRDF model example x3dom: https://github.com/x3dom/x3dom/blob/master/src/nodes/Shape/PhysicalMaterial.js exmaple CGE: https://github.com/castle-engine/castle-engine/blob/master/src/x3d/opengl/glsl/source/lighting_model_physical/shading_phong.fs H: specular-glossiness and metallic-roughness are different ways to declare the same thing so only one is needed. And since web3d does specular-glossiness in the regular material, no need for it in the physical. PhysicalMaterialNode: the textures are optional, and have specific packing of effects occlusionRoughnessMetallicTexture (occlusion=R,Roughness=G,Metallic=B) There are a lot of (optional) textures with this, with the v4 extended Material node, and with PTM projective texture mapping. IDEA: generalize what we did with PTM: - have a generic list of sampler2D textureUnit[xx] - and through a separate int32 array say which textureUnit goes with which texture. That would allow combining PTM and (PhysicalMaterial or Matierial with textures) -- in a flexible way that minimizes (GPU limited resource) sampler2Ds GPU textureUnits / samplers needed: Gross: 7 max needed: 4 (assuming physics channel packing): normal, emissive, baseColor, occlusion-metallic-roughness possible Array-ization assuming same widthxheight for all: 1 samplerArray for the physics, 1 sampler2D for baseColorTexture */ void compile_PhysicalMaterial (struct X3D_PhysicalMaterial *node) { struct X3D_Node **tnodes; struct fw_MaterialParameters *q; /* verify that the numbers are within range */ node->roughness = fclamp(node->roughness,0.0f,1.0f); node->metallic = fclamp(node->metallic,0.0f,1.0f); node->transparency = fclamp(node->transparency,0.0f,1.0f); fvecclamp3f(node->baseColor.c,0.0f,1.0f); fvecclamp3f(node->emissiveColor.c,0.0f,1.0f); if(!node->_material){ node->_material = malloc(sizeof(struct fw_MaterialParameters)); register_node_gc(node,node->_material); } memset(node->_material,0,sizeof(struct fw_MaterialParameters)); q = (struct fw_MaterialParameters *)node->_material; veccopy3f(q->baseColor,node->baseColor.c); veccopy3f(q->emissive,node->emissiveColor.c); q->metallic = node->metallic; q->roughness = node->roughness; q->transparency = node->transparency; q->type = MAT_PHYSICAL; //new v4 textures // [0] normal [1] emissive [2] diffuse OR baseColor [3] specular/shiny OR metallic/roughness [4] ambient tnodes = q->textures; memset(tnodes,0,5*sizeof(void *)); if(node->normalTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->normalTexture,tnodes[0]); } if(node->emissiveTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->emissiveTexture,tnodes[1]); } if(node->baseTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->baseTexture,tnodes[2]); } if(node->metallicRoughnessTexture) { POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->metallicRoughnessTexture,tnodes[3]); } int *cindex = q->cindex; cindex[0] = node->normalTextureChannel; cindex[1] = node->emissiveTextureChannel; cindex[2] = node->baseTextureChannel; cindex[3] = node->metallicRoughnessTextureChannel; q->nt = 0; //assume no material.texturexxx to start for(int i=0;i<5;i++){ q->tcount[i] = 0; //default: no texture for this material function q->tstart[i] = q->nt; //shader: start looping over tindex where we left off, for tcount loops if(tnodes[i]){ if(tnodes[i]->_nodeType == NODE_MultiTexture) { struct X3D_MultiTexture *mt = (struct X3D_MultiTexture*)tnodes[i]; q->tcount[i] = mt->texture.n; q->nt += mt->texture.n; q->mt++; }else{ //single texture q->nt++; q->tcount[i] = 1; } } } MARK_NODE_COMPILED } void render_PhysicalMaterial (struct X3D_PhysicalMaterial *node) { COMPILE_IF_REQUIRED { ppComponent_Shape p = (ppComponent_Shape)gglobal()->Component_Shape.prv; if (node != NULL) { memcpy (&p->appearanceProperties.fw_FrontMaterial, node->_material, sizeof (struct fw_MaterialParameters)); } } }