//
// This is a patch for the Inventor 2.0 SoPointSet node, which will
// generate incorrect normal indices when its startIndex field is not
// zero.
//
// To apply this patch, compile this file into a .o and then link
// the .o before -lInventor.  The linker may give a warning.
// This is normal and expected.
//

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void
SoPointSet::generatePrimitives(SoAction *action)
{
    // When generating primitives for picking, delay computing default
    // texture coordinates
    SbBool forPicking = action->isOfType(SoRayPickAction::getClassTypeId());

    SbBool			materialPerPoint, normalPerPoint;
    long			numPts;
    int				curCoord, i;
    SoPrimitiveVertex		pv;
    SoPointDetail		detail;

    // Push state, just in case we decide to set the NormalElement
    // because we're doing auto-normal generation.
    SoState *state = action->getState();
    state->push();
    // This extra level of brackets is to make bundle constructors get
    // called before state->pop() is called:
    {
	const SoGLCoordinateElement	*ce = (const SoGLCoordinateElement *)
	    SoCoordinateElement::getInstance(action->getState());

	// Figure out number of points in set
	curCoord = (int) startIndex.getValue();
	numPts = numPoints.getValue();
	if (numPts == SO_POINT_SET_USE_REST_OF_POINTS)
	    numPts = ce->getNum() - curCoord;

	materialPerPoint = areMaterialsPerPoint(action);
	normalPerPoint   = areNormalsPerPoint(action);

	// Test for auto-normal case; since this modifies an element this
	// MUST BE DONE BEFORE ANY BUNDLES ARE CREATED!
	SbVec3f defaultNormal(0, 0, 1);
	const SoNormalElement *ne = SoNormalElement::getInstance(state);
	if (SoNormalBindingElement::get(state) ==
	    SoNormalBindingElement::DEFAULT &&
	    curCoord + numPts > ne->getNum()) {
	    SoNormalElement::set(state, this, 1, &defaultNormal);
	    normalPerPoint = FALSE;
	}

	if (forPicking)
	    pv.setTextureCoords(SbVec4f(0.0, 0.0, 0.0, 0.0));

	pv.setDetail(&detail);

	SoNormalBundle			nb(action, FALSE);
	SoTextureCoordinateBundle	tcb(action, FALSE, ! forPicking);

	pv.setMaterialIndex(curCoord);
	detail.setMaterialIndex(curCoord);

	if (! normalPerPoint) {
	    pv.setNormal(nb.get(0));
	    detail.setNormalIndex(0);
	}

        // Get the complexity element and decide how points will be skipped
	// during processing; note that we don't want to skip anything
	// when picking.
        float cmplxValue = SoComplexityElement::get(action->getState());
        float delta = 1.8 * (0.5 - ((cmplxValue < 0.5) ? cmplxValue : 0.5));
        float fraction = 0.0;
	if (forPicking)
	    delta = 0.0;

	for (i = 0; i < numPts; i++, fraction += delta) {

            // Check to see if this point should be skipped due to complexity
            if (fraction >= 1.0) {
                fraction -= 1.0;
                curCoord++;
                continue;
            }

	    // Set coordinates, normal, and texture coordinates in
	    // detail

	    pv.setPoint(ce->get3(curCoord));
	    detail.setCoordinateIndex(curCoord);
	    if (normalPerPoint) {
		pv.setNormal(nb.get(curCoord));
		detail.setNormalIndex(curCoord);
	    }
	    if (materialPerPoint) {
		pv.setMaterialIndex(curCoord);
		detail.setMaterialIndex(curCoord);
	    }
	    if (tcb.isFunction()) {
		if (! forPicking)
		    pv.setTextureCoords(tcb.get(pv.getPoint(),
						pv.getNormal()));
		detail.setTextureCoordIndex(0);
	    }
	    else {
		pv.setTextureCoords(tcb.get(curCoord));
		detail.setTextureCoordIndex(curCoord);
	    }

	    // Generate a point primitive
	    invokePointCallbacks(action, &pv);

	    curCoord++;
	}

    }
    state->pop();     // Restore NormalElement
}