/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ /* */ #include #include #include #include static Error ShadeField(Field, float, int, float, float); extern Error _dxfNormalsObject(Object, char *); static Error DoNormals(Object, int, char *, int); static Error ShadeIt(Object, int, char *, float, int, float, float, int); static Error CheckNormalsDirection(Object, int); static Error CheckNormalsDirectionField(Field, int); Error m_Shade(Object *in, Object *out) { int shade, shininess, flipfront; char *how; float specular, ambient, diffuse; Object outo=NULL; if (!in[0]) { DXSetError(ERROR_BAD_PARAMETER, "object must be specified"); return ERROR; } /* shade param */ if (!in[1]) { shade = 1; } else { if (!DXExtractInteger(in[1], &shade)) { DXSetError(ERROR_BAD_PARAMETER,"shade must be either 0 or 1"); goto error; } } if ((shade < 0)||(shade > 1)) { DXSetError(ERROR_BAD_PARAMETER,"shade must be either 0 or 1"); goto error; } /* how param */ if (!in[2]) { how = "default"; } else { if (!DXExtractString(in[2], &how)) { DXSetError(ERROR_BAD_PARAMETER, "how must be either `faceted' or `smooth'"); goto error; } /* XXX convert to lower case */ if (strcmp(how,"faceted")&&(strcmp(how,"smooth"))) { DXSetError(ERROR_BAD_PARAMETER, "how must be either `faceted' or `smooth'"); goto error; } } if (in[2] && (shade==0)) { DXWarning("how is ignored because shade is equal to 0"); } /* specular param */ if (!in[3]) { specular = -1.0; } else { if (!DXExtractFloat(in[3], &specular)) { DXSetError(ERROR_BAD_PARAMETER, "specular must be a non-negative scalar value"); goto error; } if (specular < 0) { DXSetError(ERROR_BAD_PARAMETER, "specular must be a non-negative scalar value"); goto error; } } /* shininess */ if (!in[4]) { shininess = -1; } else { if (!DXExtractInteger(in[4], &shininess)) { DXSetError(ERROR_BAD_PARAMETER, "shininess must be a non-negative integer"); goto error; } if (shininess < 0) { DXSetError(ERROR_BAD_PARAMETER, "shininess must be a non-negative integer"); goto error; } } /* diffuse param */ if (!in[5]) { diffuse = -1.0; } else { if (!DXExtractFloat(in[5], &diffuse)) { DXSetError(ERROR_BAD_PARAMETER, "diffuse must be a non-negative scalar value"); goto error; } if (diffuse < 0) { DXSetError(ERROR_BAD_PARAMETER, "diffuse must be a non-negative scalar value"); goto error; } } /* ambient param */ if (!in[6]) { ambient = -1.0; } else { if (!DXExtractFloat(in[6], &ambient)) { DXSetError(ERROR_BAD_PARAMETER, "ambient must be a non-negative scalar value"); goto error; } if (ambient < 0) { DXSetError(ERROR_BAD_PARAMETER, "ambient must be a non-negative scalar value"); goto error; } } /* whether or not to flip front and back */ flipfront = 0; if (!(outo = DXCopy(in[0], COPY_STRUCTURE))) goto error; /* do the stuff with normals (must be done at the field level) */ if (!ShadeIt(outo, shade, how, specular, shininess, diffuse, ambient, flipfront)) goto error; out[0] = outo; return OK; error: DXDelete((Object)outo); return ERROR; } static Error ShadeIt(Object obj, int shade, char *how, float specular, int shininess, float diffuse, float ambient, int flipfront) { Object child; int i; switch (DXGetObjectClass(obj)) { case (CLASS_FIELD): /* do the normals stuff here */ if (!DoNormals((Object)obj, shade, how, flipfront)) goto error; if (!ShadeField((Field)obj, specular, shininess, diffuse, ambient)) goto error; break; case (CLASS_GROUP): switch (DXGetGroupClass((Group)obj)) { case (CLASS_COMPOSITEFIELD): /* do the normals stuff here, then continue through the composite field */ if (!DoNormals((Object)obj, shade, how, flipfront)) goto error; for (i=0; child = DXGetEnumeratedMember((Group)obj,i,NULL);i++){ if (!ShadeField((Field)child, specular, shininess, diffuse, ambient)) goto error; } break; default: for (i=0; child = DXGetEnumeratedMember((Group)obj,i,NULL); i++) { if (!ShadeIt(child, shade, how, specular, shininess, diffuse, ambient, flipfront)) goto error; } break; } break; case (CLASS_XFORM): if (!DXGetXformInfo((Xform)obj, &child, NULL)) goto error; if (!ShadeIt(child, shade, how, specular, shininess, diffuse, ambient, flipfront)) goto error; break; case (CLASS_CLIPPED): if (!DXGetClippedInfo((Clipped)obj, &child, NULL)) goto error; if (!ShadeIt(child, shade, how, specular, shininess, diffuse, ambient, flipfront)) goto error; break; case (CLASS_SCREEN): if (!DXGetScreenInfo((Screen)obj, &child, NULL, NULL)) goto error; if (!ShadeIt(child, shade, how, specular, shininess, diffuse, ambient, flipfront)) goto error; break; default: DXSetError(ERROR_INVALID_DATA, "object must be a field, group, xform, or clipped object"); goto error; } return OK; error: return ERROR; } static Error ShadeField(Field field, float specular, int shininess, float diffuse, float ambient) { Array normals; char *attr; /* set all the rendering attributes */ if (specular != -1.0) if (!DXSetFloatAttribute((Object)field, "specular", specular)) goto error; if (shininess != -1) if (!DXSetIntegerAttribute((Object)field, "shininess", shininess)) goto error; if (diffuse != -1) if (!DXSetFloatAttribute((Object)field, "diffuse", diffuse)) goto error; if (ambient != -1) if (!DXSetFloatAttribute((Object)field, "ambient", ambient)) goto error; if (!DXEndField(field)) goto error; return OK; error: return ERROR; } static Error DoNormals(Object obj, int shade, char *how, int flipfront) { int i; char *attr; Field first, child; int shadeattribute; /* this is called at the Field or Composite field level */ switch (DXGetObjectClass(obj)) { case CLASS_FIELD: /* if we don't want the object shaded... */ /* regardless of what how is, make sure either there aren't any normals, * or if there are, that the "no shade" attribute is set */ if (shade == 0) { /* if there are normals, we need to disable them */ if (DXGetComponentValue((Field)obj, "normals")) { if (!DXSetIntegerAttribute((Object)obj, "shade", 0)) goto error; } } /* we want the object shaded */ else if (shade==1) { /* if there's a shade=0 attribute set, set it to 1 */ if (DXGetAttribute((Object)obj,"shade")) { if (!DXGetIntegerAttribute((Object)obj, "shade", &shadeattribute)) goto error; if (shadeattribute==0) { /* set it to 1 */ if (!DXSetIntegerAttribute((Object)obj, "shade", 1)) goto error; } else if (shadeattribute != 1) { DXSetError(ERROR_INVALID_DATA, "invalid shade attribute, must be 0 or 1"); goto error; } } if (!strcmp(how, "default")) { /* only do something if there aren't any normals. otherwise * do nothing */ if (!DXGetComponentValue((Field)obj, "normals")) { /* follow data if present */ if (DXGetComponentValue((Field)obj,"data")) { attr = DXGetString((String)DXGetComponentAttribute((Field)obj, "data","dep")); if (!attr) { DXSetError(ERROR_MISSING_DATA, "missing data dependent attribute"); goto error; } } else { attr = "positions"; } if (!_dxfNormalsObject((Object)obj, attr)) goto error; } else { /* check whether the normals are consistent with the connections */ if (!CheckNormalsDirection(obj, flipfront)) goto error; } } else if (!strcmp(how,"faceted")) { /* call normals dep connections (make sure this routine does the * check whether they are there already) */ if (!_dxfNormalsObject((Object)obj,"connections")) goto error; } else if (!strcmp(how,"smooth")) { /* call normals dep positions (make sure this routine does the * check whether they are there already) */ if (!_dxfNormalsObject((Object)obj,"positions")) goto error; } break; case (CLASS_GROUP): /* it's a composite field */ if (shade == 0) { /* if there are normals, we need to disable them */ first = (Field)DXGetEnumeratedMember((Group)obj, 0, NULL); /* if there are normals, we need to disable them */ if (DXGetComponentValue(first, "normals")) { for (i=0; child = (Field)DXGetEnumeratedMember((Group)obj,i,NULL);i++){ if (!DXSetIntegerAttribute((Object)child, "shade", 0)) goto error; } } } /* we want the object shaded */ else if (shade==1) { if (!strcmp(how, "default")) { /* only do something if there aren't any normals. otherwise * do nothing */ first = (Field)DXGetEnumeratedMember((Group)obj, 0, NULL); if (!DXGetComponentValue(first, "normals")) { /* follow data if present */ if (DXGetComponentValue(first,"data")) { attr = DXGetString((String)DXGetComponentAttribute(first, "data", "dep")); if (!attr) { DXSetError(ERROR_MISSING_DATA, "missing data dependent attribute"); goto error; } } else { attr = "positions"; } /* call normals on the composite field */ if (!_dxfNormalsObject(obj, attr)) goto error; } else { /* check whether the normals are consistent with the connections */ if (!CheckNormalsDirection(obj, flipfront)) goto error; } } else if (!strcmp(how,"faceted")) { /* call normals dep connections (make sure this routine does the * check whether they are there already) */ if (!_dxfNormalsObject((Object)obj,"connections")) goto error; } else if (!strcmp(how,"smooth")) { /* call normals dep positions (make sure this routine does the * check whether they are there already) */ if (!_dxfNormalsObject((Object)obj,"positions")) goto error; } } } break; } return OK; error: return ERROR; } /* this routine checks the normals against the direction of the connections. If they disagree, it fixes the normals. This can be called on either a field or a composite field. */ static Error CheckNormalsDirection(Object obj, int flipfront) { int i; Object child; switch (DXGetObjectClass(obj)) { case (CLASS_FIELD): if (!CheckNormalsDirectionField((Field)obj, flipfront)) goto error; break; case (CLASS_GROUP): if (DXGetGroupClass((Group)obj) != CLASS_COMPOSITEFIELD) { DXSetError(ERROR_UNEXPECTED,"unexpected class in CheckNormalsDirection"); goto error; } for (i=0; child = DXGetEnumeratedMember((Group)obj,i,NULL);i++){ if (!CheckNormalsDirectionField((Field)child, flipfront)) goto error; } break; default: DXSetError(ERROR_UNEXPECTED,"unexpected class in CheckNormalsDirection"); goto error; } return OK; error: return ERROR; } static Error CheckNormalsDirectionField(Field obj, int flipfront) { Array connections, normals, positions, newnormals=NULL, newconnections=NULL; Point *pos1, *pos2, *pos3, *norm, vec1, vec2, crossprod, newnormal; Point position1, position2, position3; Vector zerovec={0.0, 0.0, 0.0}; char *str1, *str2; Object dep, eType; ArrayHandle positionshandle=NULL, normalshandle=NULL, connectionshandle=NULL; int rank, shape[10], *connections_ptr, conn1, conn2, conn3; int numitems, i, *cptr1, *cptr2, *cptr3, numcon, scratchtri[3]; int scratchquad[4], dim, counts[3]; float dotprod, scratch[3]; Triangle newtri, *triptr; Quadrilateral newquad, *quadptr; /* get the connections component */ connections = (Array)DXGetComponentValue(obj, "connections"); if (!connections) { goto done; } if (!DXGetArrayInfo(connections, &numcon, NULL, NULL, NULL, NULL)) goto error; /* get the element type attribute */ eType = DXGetAttribute((Object)connections, "element type"); if (! eType || DXGetObjectClass(eType) != CLASS_STRING) { DXSetError(ERROR_INVALID_DATA, "invalid or missing element type attribute"); goto error; } str1 = DXGetString((String)eType); /* what about faces XXX ? */ if (strcmp(str1, "quads") && strcmp(str1, "triangles")) goto done; connectionshandle = DXCreateArrayHandle(connections); if (!connectionshandle) goto error; /* first do the flipping on the connections. That way we only flip the normals once */ if (flipfront) { if (!strcmp(str1,"triangles")) { newconnections = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 3); if (!newconnections) goto error; if (!DXAddArrayData(newconnections, 0, numcon, NULL)) goto error; for (i=0; ip; newtri.q = triptr->r; newtri.r = triptr->q; if (!DXAddArrayData(newconnections, i, 1, &newtri)) goto error; } } else { /* quads */ /* else I'll treat it as irregular XXX */ newconnections = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 4); if (!newconnections) goto error; if (!DXAddArrayData(newconnections, 0, numcon, NULL)) goto error; for (i=0; ir; newquad.q = quadptr->s; newquad.r = quadptr->p; newquad.s = quadptr->q; if (!DXAddArrayData(newconnections, i, 1, &newquad)) goto error; } } if (!DXSetComponentValue(obj, "connections", (Object)newconnections)) goto error; newconnections = NULL; DXChangedComponentValues(obj,"connections"); connections = (Array)DXGetComponentValue(obj, "connections"); connectionshandle = DXCreateArrayHandle(connections); if (!connectionshandle) goto error; } /* get the dependency attribute of the normals */ normals = (Array)DXGetComponentValue(obj, "normals"); if (!normals) { /* I don't think this should happen, but if it does, it's ok */ goto done; } dep = DXGetAttribute((Object)normals, "dep"); if (! dep || DXGetObjectClass(dep) != CLASS_STRING) { DXSetError(ERROR_INVALID_DATA, "invalid or missing normals dep attribute"); goto error; } str2 = DXGetString((String)dep); if (strcmp(str2, "positions") && strcmp(str2, "connections")) { DXSetError(ERROR_INVALID_DATA,"unrecognized normals dep attribute"); goto error; } positions = (Array)DXGetComponentValue(obj, "positions"); if (!positions) { goto done; } positionshandle = DXCreateArrayHandle(positions); if (!positionshandle) goto error; if (!DXGetArrayInfo(positions, NULL, NULL, NULL, &rank, shape)) goto error; if (rank != 1) { DXSetError(ERROR_INVALID_DATA,"rank %d positions not supported", rank); goto error; } normalshandle = DXCreateArrayHandle(normals); if (!normalshandle) goto error; if (!DXGetArrayInfo(normals, &numitems, NULL, NULL, NULL, NULL)) goto error; if ((shape[0] != 2)&&(shape[0] != 3)) { DXSetError(ERROR_INVALID_DATA, "only 2D and 3D positions supported for quads and triangles"); goto error; } if (!strcmp(str1, "triangles")) { /* check the first triangle */ connections_ptr = (int *)DXGetArrayData(connections); conn1 = connections_ptr[0]; conn2 = connections_ptr[1]; conn3 = connections_ptr[2]; if (NULL == (pos1 = DXGetArrayEntry(positionshandle, conn1, scratch))) goto error; if (shape[0]==2) position1 = DXPt(pos1->x, pos1->y, 0.0); else position1 = DXPt(pos1->x, pos1->y, pos1->z); if (NULL == (pos2 = DXGetArrayEntry(positionshandle, conn2, scratch))) goto error; if (shape[0]==2) position2 = DXPt(pos2->x, pos2->y, 0.0); else position2 = DXPt(pos2->x, pos2->y, pos2->z); if (NULL == (pos3 = DXGetArrayEntry(positionshandle, conn3, scratch))) goto error; if (shape[0]==2) position3 = DXPt(pos3->x, pos3->y, 0.0); else position3 = DXPt(pos3->x, pos3->y, pos3->z); vec1 = DXSub(position2, position1); vec2 = DXSub(position3, position2); crossprod = DXCross(vec1, vec2); /* now grab an appropriate normal */ if (!strcmp(str2,"positions")) { if (NULL == (norm = DXGetArrayEntry(normalshandle, conn1, scratch))) goto error; } else { if (NULL == (norm = DXGetArrayEntry(normalshandle, 0, (Pointer)scratch))) goto error; } /* check the dot product of the normal with crossprod. Should be + */ dotprod = DXDot(crossprod, *norm); if (dotprod > 0) goto done; else { /* flip the normals */ switch (DXGetArrayClass(normals)) { case (CLASS_CONSTANTARRAY): if (NULL == (norm = DXIterateArray(normalshandle, i, norm, (Pointer)scratch))) goto error; newnormal = DXSub(zerovec, *norm); newnormals = (Array)DXNewConstantArray(numitems, &newnormal, TYPE_FLOAT, CATEGORY_REAL, 1, 3); break; default: newnormals = DXNewArray(TYPE_FLOAT,CATEGORY_REAL, 1, 3); if (!DXAddArrayData(newnormals, 0, numitems, NULL)) goto error; for (i=0; ix, pos1->y, 0.0); else position1 = DXPt(pos1->x, pos1->y, pos1->z); if (NULL == (pos2 = DXGetArrayEntry(positionshandle, cptr1[1], scratch))) goto error; if (shape[0]==2) position2 = DXPt(pos2->x, pos2->y, 0.0); else position2 = DXPt(pos2->x, pos2->y, pos2->z); if (NULL == (pos3 = DXGetArrayEntry(positionshandle, cptr1[2], scratch))) goto error; if (shape[0]==2) position3 = DXPt(pos3->x, pos3->y, 0.0); else position3 = DXPt(pos3->x, pos3->y, pos3->z); vec1 = DXSub(position3, position1); vec2 = DXSub(position2, position1); crossprod = DXCross(vec1, vec2); /* now grab an appropriate normal */ if (!strcmp(str2,"positions")) { if (NULL == (norm = DXGetArrayEntry(normalshandle, *cptr1, scratch))) goto error; } else { if (NULL == (norm = DXGetArrayEntry(normalshandle, 0, (Pointer)scratch))) goto error; } /* check the dot product of the normal with crossprod. Should be + */ dotprod = DXDot(crossprod, *norm); if (dotprod > 0) goto done; else { /* flip the normals */ switch (DXGetArrayClass(normals)) { case (CLASS_CONSTANTARRAY): if (NULL == (norm = DXIterateArray(normalshandle, i, norm, (Pointer)scratch))) goto error; newnormal = DXSub(zerovec, *norm); newnormals = (Array)DXNewConstantArray(numitems, &newnormal, TYPE_FLOAT, CATEGORY_REAL, 1, 3); break; default: newnormals = DXNewArray(TYPE_FLOAT,CATEGORY_REAL, 1, 3); if (!DXAddArrayData(newnormals, 0, numitems, NULL)) goto error; for (i=0; i