/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ #include #include #include #include "IfAssert.h" #include "IfShape.h" #include "IfMerger.h" ///////////////////////////////////////////////////////////////////////////// // // Constructor. // ///////////////////////////////////////////////////////////////////////////// IfMerger::IfMerger() { } ///////////////////////////////////////////////////////////////////////////// // // Destructor. // ///////////////////////////////////////////////////////////////////////////// IfMerger::~IfMerger() { } ///////////////////////////////////////////////////////////////////////////// // // This merges adjacent IfShapes in the given IfShapeList, if possible. // ///////////////////////////////////////////////////////////////////////////// void IfMerger::merge(IfShapeList &shapeList) { ////////////////////////////////////////////////////////////////// // // We can merge materials of 2 adjacent shapes if: // // - The IfShapes differ in material properties, only // // - The IfShapes' shape nodes are all indexed shapes // // - The material binding is PER_VERTEX_INDEXED for both shapes // // This is the IfShape to merge into IfShape *shape1 = shapeList[0]; for (int i = 1; i < shapeList.getLength(); i++) { IfShape *shape2 = shapeList[i]; if (canMergeShapes(shape1, shape2) && mergeShapes(shape1, shape2)) { shape2->differenceLevel = 0; shape2->differenceCode = IfShape::NONE; } else shape1 = shape2; } } ///////////////////////////////////////////////////////////////////////////// // // Returns TRUE if materials can probably be merged from shape2 to // shape1. // ///////////////////////////////////////////////////////////////////////////// SbBool IfMerger::canMergeShapes(IfShape *shape1, IfShape *shape2) { // Can't be any difference other than material if (shape2->differenceCode != 0 && shape2->differenceCode != IfShape::MATERIAL) return FALSE; ASSERT(shape1->shape != NULL); ASSERT(shape2->shape != NULL); // Have to have indexed shapes if (! shape1->shape->isOfType(SoIndexedShape::getClassTypeId()) || ! shape2->shape->isOfType(SoIndexedShape::getClassTypeId())) return FALSE; // Have to have PER_VERTEX_INDEXED bindings if (shape1->materialBinding == NULL || shape2->materialBinding == NULL || shape1->materialBinding->value.getValue() != SoMaterialBinding::PER_VERTEX_INDEXED || shape2->materialBinding->value.getValue() != SoMaterialBinding::PER_VERTEX_INDEXED) return FALSE; return TRUE; } ///////////////////////////////////////////////////////////////////////////// // // Merges shape2 into shape1. Returns TRUE if successful. // ///////////////////////////////////////////////////////////////////////////// SbBool IfMerger::mergeShapes(IfShape *shape1, IfShape *shape2) { ////////////////////////////////////////////////////////////////// // // Step 1: merge shape2's material into shape1's by concatenating // the field values at the end. // SoMaterial *m1 = shape1->material; SoMaterial *m2 = shape2->material; // // We try to do this only in these common cases: // // 1 - All material fields change per vertex // 2 - Only diffuseColor changes per vertex // 3 - Only diffuseColor and transparency change per vertex // // In cases 2 and 3, we can merge the materials only if all other // material fields have 1 value and it is the same for both // materials. // // First, determine which case we have for each material int case1 = getMaterialCase(m1); int case2 = getMaterialCase(m2); // If either is 0 or they're not the same, we can't merge them if (case1 == 0 || case1 != case2) return FALSE; // If we have case 2 or 3, make sure the material values that are // not changing are the same in the two nodes if (case1 == 2 || case1 == 3) { if (m1->ambientColor[0] != m2->ambientColor[0] || m1->specularColor[0] != m2->specularColor[0] || m1->emissiveColor[0] != m2->emissiveColor[0] || m1->shininess[0] != m2->shininess[0]) return FALSE; if (case1 == 2 && m1->transparency[0] != m2->transparency[0]) return FALSE; } // Save the original number of values in the first shape's // material. This will be the offset into the second shape's // materials. int indexOffset = m1->diffuseColor.getNum(); // Append material2's values to material 1 #define APPEND_VALS(FIELD) \ m1->FIELD.setValues(m1->FIELD.getNum(), m2->FIELD.getNum(), \ m2->FIELD.getValues(0)) APPEND_VALS(diffuseColor); if (case1 != 2) { APPEND_VALS(transparency); if (case1 == 1) { APPEND_VALS(ambientColor); APPEND_VALS(specularColor); APPEND_VALS(emissiveColor); APPEND_VALS(shininess); } } #undef APPEND_VALS ////////////////////////////////////////////////////////////////// // // Modify IfShape2's shape's material indices // SoIndexedShape *iShape = (SoIndexedShape *) shape2->shape; // If the material indices are the same as the coordinate // indices, we will have to copy them first if (iShape->materialIndex.getNum() == 1 && iShape->materialIndex[0] == -1) { int num = iShape->coordIndex.getNum(); iShape->materialIndex.setNum(iShape->coordIndex.getNum()); const int32_t *c = iShape->coordIndex.getValues(0); int32_t *m = iShape->materialIndex.startEditing(); for (int i = 0; i < num; i++) { if (c[i] < 0) m[i] = c[i]; else m[i] = c[i] + indexOffset; } iShape->materialIndex.finishEditing(); } else { // Adjust the existing material indices by the offset int num = iShape->materialIndex.getNum(); int32_t *m = iShape->materialIndex.startEditing(); for (int i = 0; i < num; i++) if (m[i] >= 0) m[i] += indexOffset; iShape->materialIndex.finishEditing(); } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // // Determines which material case (0 - 3) is true for a material. // ///////////////////////////////////////////////////////////////////////////// int IfMerger::getMaterialCase(SoMaterial *material) { int nam = material->ambientColor.getNum(); int ndi = material->diffuseColor.getNum(); int nsp = material->specularColor.getNum(); int nem = material->emissiveColor.getNum(); int nsh = material->shininess.getNum(); int ntr = material->transparency.getNum(); // Case 1: all material fields change per vertex if (nam == ndi && nam == nsp && nam == nem && nam == nsh && nam == ntr) return 1; // Cases 2 and 3: only diffuseColor and transparency can change // per vertex if (nam == 1 && nsp == 1 && nem == 1 && nsh == 1) { // Case 2: only diffuseColor changes per vertex if (ntr == 1) return 2; // Case 3: diffuseColor and transparency change per vertex else return 3; } // Any other case is case 0 - unmergeable return 0; }