/*
 * Scene.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include "stdafx.h"
#ifndef _WIN32
# include <unistd.h>
# include <fcntl.h>
#endif
#include "Util.h"
#include "resource.h"

#ifdef HAVE_LIBZ
extern "C" {
# include "zlib.h"
}
#endif

#include "swt.h"

#include "Scene.h"
#include "SceneProtoMap.h"
#include "SceneView.h"
#include "WonderlandModuleExport.h"
#include "Matrix.h"
#include "FieldValue.h"
#include "FieldCommand.h"
#include "MFieldCommand.h"
#include "CommandList.h"
#include "RouteCommand.h"
#include "UnRouteCommand.h"
#include "MoveCommand.h"
#include "Node.h"
#include "SFNode.h"
#include "SFTime.h"
#include "MFNode.h"
#include "Proto.h"
#include "parser.h"
#include "Path.h"
#include "Field.h"
#include "EventIn.h"
#include "EventOut.h"
#include "ExposedField.h"
#include "URL.h"
#include "FontInfo.h"
#include "DuneApp.h"
#include "WriteFlags.h"
#include "RenderState.h"
#include "Interpolator.h"

#include "NodeTimeSensor.h"
#include "NodeNavigationInfo.h"
#include "NodeViewpoint.h"
#include "NodeBackground.h"
#include "NodeFog.h"
#include "NodeInline.h"
#include "NodeGroup.h"
#include "NodeShape.h"
#include "NodeIndexedFaceSet.h"
#include "NodeIndexedLineSet.h"
#include "NodeIndexedTriangleSet.h"
#include "NodeTriangleSet.h"
#include "NodeLdrawDatExport.h"

#define ARRAY_SIZE(v)  ((int) (sizeof(v) / sizeof(v[0])))

#define PICK_BUFFER_SIZE 1024
#define PICK_REGION_SIZE 2.5

#define MAX_JAVA_CALLS 8000

enum {
    PICKED_NODE,
    PICKED_HANDLE,
    PICKED_RIGID_BODY_HANDLE,
    PICKED_3DCURSOR
};

Scene::Scene()
{
    _writeFlags = 0;

    _numSymbols = 0;

    _headlight = true;
    _numLights = 0;
    _selection = NULL;
    _selectedHandles.resize(0);
    _lastSelectedHandle = -1;
    _isNewSelectedHandle = false;
    _transformMode = new TransformMode(
          (TMode) TheApp->GetIntPreference("TransformMode",TM_HOVER),
          (TDimension) TheApp->GetIntPreference("TransformDimension",TM_3D),
          (T2axes) TheApp->GetIntPreference("Transform2Axes",TM_NEAR_FAR));
    if (TheApp->getMaxNumberAxesInputDevices()<=4)
        if ((_transformMode->tmode == TM_6D) || (_transformMode->tmode == TM_6D))
            _transformMode->tmode = TM_HOVER;
    if (TheApp->getMaxNumberAxesInputDevices()<3)
        if (_transformMode->tmode == TM_ROCKET)
            _transformMode->tmode = TM_HOVER;
    if (TheApp->getMaxNumberAxesInputDevices()<2) 
        if (_transformMode->tmode >= TM_6D)
            _transformMode->tmode = TM_TRANSLATE;
 
    _root = NULL;
    _unmodified = NULL;
    resetWriteFlags(0);
    _extraModifiedFlag = false;

    SceneProtoMap::createProtoMap(&_protos, this);

    _numberBuildinProtos = getAllNodeNames()->size();

    _root = createNode("Group");
    _root->ref();
    _rootField = ((NodeGroup *) _root)->children_Field();
    _running = false;
    _recording = false;

    _setViewpoint = false;
    _defaultViewpoint = (NodeViewpoint *) createNode("Viewpoint");
    _defaultViewpoint->ref();
    _currentViewpoint = _defaultViewpoint;
    _currentViewpoint->ref();

    _setNavigationInfo = false;
    _defaultNavigationInfo = (NodeNavigationInfo *) 
                             createNode("NavigationInfo");
    _defaultNavigationInfo->ref();
    _currentNavigationInfo = _defaultNavigationInfo;
    _currentNavigationInfo->ref();

    _currentFog = NULL;

    _numProtoNames = 0; 
    _numProtoDefinitions = 0; 

    _navigationMouseMode = true;
    _navigationInputDeviceMode = true;

    _routeList.Init();

    _selectlevel=1;
    _hasFocus=false;

    _viewOfLastSelection=NULL;
    _selection_is_in_scene=false;
    _URL="";
    _errorLineNumber = -1;

    _obj3dCursor = gluNewQuadric();

    _multipleUndoTop = -1;
    _backupCommandList = NULL;

    _use3dCursor = false;

    _nodesWithExternProto.append("Contour2D");
    _nodesWithExternProto.append("CoordinateDeformer");
    _nodesWithExternProto.append("InlineLoadControl");
    _nodesWithExternProto.append("LoadSensor");

    _nodesWithExternProto.append("NurbsCurve");
    _nodesWithExternProto.append("NurbsCurve2D");
    _nodesWithExternProto.append("NurbsGroup");
    _nodesWithExternProto.append("NurbsPositionInterpolator");
    _nodesWithExternProto.append("NurbsSurface");
    _nodesWithExternProto.append("NurbsTextureSurface");
    _nodesWithExternProto.append("TrimmedSurface");

    _nodesWithExternProto.append("Arc2D");
    _nodesWithExternProto.append("ArcClose2D");
    _nodesWithExternProto.append("BooleanFilter");
    _nodesWithExternProto.append("BooleanSequencerExample.wrl");
    _nodesWithExternProto.append("BooleanSequencer");
    _nodesWithExternProto.append("BooleanToggle");
    _nodesWithExternProto.append("BooleanTrigger");
    _nodesWithExternProto.append("Circle2D");
    _nodesWithExternProto.append("ColorRGBA");
    _nodesWithExternProto.append("CoordinateInterpolator2D");
    _nodesWithExternProto.append("Disk2D");
    _nodesWithExternProto.append("FillProperties");
    _nodesWithExternProto.append("IntegerSequencer");
    _nodesWithExternProto.append("IntegerTrigger");
    _nodesWithExternProto.append("KeySensor");
    _nodesWithExternProto.append("LineProperties");
    _nodesWithExternProto.append("MultiTexture");
    _nodesWithExternProto.append("Polyline2D");
    _nodesWithExternProto.append("Polypoint2D");
    _nodesWithExternProto.append("PositionInterpolator2D");
    _nodesWithExternProto.append("Rectangle2D");
    _nodesWithExternProto.append("StringSensor");
    _nodesWithExternProto.append("TimeTrigger");
    _nodesWithExternProto.append("TriangleSet2D");

    _nodesWithExternProto.append("SuperEllipsoid");
    _nodesWithExternProto.append("SuperExtrusion");
    _nodesWithExternProto.append("SuperShape");
    _nodesWithExternProto.append("SuperRevolver");

    _nodesWithExternProto.append("CattExportRec");
    _nodesWithExternProto.append("CattExportSrc");

    _nodesWithExternProto.append("LdrawDatExport");

    _nodesWithExternProto.append("VrmlCut");
    _nodesForceExternProtoWrite.append("VrmlCut");
    _nodesWithExternProto.append("VrmlScene");
    _nodesForceExternProtoWrite.append("VrmlScene");

    if (TheApp->getCoverMode()) {
        _nodesWithExternProto.append("ARSensor");
        _nodesWithExternProto.append("COVER");
        _nodesWithExternProto.append("CubeTexture");
        _nodesWithExternProto.append("JoystickSensor");
        _nodesWithExternProto.append("LabView");
        _nodesWithExternProto.append("SpaceSensor");
        _nodesWithExternProto.append("Sky");
        _nodesWithExternProto.append("SteeringWheel");
        _nodesWithExternProto.append("TUIButton");
        _nodesWithExternProto.append("TUIComboBox");
        _nodesWithExternProto.append("TUIFloatSlider");
        _nodesWithExternProto.append("TUIFrame");
        _nodesWithExternProto.append("TUILabel");
        _nodesWithExternProto.append("TUIListBox");
        _nodesWithExternProto.append("TUIMap");
        _nodesWithExternProto.append("TUIProgressBar");
        _nodesWithExternProto.append("TUISlider");
        _nodesWithExternProto.append("TUISplitter");
        _nodesWithExternProto.append("TUITab");
        _nodesWithExternProto.append("TUITabFolder");
        _nodesWithExternProto.append("TUIToggleButton");
        _nodesWithExternProto.append("Vehicle");
        _nodesWithExternProto.append("VirtualAcoustics");
        _nodesWithExternProto.append("VirtualSoundSource");
        _nodesWithExternProto.append("Wave");
    }
    if (TheApp->getKambiMode()) {
        _nodesWithExternProto.append("Text3D");
        _nodesWithExternProto.append("KambiAppearance");
        _nodesWithExternProto.append("KambiHeadLight");
        _nodesWithExternProto.append("KambiTriangulation");
    }
    _externProtoWarning = true;
    TheApp->readProtoLibrary(this);
    _isParsing = false;
    _hasJoints = false;
    _showJoints = false;
    _headlightIsSet = false;
    _xrayRendering = false;
    _defNode = NULL;
    buildInteractiveProtos();
    _importIntoVrmlScene = false;
    _rigidBodyHandleNode = NULL;
    _writeKanimNow = false;
    _ac3dEmptyMaterial = 0;
    _selectionLevel = 1;
    _canUpdateViewsSelection = true;
    _variableCounter = 0;
    zeroNumDataFunctions();
    zeroNumReducedDataFunctions();
    setSelection(getRoot());
}

Scene::~Scene()
{
    int i;

    TheApp->SetIntPreference("TransformMode",_transformMode->tmode);
    TheApp->SetIntPreference("TransformDimension",_transformMode->tdimension);
    TheApp->SetIntPreference("Transform2Axes",_transformMode->t2axes);

    while (!_undoStack.empty()) delete _undoStack.pop();

    while (!_redoStack.empty()) delete _redoStack.pop();

    _defaultViewpoint->unref();
    _currentViewpoint->unref();

    delete _selection;
    _selection = NULL;    

    _root->unref();

    ProtoMap::Chain::Iterator *j;

    for (i = 0; i < _protos.width(); i++) {
        for (j = _protos.chain(i).first(); j != NULL; j = j->next()) {
            delete j->item()->getData();
        }
    }
    for (i = 0; i < _fonts.size(); i++) {
        delete _fonts[i];
    }
    gluDeleteQuadric(_obj3dCursor);
}

void Scene::def(const char *nodeName, Node *value)
{
    if (value && nodeName && (strlen(nodeName) > 0)) {
        value->setName(nodeName);
        _nodeMap[nodeName] = value;
    }
}

void Scene::undef(MyString nodeName)
{
    if (nodeName && nodeName.length() > 0) {
       _defNode = _nodeMap[nodeName];
       if (_defNode)
           _nodeMap.remove(nodeName);
    }
}

Node *Scene::use(const char *nodeName)
{
    return _nodeMap[nodeName];
}

bool Scene::canUse(Node *parentNode, int parentField)
{
    if (_defNode == NULL)
        return false;
    // avoid to create recursive scenegraphs
    if (_defNode == parentNode)
        return false; 
    return getDestField(_defNode, parentNode, parentField) >= 0;
}

bool Scene::use(Node *parentNode, int parentField)
{
    // avoid to create recursive scenegraphs
    Node *testRecursive = parentNode;
    while (testRecursive->hasParent()) {
       if (testRecursive == _defNode)
           return false;
       testRecursive = testRecursive->getParent();
    }

    int destField = getDestField(_defNode, parentNode, parentField);
    if (destField >= 0)
        execute(new MoveCommand(_defNode, NULL, -1, parentNode, destField));
    return true;
}

int Scene::getDestField(Node* src, Node *dest, int destField)
{
    int field = destField;
    if (field == -1) 
        field = dest->findValidField(src);
    if (dest->validChildType(field, src->getNodeClass()) ||
        dest->validChildType(field, src->getType())) {
        if (src->isInvalidChildNode())
            if (dest->getProto()->getField(field)->getNodeType() & CHILD_NODE)
                return -1; 
        return field;
    }
    return -1;    
}

int
Scene::addSymbol(MyString s)
{
    int &id = _symbols[s];

    if (id == 0) {
        id = _numSymbols++;
        _symbolList[id] = s;
    }
    return id;
}

// store begin of multiple undo commands 
// this can be used for later optimization

void 
Scene::startMultipleUndo()
{
    _multipleUndoTop = _undoStack.getTop();
}

// optimize multiple undo commands
// read stack and throw out all unneeded commands
// remember: backup commands contain setField(node, field) information
// a setField(node, field) is erased by a later setField(node, field) 
// WARNING ! 

void               
Scene::optimizeMultipleUndo()
{
   int i;
   // Take care, that the optimization mechanism for the commandStack 
   // do not try to optimize to handle already deleted nodes
   // prove if all commands on the undoStack till _multipleUndoTop
   // are not MOVE_COMMANDs (but FIELD_COMMANDs)
   if (_multipleUndoTop == -1)
       return;
   for (i = _undoStack.getTop(); i > _multipleUndoTop; i--)
       if (_undoStack.peek(i)->getType() != FIELD_COMMAND) {
           _multipleUndoTop = -1;
           return;
   }
   CommandStack tmpStack;
   while (_undoStack.getTop() > _multipleUndoTop) {
       if (_undoStack.empty()) {
           _multipleUndoTop = -1;
           return;
       }
       Command *stackCommand = _undoStack.pop();
       bool storeStackCommandToTmpStack = true;
       for (i = tmpStack.getTop(); i > 0; i--) {
           Command *tmpCommand = tmpStack.peek(i);
           if (   (stackCommand->getType() == FIELD_COMMAND)
               && (tmpCommand->getType() == FIELD_COMMAND)) {
               FieldCommand *fieldCommand = (FieldCommand *)tmpCommand;
               FieldCommand *fstackCommand = (FieldCommand *)stackCommand;
               if (   (fstackCommand->getNode() == fieldCommand->getNode())
                   && (fstackCommand->getField() == fieldCommand->getField()) ){
                   // there is already a command for this node and field
                   // in the tmpstack, ignore this command
                   storeStackCommandToTmpStack = false;
                   break;
               }
           }
       }
       if (storeStackCommandToTmpStack)
           tmpStack.push(stackCommand);
   }
   // store content of tmpStack back to _undoStack
   while (!tmpStack.empty())
       _undoStack.push(tmpStack.pop());
   _multipleUndoTop = -1;
}


const MyString &
Scene::getSymbol(int id) const
{
    return _symbolList[id];
}

void
Scene::setNodes(NodeList *nodes)
{
    ((NodeGroup *)_root)->children(new MFNode(nodes));
}

void
Scene::addNodes(Node *targetNode, int targetField, NodeList *nodes)
{
    if (targetNode == NULL)
        _root->addFieldNodeList(_rootField, nodes);
    else if (targetField == -1)
        targetNode->addFieldNodeList(-1, nodes);
    else if (targetNode->getField(targetField)->getType() == MFNODE) {
        targetNode->setField(targetField, new MFNode(nodes));        
    } else if (targetNode->getField(targetField)->getType() == SFNODE) {
        SFNode *oldSFNode = (SFNode *)targetNode->getField(targetField);
        MoveCommand *removeCommand = new MoveCommand(oldSFNode->getValue(), 
                                                     targetNode, targetField,
                                                     NULL, -1);
        removeCommand->execute();
        Node *lastNode = nodes->get(nodes->size() - 1);
        MoveCommand *addCommand = new MoveCommand(lastNode, NULL, -1,
                                                  targetNode, targetField);
        addCommand->execute();
    } else {
        // wrong targetField
        assert(0);
    }
    scanForExternProtos();
    scanForInlines(nodes);
}

void 
Scene::scanForInlines(NodeList *nodes)
{
    if (nodes == NULL) 
        return;
    for (int i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if ((node->getType() == VRML_INLINE) ||
            (node->getType() == VRML_INLINE_LOAD_CONTROL)) {
            if (TheApp->loadNewInline())
                readInline((NodeInline *)node);
        } else {
            for (int j = 0; j < node->getProto()->getNumFields(); j++) {
                FieldValue *field = node->getField(j);
                if (field && field->getType() == MFNODE) {
                    NodeList *childList = ((MFNode *) field)->getValues();
                    scanForInlines(childList);
                }
            }
        }
    }
}

static bool recreateNodePROTO(Node *node, void *data)
{
    if (node->isPROTO() && !node->hasProtoNodes()) {
        NodePROTO *protoNode = (NodePROTO *)node;
        protoNode->handleIs();
        protoNode->createPROTO();
        protoNode->reInit();
    }
    return true;
}     


void 
Scene::scanForExternProtos(void)
{
    bool protoLoaded = false;
    do {
        protoLoaded = false;
        ProtoMap::Chain::Iterator *j;
        for (int i = 0; i < _protos.width(); i++) {
            for (j = _protos.chain(i).first(); j != NULL; j = j->next()) {
                Proto *proto = j->item()->getData();
                if (proto == NULL)
                    continue;
                if (proto->isLoaded())
                    continue;
                if (belongsToNodeWithExternProto(proto->getName(isX3d())))
                    continue;
                if (proto->isCoverProto() || proto->isKambiProto())
                    continue;
                if (proto->getUrls() != NULL)
                    if (readExternProto(proto))
                        protoLoaded = true;
                    else {
                        MFString *urls = (MFString *)proto->getUrls();
                        const char *firstFile = "";
                        if (urls->getSize() > 0)
                            firstFile = urls->getValue(0);
                        TheApp->MessageBox(IDS_EXTERNPROTO_FILE_FAILED, 
                                           proto->getName(isX3d()), firstFile);
                    }
                if (protoLoaded)
                    break;
            }
        }
    } while (protoLoaded);
    _root->doWithBranch(recreateNodePROTO, scene, true, false, true);
}

void 
Scene::addToNodeList(Node *node)
{
    _nodeList.append(node);
}

static bool checkNodeType(Node *node, void *data)
{
    int *nodeType = (int *) data;
    if (node->getType() == *nodeType)
        node->getScene()->addToNodeList(node);
    return true;
}     

NodeList *
Scene::searchNodeType(int nodeType)
{
    _nodeList.resize(0);
    _root->doWithBranch(checkNodeType, &nodeType);
    NodeList *nodeList = new NodeList();
    for (int i = 0; i < _nodeList.size(); i++)
        nodeList->append(_nodeList[i]);  
    /// delete returned NodeList after usage
    return nodeList;
}

void 
Scene::readInline(NodeInline *node)
{
    if (node->alreadyLoaded())
        return;
    MFString *urls = node->url();
    for (int j = 0; j < urls->getSize(); j++) {
        if (urls->getValue(j).length() == 0) 
            continue;
        URL url(getURL(), urls->getValue(j));
        MyString path;
        if (Download(url, &path)) {
            struct stat fileStat;
            const char *filename = path;
            if (stat(filename, &fileStat) == 0) {
                if (S_ISREG(fileStat.st_mode)) {
                    bool oldX3d = isX3d();
                    URL importURL;
                    importURL.FromPath(filename);
                    TheApp->setImportURL(importURL);
                    FILE *file = fopen(filename, "rb");
                    if (file == NULL) {
                        TheApp->MessageBoxPerror(IDS_INLINE_FILE_FAILED,
                                                 filename);
                        break;
                    }
                    parse(file, false, node);
                    fclose(file);
                    if (oldX3d && (!isX3d()))
                        setX3d();
                    else if ((!oldX3d) && isX3d())             
                        setVrml();
                    break;
                }
            }
        }
    }    
}

bool                
Scene::readExternProto(Proto *proto)
{
    FILE* file = NULL;
    bool oldX3d = scene->isX3d();
    MFString *urls = (MFString *)proto->getUrls();
    for (int j = 0; j < urls->getSize(); j++) {
        if (urls->getValue(j).length() == 0) 
            continue;
        URL url(scene->getURL(), urls->getValue(j));
        url.TrimTopic();
        MyString path;
        if (Download(url, &path)) {
            struct stat fileStat;
            const char *filename = path;
            if (stat(filename, &fileStat) == 0) {
                if (S_ISREG(fileStat.st_mode)) {
                    URL importURL;
                    importURL.FromPath(filename);
                    TheApp->setImportURL(importURL);
                    file = fopen(filename, "rb");
                    if (file == NULL)
                        break;
                    Node *oldTargetNode = targetNode;
                    int oldTargetField = targetField;
                    // fake root node, will be ignored later
                    NodeGroup *node = (NodeGroup *)scene->createNode("Group");
                    proto->setLoaded(true);
                    parse(file, false, node, node->children_Field());
                    targetNode = oldTargetNode;
                    targetField = oldTargetField;
                    fclose(file);
                    if (oldX3d && (!scene->isX3d()))
                        setX3d();
                    else if ((!oldX3d) && scene->isX3d())             
                        setVrml();
                    break;
                }
            }
        }
    }
    if (file == NULL)
        return false;
    return true;
}


static bool getProfileCallback(Node *node, void *data)
{
    int *currentProfile = (int *)data;
    if (node->getProfile() > *currentProfile)
        *currentProfile = node->getProfile();
    return true;     
}


int
Scene::writeRouteStrings(int filedes, int indent, bool end)
{
    static bool alreadyIn = false;
    // avoid recursive call of writeRouteStrings via Node::write
    if (alreadyIn)
        return 0;
    alreadyIn = true;
    // write tempory nodes first
    for (int i = 0; i < _delayedWriteNodes.size(); i++) {
        int ret = 0;
        if (isX3dXml()) 
            ret = _delayedWriteNodes[i]->writeXml(filedes, indent);
        else
            ret = _delayedWriteNodes[i]->write(filedes, indent);
        if (ret < 0) {
            alreadyIn = false;
            return ret;
        }
        removeNode(_delayedWriteNodes[i]);
    }
    if (_delayedWriteNodes.size() > 0)
        _delayedWriteNodes.resize(0);
    alreadyIn = false;

    if (_routeList.size() != 0) {
       for (List<MyString>::Iterator* routepointer = _routeList.first();
            routepointer != NULL; routepointer = routepointer->next() ) 
          {
          RET_ONERROR( mywritestr(filedes ,(const char*) routepointer->item()) )
          RET_ONERROR( mywritestr(filedes ,"\n") )
          TheApp->incSelectionLinenumber();
          }
       RET_ONERROR( mywritestr(filedes ,"\n") )
       TheApp->incSelectionLinenumber();
       _routeList.removeAll();
    }
    if (end && _endRouteList.size() != 0) {
       for (List<MyString>::Iterator* routepointer = _endRouteList.first();
            routepointer != NULL; routepointer = routepointer->next() ) 
          {
          RET_ONERROR( mywritestr(filedes ,(const char*) routepointer->item()) )
          RET_ONERROR( mywritestr(filedes ,"\n") )
          TheApp->incSelectionLinenumber();
          }
       RET_ONERROR( mywritestr(filedes ,"\n") )
       TheApp->incSelectionLinenumber();
       _endRouteList.removeAll();
    }
    return 0;
}

int Scene::writeExternProto(int f, const char* protoName)
{
    // search if EXTERNPROTO already exist
    int i;
    bool foundProto = false;
    for (i = 0; i < _numProtoNames; i++)
        if (strcmp((const char*)_protoNames[i], protoName)==0) {
            foundProto = true;   
            // force writing of some nodes despite EXTERNPROTO already exist
            for (int j = 0; j < _nodesForceExternProtoWrite.size(); j++)
                if (strcmp(_nodesForceExternProtoWrite[j], protoName) == 0)
                    foundProto = false;
        }        

    if (!foundProto) {
        // write EXTERNPROTO
        const NodeList *nodes = getNodes();
        for (i = 0; i < nodes->size(); i++) {
            Node    *node = nodes->get(i);
            if (node->isInScene(this)) {
                const char *nodeName = node->getProto()->getName(isX3d());
                if (strcmp(nodeName, protoName)==0) {
                    RET_ONERROR( node->writeProto(f) )
                    RET_ONERROR( mywritestr(f ,"\n\n") )
                    TheApp->incSelectionLinenumber(2);
                    break;
                }
            }
        }
    }
    return 0;
}


static bool avoidProtoOnPureVrml(MyString name)
{
    if (strcmp(name, "SuperEllipsoid") == 0)
        return true;
    if (strcmp(name, "SuperExtrusion") == 0)
        return true;
    if (strcmp(name, "SuperRevolver") == 0)
        return true;
    if (strcmp(name, "SuperShape") == 0)
        return true;
    if (strcmp(name, "NurbsCurve") == 0)
        return true;
    if (strcmp(name, "NurbsCurve2D") == 0)
        return true;
    if (strcmp(name, "NurbsGroup") == 0)
        return true;
    if (strcmp(name, "NurbsOrientationInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsPositionInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsSet") == 0)
        return true;
    if (strcmp(name, "NurbsSurface") == 0)
        return true;
    if (strcmp(name, "NurbsSurfaceInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsSweptSurface") == 0)
        return true;
    if (strcmp(name, "NurbsSwungSurface") == 0)
        return true;
    if (strcmp(name, "NurbsTextureCoordinate") == 0)
        return true;
    if (strcmp(name, "NurbsTextureSurface") == 0)
        return true;
    if (strcmp(name, "NurbsTrimmedSurface") == 0)
        return true;
    if (strcmp(name, "Contour2D") == 0)
        return true;
    if (strcmp(name, "CoordinateDeformer") == 0)
        return true;
    if (strcmp(name, "Polyline2D") == 0)
        return true;
    if (strcmp(name, "TrimmedSurface") == 0)
        return true;
    if (strcmp(name, "KambiAppearance") == 0)
        return true;
    if (strcmp(name, "KambiHeadLight") == 0)
        return true;
    if (strcmp(name, "KambiInline") == 0)
        return true;
    if (strcmp(name, "KambiNavigationInfo") == 0)
        return true;
    if (strcmp(name, "KambiOctreeProperties") == 0)
        return true;
    if (strcmp(name, "KambiTriangulation") == 0)
        return true;
    if (strcmp(name, "Text3D") == 0)
        return true;

    return false;
}

static bool avoidProtoOnX3dv(MyString name)
{
    if (strcmp(name, "NurbsCurve") == 0)
        return true;
    if (strcmp(name, "NurbsCurve2D") == 0)
        return true;
    if (strcmp(name, "NurbsGroup") == 0)
        return true;
    if (strcmp(name, "NurbsOrientationInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsPositionInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsSet") == 0)
        return true;
    if (strcmp(name, "NurbsSurface") == 0)
        return true;
    if (strcmp(name, "NurbsSurfaceInterpolator") == 0)
        return true;
    if (strcmp(name, "NurbsSweptSurface") == 0)
        return true;
    if (strcmp(name, "NurbsSwungSurface") == 0)
        return true;
    return false;
}

static bool avoidComponentOnPureX3dv(MyString name)
{
    if (strcmp(name, "NURBS") == 0)
        return true;
    return false;
}

static bool getComponents(Node *node, void *data)
{
    StringMap *components = (StringMap *) data;
    if (node->isPROTO())
        ((NodePROTO *)node)->getComponentsInBranch(getComponents, data);
    else {   
        int level = node->getComponentLevel();
        if (level != -1) {
            const char* name = node->getComponentName();
#ifdef HAVE_XJ3D_RIGID_BODY_PHYSICS_COMPONENT
            if (strcmp(name, "RigidBodyPhysics") == 0)
                name = "xj3d_RigidBodyPhysics";
#endif
            StringMap::Chain::Iterator *j;
            bool hasAlreadyName = false;
            for (int i = 0; i < components->width(); i++)
                for (j = components->chain(i).first(); j != NULL; j = j->next())
                    if (strcmp(name, j->item()->getKey()) == 0)
                        hasAlreadyName = true;
            bool x3d = node->getScene()->isX3d();
            if (node->getScene()->isPureX3dv() && 
                avoidComponentOnPureX3dv(node->getProto()->getName(x3d)))
                return true;
            if (!hasAlreadyName)
                (*components)[name] = level;
            else if (level > (*components)[name])
                (*components)[name] = level;
        }
    }
    return true;
}

int
Scene::writeComponents(int f)
{
    StringMap components;
    _root->doWithBranch(getComponents, &components);

    if (components.width() == 0)
        return 0;

    if (isX3dXml()) {
        RET_ONERROR( mywritestr(f, "<head>\n") )
        TheApp->incSelectionLinenumber();
    }

    StringMap::Chain::Iterator *j;

    for (int i = 0; i < components.width(); i++)
        for (j = components.chain(i).first(); j != NULL; j = j->next())
            if (j->item()->getData() != -1) {
                if (isX3dv())
                    RET_ONERROR( mywritestr(f, "COMPONENT ") )
                else if (isX3dXml()) {
                    RET_ONERROR( indentf(f, TheApp->GetIndent()) )
                    RET_ONERROR( mywritestr(f, "<component name='") )
                }
                RET_ONERROR( mywritestr(f, (const char *) j->item()->getKey()) )
                if (isX3dv())
                    RET_ONERROR( mywritestr(f, ":") )
                else if (isX3dXml())
                    RET_ONERROR( mywritestr(f, "' level='") )
                RET_ONERROR( mywritef(f, "%d ", j->item()->getData()) ) 
                if (isX3dXml())
                    RET_ONERROR( mywritestr(f, "'/>") )
                RET_ONERROR( mywritestr(f, "\n") )
                TheApp->incSelectionLinenumber();
            }

    if (isX3dXml()) {
        RET_ONERROR( mywritestr(f, "</head>\n") )
        TheApp->incSelectionLinenumber();
    }
    return 0;            
}

#define RET_RESET_FLAGS_ONERROR(x) RET_AND_RESET_ONERROR(x, \
{    \
    TheApp->disableEFloatWriteFormat(_writeFlags); \
    if (_writeFlags & TEMP_EXPORT) \
        _writeFlags = oldWriteFlags; \
}) 

static bool generateConvertedNode(Node *node, void *data)
{
    if (node != NULL)
        node->setConvertedNode();
    return true;
}

static bool deleteConvertedNode(Node *node, void *data)
{
    node->deleteConvertedNode();
    return true;
}

int Scene::write(int f, const char *url, int writeFlags)
{
    int oldWriteFlags = _writeFlags;
    _writeFlags = writeFlags;
    bool done = false;
    int ret = 0;
    if (writeFlags & (TRIANGULATE | C_MESH))
        _root->doWithBranch(generateConvertedNode, NULL);
    writeFlags = writeFlags & (~(TRIANGULATE | C_MESH));
    bool x3dv = ::isX3dv(writeFlags);
    if (writeFlags & C_SOURCE)
        x3dv = true;
    if (writeFlags & CC_SOURCE)
        x3dv = true;
    if (writeFlags & JAVA_SOURCE)
        x3dv = true;
    if (x3dv)
        setX3dv();
    else if (::isX3dXml(writeFlags))
        setX3dXml();
    else
        setVrml();
    if (writeFlags & AC3D) {
        ret =  writeAc3d(f, writeFlags & AC3D_4_RAVEN);
        done = true;
    } else if (writeFlags & KANIM) {
        ret =  writeKanim(f, url);
        done = true;
    } else if (writeFlags & LDRAW_DAT) {
        ret =  writeLdrawDat(f, url);
        done = true;
    } else if (writeFlags & C_SOURCE) {
        ret =  writeC(f, writeFlags);
        done = true;
    } else if (writeFlags & CC_SOURCE) {
        ret =  writeC(f, writeFlags);
        done = true;
    } else if (writeFlags & JAVA_SOURCE) {
        ret =  writeC(f, writeFlags);
        done = true;
    } else if (writeFlags & WONDERLAND) {
        URL *fileUrl = new URL(getURL());
        const char *fileName = fileUrl->GetFileNameWithoutExtension();
        ret =  TheApp->writeWonderlandModule(url, fileName, this, 
                                             writeFlags & MANY_JAVA_CLASSES);
        done = true;
    }
    if (done) {
        _writeFlags = oldWriteFlags;
        _root->doWithBranch(deleteConvertedNode, NULL);
        return ret;
    }
    TheApp->disableEFloatWriteFormat(writeFlags);
    _newURL = url;

    int i;

    getNodes()->clearFlag(NODE_FLAG_DEFED);
    getNodes()->clearFlag(NODE_FLAG_TOUCHED);

    // remove multiple identical ProtoDefinitions smuggled in by Inline nodes
    for (i = 0;i < _numProtoDefinitions;i++) {
        for (int j = 0;j < _numProtoDefinitions;j++) 
           if (i != j)
               if (strcmp((const char*)_protoDefinitions[i],
                         (const char*)_protoDefinitions[j]) == 0)
                   _protoDefinitions[j] = "";
    }

    if (isX3d()) {
        if (::isX3dXml(writeFlags)) {
            RET_RESET_FLAGS_ONERROR( mywritestr(f, 
                  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") )
            TheApp->incSelectionLinenumber();
            RET_RESET_FLAGS_ONERROR( mywritestr(f,"<!DOCTYPE X3D PUBLIC ") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f,"\"ISO//Web3D//DTD ") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f,"X3D 3.0//EN\" " ) )
            RET_RESET_FLAGS_ONERROR( mywritestr(f, 
                  "\"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n") )
            TheApp->incSelectionLinenumber();
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"<X3D profile='") )
        } else if (::isX3dv(writeFlags)) {
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"#X3D V3.0 utf8\n") )
            TheApp->incSelectionLinenumber();
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"PROFILE ") )
        }
        int profile = PROFILE_CORE;
        getRoot()->doWithBranch(getProfileCallback, &profile);
        switch (profile) {
          case PROFILE_CORE:
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"Core") )
            break;
          case PROFILE_INTERCHANGE:
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"Interchange") )
            break;
          case PROFILE_INTERACTIVE:
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"Interactive") )
            break;
          case PROFILE_MPEG4_INTERACTIVE:
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"MPEG-4 interactive") )
            break;
          case PROFILE_IMMERSIVE:
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"Immersive") )
            break;
          default:  //  PROFILE_FULL
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"Full") )
            break;
        }
        if (::isX3dXml(writeFlags))
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"'>") )
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"\n") )
        TheApp->incSelectionLinenumber();
        RET_RESET_FLAGS_ONERROR( writeComponents(f) )
        TheApp->incSelectionLinenumber();
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"\n") )
        TheApp->incSelectionLinenumber();
    } else {
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"#VRML V2.0 utf8\n\n") )
        TheApp->incSelectionLinenumber(2);
    }

    int maxMetas = _metaKeys.size();
    if (_metaValues.size() < maxMetas)
        maxMetas = _metaValues.size(); 
    if (::isX3dXml(writeFlags))
        for (int i = 0; i < maxMetas; i++) {
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "<meta name='") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , _metaKeys[i]) )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "' content='") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , _metaValues[i]) )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "' />\n") )
        }
    else
        for (int i = 0; i < maxMetas; i++) {
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "META \"") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , _metaKeys[i]) )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "\" \"") )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , _metaValues[i]) )
            RET_RESET_FLAGS_ONERROR( mywritestr(f , "\"\n") )
        }    
    if (maxMetas > 0)
        RET_RESET_FLAGS_ONERROR( mywritestr(f , "\n") )

    if (::isX3dXml(_writeFlags)) {
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"<Scene>\n") )
        TheApp->incSelectionLinenumber();
    }
    if (writeFlags & (PURE_VRML97 | PURE_X3DV)) {
        if (TheApp->getCoverMode())
            writeExtensionProtos(f, FF_COVER_ONLY);
        if (TheApp->getKambiMode())
            writeExtensionProtos(f, FF_KAMBI_ONLY);
    }
    for (i = 0;i < _numProtoDefinitions;i++) {
        MyString protoName = _protoNames[i];
        if ((isPureVRML() && avoidProtoOnPureVrml(protoName)) ||
            (isX3dv() && avoidProtoOnX3dv(protoName))) {
            if (belongsToNodeWithExternProto(protoName))
                continue;
        }
        // avoid writing of Protos, which are forced written via ExternProto
        bool avoidProto = false;
        for (int j = 0; j < _nodesForceExternProtoWrite.size(); j++)
            if (strcmp(_nodesForceExternProtoWrite[j], protoName) == 0)
                avoidProto = true;
        if (avoidProto) 
            continue;   
        if (TheApp->getPrefix() != NULL) {
            MyString prefix = TheApp->getPrefix();
            bool isPrefixProto = false;
            for (int j = 0; j < _numProtoDefinitions; j++) {
                if (strncmp(_protoNames[j], prefix, prefix.length()) == 0)
                    if (strcmp(_protoNames[j], protoName) == 0)
                        isPrefixProto = true;
            }
            if (!isPrefixProto) {
                for (int j = 0; j < _numProtoDefinitions; j++) {
                    if (strncmp(_protoNames[j], prefix, prefix.length()) == 0)
                        _protoDefinitions[i].gsubOnce(
                              _protoNames[j] + prefix.length(), 
                              _protoNames[j]);
                }
            }
        }   
        if (_isNestedProto[i]) {
            if (isX3dXml()) {
                static char format[256];
                swLoadString(IDS_X3D_NESTED_PROTO_NOT_SUPPORTED, format, 255);
                errorf(format, (const char *)protoName);
                return -1;
            }
            RET_RESET_FLAGS_ONERROR( _protoDefinitions[i].write(f) )
            // count end of line characters in protodefinitions
            char* string=(char*) ((const char*) _protoDefinitions[i]);
            while ((string=strchr(string, '\n')) !=NULL) {
                TheApp->incSelectionLinenumber();
                string++;
            }
            RET_RESET_FLAGS_ONERROR( mywritestr(f ,"\n\n") )
            TheApp->incSelectionLinenumber(2);
        } else if (strlen(_protoNames[i]) > 0)  {
            RET_RESET_FLAGS_ONERROR( _protos[protoName]->write(f, 0, 
                                                               _writeFlags) )
        }
    }

    for (int j = 0; j < _nodesWithExternProto.size(); j++) {
        // do not write EXTERN PROTOs for some (e.g. Nurbs Nodes) when using 
        // pureVRML97 cause this nodes are written converted to pure VRML97
        bool doWriteExternProto = true;
        if (isPureVRML() && avoidProtoOnPureVrml(_nodesWithExternProto[j]))
            doWriteExternProto = false;
        if (isX3dv() && avoidProtoOnX3dv(_nodesWithExternProto[j]))
            doWriteExternProto = false;
        if (doWriteExternProto)
            RET_RESET_FLAGS_ONERROR( 
                writeExternProto(f, _nodesWithExternProto[j]) 
            )
    }

    _nodes.clearFlag(NODE_FLAG_TOUCHED);

    NodeList   *childList = ((NodeGroup *)getRoot())->children()->getValues();
    for (i = 0; i < childList->size(); i++) {
        if (::isX3dXml(_writeFlags))
            RET_RESET_FLAGS_ONERROR( childList->get(i)->writeXml(f, 0) )
        else
            RET_RESET_FLAGS_ONERROR( childList->get(i)->write(f, 0) )
    }

    if (::isX3dXml(_writeFlags)) {
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"</Scene>\n") )
        TheApp->incSelectionLinenumber();
        RET_RESET_FLAGS_ONERROR( mywritestr(f ,"</X3D>\n") )
        TheApp->incSelectionLinenumber();
    }

    if ((!isTempSave()) && (!isPureVRML())) {
        _unmodified = _undoStack.empty() ? (Command *) NULL : _undoStack.peek();
        _extraModifiedFlag = false;
        _URL = url;
    }
    RET_RESET_FLAGS_ONERROR( writeRouteStrings(f, 0, true) )
    if (writeFlags & (PURE_VRML97 | PURE_X3DV))
        if (TheApp->getCoverMode())
            deleteExtensionProtos();
    TheApp->enableEFloatWriteFormat(writeFlags);
    if (oldWriteFlags & TEMP_EXPORT) {
        resetWriteFlags(oldWriteFlags);
    }
    return(0);
}

bool
Scene::belongsToNodeWithExternProto(const char *protoName) 
{
    bool found = false;
    for (int i = 0; i < _nodesWithExternProto.size(); i++)
        if (strcmp(protoName, _nodesWithExternProto[i]) == 0) {
            found = true;
            break;
        }
    return found;
} 

#define KANIM_RET_ONERROR(x) RET_AND_RESET_ONERROR(x, delete [] name;_writeKanimNow = false)

int 
Scene::writeKanim(int f, const char *url)
{
    int i;
    _writeKanimNow = true;
    char* name = new char[strlen(url) + 1 + strlen(".wrl") + 1 + LEN_DEZIMAL_INT_MAX + 1];
    strcpy(name, url);
    char *nameBaseEnd = strrchr(name, '.');
    if (nameBaseEnd == NULL)
        nameBaseEnd = name + strlen(url);
    FloatArray keyTimes;
    bool allInterpolatorsLoop = true;
    for (i = 0; i < _timeSensors.size(); i++) {
        NodeTimeSensor *timer = (NodeTimeSensor *)_timeSensors[i];
        if (!timer->loop()->getValue())
            allInterpolatorsLoop = false;
        int frac = timer->fraction_changed_Field();
        float interval = timer->cycleInterval()->getValue();
        for (SocketList::Iterator *j = timer->getOutput(frac).first(); 
               j != NULL; j = j->next()) {
            Interpolator *interpolator = (Interpolator *)(j->item().getNode());
            MFFloat *keys = interpolator->key();
            for (int k = 0; k < keys->getSize(); k++) {
                float time = keys->getValue(k) * interval;
                if (keyTimes.size() == 0)
                    keyTimes.append(time);
                else if (time > keyTimes[keyTimes.size() - 1])
                    keyTimes.append(time);                    
                else for (int l = 0 ; l < keyTimes.size(); l++) {
                    if (time == keyTimes[l])
                        break;
                    if (time < keyTimes[l]) {
                        keyTimes.insert(time, l);
                        break;
                    }
                }
            }        
        }           
    }
    KANIM_RET_ONERROR( mywritestr(f ,"<?xml version=\"1.0\"?>\n") ) 
    KANIM_RET_ONERROR( mywritestr(f ,"<animation ") )
    if (allInterpolatorsLoop)
        KANIM_RET_ONERROR( mywritestr(f ,"loop=\"true\"") )
    else
        KANIM_RET_ONERROR( mywritestr(f ,"loop=\"false\"") )
    KANIM_RET_ONERROR( mywritestr(f ,">\n") )     
    double t = swGetCurrentTime();
    _timeStart = t;
    for (i = 0; i < _timeSensors.size(); i++)
        ((NodeTimeSensor *) _timeSensors[i])->start(t);
    for (i = 0; i < keyTimes.size(); i++) {
        updateTimeAt(t + keyTimes[i]);
        sprintf(nameBaseEnd, "_%d.wrl", i);
        int filedes = open(name, O_WRONLY | O_CREAT,00666);
        if (filedes == -1) {
            delete [] name;
            _writeKanimNow = false;
            return -1;
        }
        KANIM_RET_ONERROR( write(filedes, name, PURE_VRML97) )
        if (swTruncateClose(filedes)) {
            delete [] name;
            _writeKanimNow = false;
            return -1;
        }
        KANIM_RET_ONERROR( mywritestr(f ,"    <frame file_name=\"") )
        URL fileUrl(name);
        MyString filename = fileUrl.GetFileName();
        if (filename.length() == 0)
            filename = name;
        KANIM_RET_ONERROR( mywritestr(f, (const char *)filename) )
        KANIM_RET_ONERROR( mywritestr(f ,"\" time=\"") )
        KANIM_RET_ONERROR( mywritef(f ,"%f", keyTimes[i]) )
        KANIM_RET_ONERROR( mywritestr(f ,"\"/>\n") )
    }
    KANIM_RET_ONERROR( mywritestr(f ,"</animation>\n") )
    delete [] name;
    if (swTruncateClose(f)) {
        _writeKanimNow = false;
        return -1;
    }
    updateTime();
    _writeKanimNow = false;    
    return 0;
}

static bool hasEmptyMaterialCallback(Node *node, void *data)
{
    bool *emptyMaterial = (bool *) data;
    if (node->getType() == VRML_SHAPE)
        if (((NodeShape *)node)->appearance()->getValue() == NULL)
            *emptyMaterial = true;
    if (node->getType() == VRML_APPEARANCE)
        if (((NodeAppearance *)node)->material()->getValue() == NULL)
            *emptyMaterial = true;
    return true;
}

bool Scene::hasEmptyMaterial()
{
    bool emptyMaterial = false;
    getRoot()->doWithBranch(hasEmptyMaterialCallback, &emptyMaterial);
    return emptyMaterial;
}

void 
Scene::collectAc3dMaterialInfo(char *name, Node *node)
{
    _ac3dMaterialNameArray.append(name);
    _ac3dMaterialNodeArray.append(node);
    int materialIndex = _ac3dMaterialNodeArray.size() - 1;

    StringMap::Chain::Iterator *j;
    bool alreadyInMap = false;
    for (int i = 0; i < _ac3dMaterialIndexMap.width(); i++)
        for (j = _ac3dMaterialIndexMap.chain(i).first(); j != NULL; j = j->next())
            if (strcmp(name, j->item()->getKey()) == 0)
                alreadyInMap = true;
    if (alreadyInMap)
        TheApp->PrintMessageWindowsString(IDS_AC3D_EXPORT_COLOR_IGNORED, name);
    else
        _ac3dMaterialIndexMap[name] = materialIndex;
}

static void handleMaterial(Scene *scene, char *name, Node *node)
{
    if (name != NULL) {
        scene->collectAc3dMaterialInfo(name, node);
        //free(name);? later !
    }
}

int 
Scene::writeAc3d(int f, bool raven) 
{
    RET_ONERROR( mywritestr(f ,"AC3Db\n") ) 

    _ac3dMaterialNameArray.resize(0);
    _ac3dMaterialNodeArray.resize(0);
    _ac3dMaterialIndexMap.removeAll();

    NodeList *childList = ((NodeGroup *)getRoot())->children()->getValues();

    int i;
    for (i = 0; i < childList->size(); i++)
        childList->get(i)->handleAc3dMaterial(handleMaterial ,this);

    if (raven) {
        int materialIndex = 0;
        StringMap::Chain::Iterator *j;
        for (int i = 0; i < _ac3dMaterialIndexMap.width(); i++) {
            for (j = _ac3dMaterialIndexMap.chain(i).first(); j != NULL; 
                 j = j->next()) {
                const char *materialName = j->item()->getKey();
                Node *materialNode = NULL;
                for (int k = 0; k < _ac3dMaterialNameArray.size(); k++) {
                     if (strcmp(materialName, _ac3dMaterialNameArray[k]) == 0) {
                         materialNode = _ac3dMaterialNodeArray[k];
                         materialNode->setAc3dMaterialIndex(materialIndex);
                     } 
                }
                if (materialNode != NULL) {
                    materialNode->writeAc3dMaterial(f, 0, materialName);
                    materialIndex += materialNode->getIncAc3dMaterialIndex();
                }
            }
        }
        _ac3dEmptyMaterial = materialIndex;
    } else {
        int materialIndex = 0;
        for (int i = 0; i < _ac3dMaterialNameArray.size(); i++) {
            const char *materialName = _ac3dMaterialNameArray[i];
            Node *materialNode = _ac3dMaterialNodeArray[i]; 
            materialNode->setAc3dMaterialIndex(materialIndex); 
            materialNode->writeAc3dMaterial(f, 0, materialName);
            materialIndex += materialNode->getIncAc3dMaterialIndex();
        }
        _ac3dEmptyMaterial = materialIndex;
    }

    
    // write ac3d equivalent of empty material
    RET_ONERROR( mywritestr(f, "MATERIAL \"") )
    if (raven) 
        RET_ONERROR( mywritestr(f, "__") ) 
    RET_ONERROR( mywritestr(f, TheApp->GetDefaultAc3dMaterialName()) )
    RET_ONERROR( mywritestr(f, "\" ") )
    RET_ONERROR( mywritestr(f, "rgb 1 1 1  amb 0.2 0.2 0.2  emis 0 0 0  ") ) 
    RET_ONERROR( mywritestr(f, "spec 0 0 0  shi 2  trans 0\n") )

    int kids = 0;
    for (i = 0; i < childList->size(); i++)
        if (childList->get(i)->canWriteAc3d())
            kids++;
    RET_ONERROR( mywritef(f, "OBJECT world\nkids %d\n", kids) )
    for (i = 0; i < childList->size(); i++)
        RET_ONERROR( childList->get(i)->writeAc3d(f, 0) )
    return(0);
}

int 
Scene::writeCattGeo(char *path) 
{
    _cattGeoPath = path;
    _cattGeoFileCounter = 0;
    _cattGeoCornerCounter = 1;
    _cattGeoPlaneCounter = 1;
    _cattRecIsWritten = false;
    _cattSrcIsWritten = false;

    if (_root->writeCattGeo(0,0))
        return -1;

    return(0);
}

bool 
Scene::validateLdrawExport()
{
    NodeList *list = searchNodeType(DUNE_LDRAW_DAT_EXPORT);
    if (list->size() == 0) {
        TheApp->MessageBoxId(IDS_LDRAW_DAT_EXPORT_NODE_CREATED, SW_MB_WARNING);
        Node *node = createNode("LdrawDatExport");
        execute(new MoveCommand(node, NULL, -1, _root, _rootField));
        UpdateViews(NULL, UPDATE_SELECTION);
        return false;
    }
    if (list->size() > 1) {
        TheApp->MessageBoxId(IDS_TO_MUCH_LDRAW_DAT_EXPORT_NODES);
        return false;
    }
    NodeLdrawDatExport *node = (NodeLdrawDatExport *)list->get(0);
    return node->validate();
}

int                 
Scene::writeLdrawDat(int filedes, const char *path)
{
    _currentLdrawColor = -1;
    NodeList *list = searchNodeType(DUNE_LDRAW_DAT_EXPORT);
    const char *filename = strrchr(path, swGetPathSelector());
    if (filename == NULL)
        filename = path;
    else
        filename++;
    if (list->size() == 1) {
        NodeLdrawDatExport *node = (NodeLdrawDatExport *)list->get(0);
        if (node->writeLdrawDatHeader(filedes, filename) != 0)
            return 0;
    };
    _root->writeLdrawDat(filedes, 0);
    return 0;
}

int 
Scene::writeCDeclaration(int f, int languageFlag) 
{
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++)
        for (j = _protos.chain(i).first(); j != NULL; j = j->next()) {
            Proto *proto = j->item()->getData();
            if (proto->getType() == VRML_COMMENT)
                continue;
            if (proto->isMismatchingProto())
                continue;   
            if (proto->writeCDeclaration(f, languageFlag) != 0)
                return -1;
        }
    return(0);
}

int
Scene::writeCDataFunctionsCalls(int f, int languageFlag)
{
    zeroNumReducedDataFunctions();
    int numFunctionCounter = 0;

    if (languageFlag & MANY_JAVA_CLASSES)
        RET_ONERROR( mywritef(f, "class data%sFunctionClass_%d {\n",
                              TheApp->getCPrefix(), 
                              getNumReducedDataFunctions()) )

    RET_ONERROR( mywritestr(f, "    ") )
    if (languageFlag & MANY_JAVA_CLASSES)
        RET_ONERROR( mywritestr(f, "public static ") )
    RET_ONERROR( mywritef(f, "void data%sFunction_%d() {\n",
                          TheApp->getCPrefix(), getNumReducedDataFunctions()) )
    numFunctionCounter++;
    int i;
    for (i = 0; i < getNumDataFunctions(); i++) {
        if (numFunctionCounter > MAX_JAVA_CALLS) {
            RET_ONERROR( mywritef(f, "    }\n") )
            if (languageFlag & MANY_JAVA_CLASSES)
                RET_ONERROR( mywritef(f, "}\n") )
            increaseNumReducedDataFunctions();
            if (languageFlag & MANY_JAVA_CLASSES)
                RET_ONERROR( mywritef(f, "class data%sFunctionClass_%d {\n",
                                      TheApp->getCPrefix(), 
                                      getNumReducedDataFunctions()) )
            RET_ONERROR( mywritestr(f, "    ") )
            if (languageFlag & MANY_JAVA_CLASSES)
                RET_ONERROR( mywritestr(f, "public static ") )
            RET_ONERROR( mywritef(f, "void data%sFunction_%d() {\n",
                                  TheApp->getCPrefix(),
                                  getNumReducedDataFunctions()) ) 
        }

        RET_ONERROR( mywritestr(f, "        ") ) 
        if (languageFlag & MANY_JAVA_CLASSES)
                RET_ONERROR( mywritef(f, "%sScenegraphFunctions%d.", 
                                      TheApp->getCPrefix(), i) )        
        RET_ONERROR( mywritef(f, "data%sFunction%d();\n", 
                              TheApp->getCPrefix(), i) )

        if (numFunctionCounter > MAX_JAVA_CALLS) {
            numFunctionCounter = 0;
        } else
            numFunctionCounter++;

    }
    increaseNumReducedDataFunctions();
    RET_ONERROR( mywritef(f, "    }\n") )
    if (languageFlag & MANY_JAVA_CLASSES)
        RET_ONERROR( mywritef(f, "}\n") )
    return(0);
}

struct writeCStruct {
    int filedes;
    int ret;
    int languageFlag;
    bool outsideJavaClass;
};

static bool writeCNodeData(Node *node, void *data)
{
    if (node == NULL)
        return true;

    Node* convertedNode = node->getConvertedNode();
    if (convertedNode != NULL) {
        convertedNode->doWithBranch(writeCNodeData, data);
        return true;
    }

    Proto *proto = node->getProto();
    if (proto->getType() == VRML_COMMENT)
        return true;
    if (proto->isMismatchingProto())
        return true;   

    // do not write multiple declarations for USE'd nodes
    if (node->getFlag(NODE_FLAG_TOUCHED))
        return true;
    node->setFlag(NODE_FLAG_TOUCHED); 

    if (node->hasProtoNodes())
        ((NodePROTO *)node)->getProtoRoot()->doWithBranch(writeCNodeData, data);

    struct writeCStruct *cWrite = (struct writeCStruct *)data;

    bool extraJavaClass = false;
    if (node->needExtraJavaClass())
        extraJavaClass = true;

    if (cWrite->languageFlag & MANY_JAVA_CLASSES)
        if (cWrite->outsideJavaClass) {
            if (!extraJavaClass)
                return true;
        } else {
            if (extraJavaClass)
                return true;
        } 

    int f = cWrite->filedes;
    int error = 0;
    if (!(extraJavaClass && (cWrite->languageFlag & MANY_JAVA_CLASSES)))
        if (mywritestr(f, "    ") != 0) {
            cWrite->ret = -1;
            return false;
        }
    if (cWrite->languageFlag & C_SOURCE)
        if (mywritestr(f, "struct ") != 0) {
            cWrite->ret = -1;
            return false;
        }
        
    if (cWrite->languageFlag & MANY_JAVA_CLASSES) {
        if (extraJavaClass)
            RET_ONERROR( mywritef(f, "class %s%s {\n    ",
                                  TheApp->getCPrefix(), 
                                  node->getVariableName()) )
        RET_ONERROR( mywritestr(f, "public static ") ) 
    }

    if (mywritestr(f, node->getClassName()) != 0)
        error = -1;
    if ((error == -1) || (mywritestr(f, " ") != 0))
        error = -1;
    else if (mywritestr(f, node->getVariableName()) != 0)
        error = -1;
    else if (mywritestr(f, ";\n") != 0)
        error = -1;
    if (error != 0) {
        cWrite->ret = -1;
        return false;
    }
    if (cWrite->languageFlag & MANY_JAVA_CLASSES) 
        if (extraJavaClass)
            RET_ONERROR( mywritestr(f, "}\n") )  
    return true;
}

int 
Scene::writeC(int f, int languageFlag) 
{
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "/*") )
    else
        RET_ONERROR( mywritestr(f, "//") )
    RET_ONERROR( mywritestr(f, " VRML97/X3D file \"") )
    URL *url = new URL(getURL());
    RET_ONERROR( mywritestr(f, url->GetFileName()) )
    delete url;
    RET_ONERROR( mywritestr(f, "\" converted to ") )
    if (languageFlag & C_SOURCE) 
        RET_ONERROR( mywritestr(f, "C") )
    else if (languageFlag & CC_SOURCE)
        RET_ONERROR( mywritestr(f, "C++") )
    else if (languageFlag & JAVA_SOURCE)
        RET_ONERROR( mywritestr(f, "java") )
    RET_ONERROR( mywritestr(f, " with white_dune") )
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "*/") )
    RET_ONERROR( mywritestr(f, "\n\n") ) 
    if (TheApp->isFirstCExport()) {
        if (languageFlag & C_SOURCE) {
            RET_ONERROR( mywritestr(f, "#ifndef NULL\n") )
            RET_ONERROR( mywritestr(f, "# define NULL (void *)0\n") )
            RET_ONERROR( mywritestr(f, "#endif\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
            RET_ONERROR( mywritestr(f, ";\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "typedef void ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, ";\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, "Struct;\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "typedef void (*") )
            RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
            RET_ONERROR( mywritestr(f, "Callback)") )
            RET_ONERROR( mywritestr(f, "(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, " *node, void *data);\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "void treeRenderCallback(struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, "Struct *node, void *data);\n") )
            RET_ONERROR( mywritestr(f, "void treeDoWithDataCallback(struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, "Struct *node, void *data);\n") )
            RET_ONERROR( mywritestr(f, "void treeEventsProcessed(struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, "Struct *node, void *data);\n") )
            RET_ONERROR( mywritestr(f, "\n\n") )
        }
        if (languageFlag & CC_SOURCE) {
            RET_ONERROR( mywritestr(f, "#include <stddef.h> // for NULL\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "class ") )
            RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
            RET_ONERROR( mywritestr(f, ";\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "class ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " {\n") )
            RET_ONERROR( mywritestr(f, "public:\n") )
            RET_ONERROR( mywritestr(f, "    ") )    
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) )
            RET_ONERROR( mywritestr(f, "() {\n") )
            RET_ONERROR( mywritestr(f, "        m_parent = NULL;\n") )
            RET_ONERROR( mywritestr(f, "        m_protoRoot = NULL;\n") )
            RET_ONERROR( mywritestr(f, "    }\n") )
            RET_ONERROR( mywritestr(f, "    virtual void treeRender(") )
            RET_ONERROR( mywritestr(f, "void *dataptr) {\n") )
            RET_ONERROR( mywritestr(f, "        if (m_protoRoot != NULL)\n") )
            RET_ONERROR( mywritestr(f, "            m_protoRoot->") )
            RET_ONERROR( mywritestr(f, "treeRender(dataptr);\n") )
            RET_ONERROR( mywritestr(f, "    }\n") )
            RET_ONERROR( mywritestr(f, "    virtual void treeDoWithData(") )
            RET_ONERROR( mywritestr(f, "void *dataptr) {\n") )
            RET_ONERROR( mywritestr(f, "        if (m_protoRoot != NULL)\n") )
            RET_ONERROR( mywritestr(f, "            m_protoRoot->") )
            RET_ONERROR( mywritestr(f, "treeDoWithData(dataptr);\n") )
            RET_ONERROR( mywritestr(f, "    }\n") )
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " *m_parent;\n") )
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " *m_protoRoot;\n") )
            RET_ONERROR( mywritestr(f, "};\n") )
            RET_ONERROR( mywritestr(f, "\n") )
            RET_ONERROR( mywritestr(f, "typedef void (*") )
            RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
            RET_ONERROR( mywritestr(f, "Callback)") )
            RET_ONERROR( mywritestr(f, "(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " *node, void *dataptr);\n") )
            RET_ONERROR( mywritestr(f, "\n\n") ) 
        }
        if (languageFlag & JAVA_SOURCE) {
            RET_ONERROR( mywritestr(f, "class ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " {\n") )
            RET_ONERROR( mywritestr(f, "    void treeRender() {\n") )
            RET_ONERROR( mywritestr(f, "        if (m_protoRoot != null)\n") )
            RET_ONERROR( mywritestr(f, "            m_protoRoot.") )
            RET_ONERROR( mywritestr(f, "treeRender();\n") )
            RET_ONERROR( mywritestr(f, "    }\n") )
            RET_ONERROR( mywritestr(f, "    void treeDoWithData() {\n") )
            RET_ONERROR( mywritestr(f, "        if (m_protoRoot != null)\n") )
            RET_ONERROR( mywritestr(f, "            m_protoRoot.") )
            RET_ONERROR( mywritestr(f, "treeDoWithData();\n") )
            RET_ONERROR( mywritestr(f, "    }\n") )
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " m_parent;\n") )
            RET_ONERROR( mywritestr(f, "    Object m_data;\n") )
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " m_protoRoot;\n") )
            RET_ONERROR( mywritestr(f, "};\n") )
            RET_ONERROR( mywritestr(f, "\n") )

            RET_ONERROR( mywritestr(f, "class RenderCallback {\n") )
            RET_ONERROR( mywritestr(f, "   public void render(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " node) {}\n") )
            RET_ONERROR( mywritestr(f, "}\n") )

            RET_ONERROR( mywritestr(f, "class TreeRenderCallback {\n") )
            RET_ONERROR( mywritestr(f, "   public void treeRender(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " node) {}\n") )
            RET_ONERROR( mywritestr(f, "}\n") )
            RET_ONERROR( mywritestr(f, "\n") )

            RET_ONERROR( mywritestr(f, "class DoWithDataCallback {\n") )
            RET_ONERROR( mywritestr(f, "   public void doWithData(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " node) ") )
            RET_ONERROR( mywritestr(f, "{}\n") )
            RET_ONERROR( mywritestr(f, "}\n") )


            RET_ONERROR( mywritestr(f, "class TreeDoWithDataCallback {\n") )
            RET_ONERROR( mywritestr(f, "   public void treeDoWithData(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " node) ") )
            RET_ONERROR( mywritestr(f, "{}\n") )
            RET_ONERROR( mywritestr(f, "}\n") )
            RET_ONERROR( mywritestr(f, "\n") )

            RET_ONERROR( mywritestr(f, "class EventsProcessedCallback {\n") )
            RET_ONERROR( mywritestr(f, "   public void eventsProcessed(") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " node) ") )
            RET_ONERROR( mywritestr(f, "{}\n") )
            RET_ONERROR( mywritestr(f, "}\n") )

            RET_ONERROR( mywritestr(f, "\n\n") )
        }
        RET_ONERROR( writeCDeclaration(f, languageFlag) )
    }
    RET_ONERROR( mywritestr(f, "\n") ) 
    if (languageFlag & MANY_JAVA_CLASSES) {
        struct writeCStruct cWrite;
        cWrite.filedes = f;
        cWrite.ret = 0;
        cWrite.languageFlag = languageFlag;
        cWrite.outsideJavaClass = true;
        getNodes()->clearFlag(NODE_FLAG_TOUCHED); // to handle DEF/USE
        _root->doWithBranch(writeCNodeData, &cWrite);
        if (cWrite.ret !=0)
            return -1;
        RET_ONERROR( mywritestr(f, "\n") ) 

        if (_root->writeC(f, languageFlag | OUTSIDE_JAVA_CLASS))
            return -1;

        getNodes()->clearFlag(NODE_FLAG_TOUCHED); // to handle DEF/USE
        zeroNumDataFunctions();
        if (_root->writeCDataAsFunctions(f, languageFlag))
            return -1;

        RET_ONERROR( mywritestr(f, "\n") )        
        RET_ONERROR(_root->writeCDataFunctions(f, languageFlag) )
        RET_ONERROR( mywritestr(f, "\n") )        
        RET_ONERROR(writeCDataFunctionsCalls(f, languageFlag) )
        RET_ONERROR( mywritestr(f, "\n") )        
    }
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "struct ") )
    else
        RET_ONERROR( mywritestr(f, "class ") ) 
    RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
    RET_ONERROR( mywritestr(f, " {\n") ) 
    if (languageFlag & CC_SOURCE)
        RET_ONERROR( mywritestr(f, "public:\n") )
    struct writeCStruct cWrite;
    cWrite.filedes = f;
    cWrite.ret = 0;
    cWrite.languageFlag = languageFlag;
    cWrite.outsideJavaClass = false;
    getNodes()->clearFlag(NODE_FLAG_TOUCHED); // to handle DEF/USE
    _root->doWithBranch(writeCNodeData, &cWrite);
    if (cWrite.ret !=0)
        return -1;
    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, "};\n\n") )

        if (TheApp->isFirstCExport()) {
            RET_ONERROR( mywritestr(f, "struct ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, "Struct {\n") ) 
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " *m_parent;\n") )
            RET_ONERROR( mywritestr(f, "    ") )
            RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
            RET_ONERROR( mywritestr(f, " *m_protoRoot;\n") )
            RET_ONERROR( mywritestr(f, "    int m_type;\n") )
            RET_ONERROR( mywritestr(f, "};\n\n") )
        }
     
        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, "Init(") )
        RET_ONERROR( mywritestr(f, "struct ") )
        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, " *self) {\n") )
        if (_root->writeC(f, languageFlag))
            return -1;
        RET_ONERROR( mywritestr(f, "}\n\n") ) 
        if (TheApp->isFirstCExport()) {
            RET_ONERROR( writeCTreeCallback(f, "Render") )
            RET_ONERROR( mywritestr(f, "\n") ) 
            RET_ONERROR( writeCTreeCallback(f, "DoWithData") )
            RET_ONERROR( mywritestr(f, "\n") ) 
            RET_ONERROR( writeCTreeCallback(f, "EventsProcessed", false) )
            RET_ONERROR( mywritestr(f, "\n") ) 
        }

        if (TheApp->isFirstCExport())
            for (int i = 0; i < LAST_TYPE; i++) {
                FieldValue *value = typeDefaultValue(i);
                value->writeCSendEventFunction(f, languageFlag);
            }
    }
    getNodes()->clearFlag(NODE_FLAG_TOUCHED); // to handle DEF/USE
    if (languageFlag & CC_SOURCE) {        
        RET_ONERROR( mywritestr(f, "public:\n") ) 
        RET_ONERROR( mywritestr(f, "    ") ) 
        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, "();\n") )

        RET_ONERROR( mywritestr(f, "    ") ) 
        RET_ONERROR( mywritestr(f, "void render(void *data) ") )
        RET_ONERROR( mywritestr(f, "{ root.treeRender(data); }\n") ) 

        RET_ONERROR( mywritestr(f, "    ") ) 
        RET_ONERROR( mywritestr(f, "void doWithData(void *data) ") )
        RET_ONERROR( mywritestr(f, "{ root.treeDoWithData(data); }\n") ) 

        RET_ONERROR( mywritestr(f, "};\n\n") ) 

        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, "::") )
        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, "() {\n") )
        if (_root->writeC(f, languageFlag))
            return -1;
        RET_ONERROR( mywritestr(f, "}\n") ) 
        if (TheApp->isFirstCExport())
            for (int i = 0; i < LAST_TYPE; i++) {
                FieldValue *value = typeDefaultValue(i);
                value->writeCSendEventFunction(f, languageFlag);
            }
    } else if (languageFlag & JAVA_SOURCE) {        
        int i;
        if (!(languageFlag & MANY_JAVA_CLASSES)) {
            zeroNumDataFunctions();

            if (_root->writeCDataAsFunctions(f, languageFlag))
                return -1;

            RET_ONERROR(_root->writeCDataFunctions(f, languageFlag) )

            RET_ONERROR(writeCDataFunctionsCalls(f, languageFlag) )
        }


        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, TheApp->getCSceneGraphName()) ) 
        RET_ONERROR( mywritestr(f, "() {\n") )

        if (_root->writeC(f, languageFlag))
            return -1;

        for (i = 0; i < getNumReducedDataFunctions(); i++) {
            RET_ONERROR( mywritestr(f, "        ") )
            if (languageFlag & MANY_JAVA_CLASSES)
                RET_ONERROR( mywritef(f, "data%sFunctionClass_%d.",
                                      TheApp->getCPrefix(), i) )
            RET_ONERROR( mywritef(f, "data%sFunction_%d();\n",
                                      TheApp->getCPrefix(), i) ) 
        }

        RET_ONERROR( mywritestr(f, "    }\n") ) 
        
        RET_ONERROR( mywritestr(f, "    ") ) 
        if (languageFlag & MANY_JAVA_CLASSES) 
            RET_ONERROR( mywritestr(f, "static ") )
        RET_ONERROR( mywritestr(f, "void render() ") )
        RET_ONERROR( mywritestr(f, "{ root.treeRender(); }\n") ) 
        RET_ONERROR( mywritestr(f, "    ") ) 
        if (languageFlag & MANY_JAVA_CLASSES) 
            RET_ONERROR( mywritestr(f, "static ") )
        RET_ONERROR( mywritestr(f, "void doWithData() ") )
        RET_ONERROR( mywritestr(f, "{ root.treeDoWithData(); }\n") ) 

        for (i = 0; i < LAST_TYPE; i++) {
            FieldValue *value = typeDefaultValue(i);
           value->writeCSendEventFunction(f, languageFlag);
        }
    }
    if (languageFlag & JAVA_SOURCE)
        RET_ONERROR( mywritestr(f, "};\n\n") ) 
    return(0);
}

int 
Scene::writeCTreeCallback(int f, const char *functionName, 
                          bool callTreeCallback)
{
    RET_ONERROR( mywritestr(f, "void tree") )
    RET_ONERROR( mywritestr(f, functionName) )
    if (callTreeCallback)
        RET_ONERROR( mywritestr(f, "Callback") )
    RET_ONERROR( mywritestr(f, "(struct ") )
    RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
    RET_ONERROR( mywritestr(f, "Struct *node,") )
    RET_ONERROR( mywritestr(f, " void *data) {\n") )
    RET_ONERROR( mywritestr(f, "    switch(node->") ) 
    RET_ONERROR( mywritestr(f, "m_") ) 
    RET_ONERROR( mywritestr(f, "type) {\n") ) 
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++)
        for (j = _protos.chain(i).first(); j != NULL; j = j->next()) {
            Node *node = j->item()->getData()->create(this);
            if (node->getType() == VRML_COMMENT)
                continue;
            if (node->getProto()->isMismatchingProto())
                continue;
            if (node->isPROTO())
                continue;
            if (node->getType() != -1) {
                RET_ONERROR( mywritestr(f, "      case ") ) 
                RET_ONERROR( mywritef(f, "%d", node->getType()) ) 
                RET_ONERROR( mywritestr(f, ":\n") ) 
                if (callTreeCallback) {
                    RET_ONERROR( mywritestr(f, "        if (") ) 
                    RET_ONERROR( mywritestr(f, node->getClassName()) )
                    RET_ONERROR( mywritestr(f, "Tree") )
                    RET_ONERROR( mywritestr(f, functionName) )
                    RET_ONERROR( mywritestr(f, "Callback)\n") )
                    RET_ONERROR( mywritestr(f, "            ") ) 
                    RET_ONERROR( mywritestr(f, node->getClassName()) )
                    RET_ONERROR( mywritestr(f, "Tree") )
                    RET_ONERROR( mywritestr(f, functionName) )
                    RET_ONERROR( mywritestr(f, "Callback(node, data);\n") )
                    RET_ONERROR( mywritestr(f, "        else\n") ) 
                    RET_ONERROR( mywritestr(f, "    ") ) 
                }
                RET_ONERROR( mywritestr(f, "        ") ) 
                RET_ONERROR( mywritestr(f, node->getClassName()) )
                RET_ONERROR( mywritestr(f, "Tree") )
                RET_ONERROR( mywritestr(f, functionName) )
                RET_ONERROR( mywritestr(f, "(node, data);\n") )
                RET_ONERROR( mywritestr(f, "        break;\n") )
            }
        }
    RET_ONERROR( mywritestr(f, "    }\n") ) 
    RET_ONERROR( mywritestr(f, "}\n") ) 
    return 0;
}

static bool searchExtensionProto(int extension, Node *node, void *data)
{
    Array<Proto *> *protoArrayPtr = (Array<Proto *> *) data;
    if (!node->hasDefault(extension) /* && !node->hasRoute(extension)*/) {
        for (int i = 0; i < (*protoArrayPtr).size(); i++)
            if ((*protoArrayPtr)[i] == node->getProto())
                return true;        
        (*protoArrayPtr).append(node->getProto());
    }
    // Todo: also check if Route to extension event
    return true;     
}

static bool searchCoverExtensionProto(Node *node, void *data)
{
    return searchExtensionProto(FF_COVER_ONLY, node, data);
}

static bool searchKambiExtensionProto(Node *node, void *data)
{
    return searchExtensionProto(FF_KAMBI_ONLY, node, data);
}

int
Scene::writeExtensionProtos(int f, int flag)
{
    int x3d = false;
    if (flag == FF_X3D_ONLY)
        x3d = true;
    int i;
    Array<Proto *> protoArray;
    NodeList *nodes = ((NodeGroup *)getRoot())->children()->getValues();
    for (i = 0; i < nodes->size(); i++) {
        if (flag == FF_COVER_ONLY)
            nodes->get(i)->doWithBranch(searchCoverExtensionProto, &protoArray);
        if (flag == FF_KAMBI_ONLY)
            nodes->get(i)->doWithBranch(searchKambiExtensionProto, &protoArray);
    }
    for (i = 0; i < protoArray.size(); i++) {
        addProtoName(protoArray[i]->getName(false));
        Proto *newProto = new Proto(this, protoArray[i], flag);
        if (newProto->write(f, 0, x3d) != 0) {
            deleteExtensionProtos();
            return -1;
        }
        if (mywritestr(f, "\n") != 0) {
            deleteExtensionProtos();
            return -1;
        }

        _writtenExtensionProtos.append(newProto);
    }
    return 0;
}

void
Scene::deleteExtensionProtos(void)
{
    for (int i = 0; i < _writtenExtensionProtos.size(); i++) {
        deleteProtoName(_writtenExtensionProtos[i]->getName(false));
        deleteProto(_writtenExtensionProtos[i]->getName(false));
        delete(_writtenExtensionProtos[i]);
    }
    _writtenExtensionProtos.resize(0);
}

Proto *
Scene::getExtensionProto(Proto *proto)
{
    for (int i = 0; i < _writtenExtensionProtos.size(); i++)
        if (_writtenExtensionProtos[i]->getNode(0)->getProto() == proto)
            return _writtenExtensionProtos[i];
    return NULL;
}


void
Scene::addProto(MyString name, Proto *value)
{
    if (belongsToNodeWithExternProto(name))
        return;
    _protos[name] = value;
}

void Scene::deleteProto(MyString name)
{
    if (_protos.hasKey(name))
        _protos.remove(name);
}

Proto *
Scene::getProto(MyString name)
{
    return _protos[name];
}

Proto *
Scene::getExtensionProto(MyString name)
{
    Proto *proto = NULL;
    MyString protoName = name;
    // TODO: handle X3D node names like the following in a better way
    if (strcmp(protoName, "NurbsPatchSurface") == 0) {
        protoName = "";
        protoName += "NurbsSurface";
    }
    if (_protos.hasKey(protoName)) 
        proto = _protos[protoName];
    else
        return NULL;
    if (TheApp->getCoverMode() && proto->isExtensionProto(FF_COVER_ONLY)) {
        proto = proto->getNode(0)->getProto();
        proto->setScene(this);
    } 
    if (TheApp->getKambiMode() && proto->isExtensionProto(FF_KAMBI_ONLY)) {
        proto = proto->getNode(0)->getProto();
        proto->setScene(this);
    }
    return proto;
}



// static 
bool
Scene::validRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    bool onlyOneConnectAnything = false;
    // "connect anything" route of ScriptNode (eventOut will be created)
    if (src->getType() == VRML_SCRIPT)
        if (eventOut == src->getProto()->getNumEventOuts())
            onlyOneConnectAnything = true;

    // "connect anything" route of ScriptNode (eventIn will be created)
    if (dst->getType() == VRML_SCRIPT)
        if (eventIn == dst->getProto()->getNumEventIns())
            if (onlyOneConnectAnything)
                onlyOneConnectAnything = false;
            else
                onlyOneConnectAnything = true;

    if (onlyOneConnectAnything)
        return true;

    if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts())
        return false;

    if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns())
        return false;

    if (src->getProto()->getEventOut(eventOut)->getType() != 
        dst->getProto()->getEventIn(eventIn)->getType())
        return false;

    return true;
}

bool
Scene::addRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    if (!validRoute(src, eventOut, dst, eventIn)) return false;
    RouteUpdate hint(src, eventOut, dst, eventIn);

    src->addOutput(eventOut, dst, eventIn);
    dst->addInput(eventIn, src, eventOut);

    // try to copy a exposedField value or field of a eventIn
    // to the first value of a Interpolator

    int field = -1;
    bool isFlag = false;

    // is this dst field an ExposedField?
    ExposedField *e = dst->getProto()->getEventIn(eventIn)->getExposedField();
    if (e) {
        field = e->getField();
        isFlag = e->getFlags() & FF_IS;
    } else {
        // is this dst field an EventIn connected to a Field ?
        field = dst->getProto()->getEventIn(eventIn)->getField();
        isFlag = dst->getProto()->getEventIn(eventIn)->getFlags() & FF_IS;
    }
    if ((field != -1) && (!isFlag)) {
        Interpolator *interp = findUpstreamInterpolator(dst, field);
        if (interp && !((Node *)interp)->isPROTO()) {
            bool setValue = false;
            if (interp->getNumKeys() == 0) 
                setValue = true;
            if (interp->getNumKeys() == 1)
                if (interp->getKey(0) == 0.0f)
                    setValue = true;
            if (setValue) {
                FieldValue *value = dst->getField(field);
                if (value)
                    interp->recordKey(value, true);
            }
        }
    }

    UpdateViews(NULL, UPDATE_ADD_ROUTE, (Hint *) &hint);
    return true;
}

void
Scene::deleteRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts())
        return;

    if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns())
        return;

    RouteUpdate hint(src, eventOut, dst, eventIn);

    src->removeOutput(eventOut, dst, eventIn);
    dst->removeInput(eventIn, src, eventOut);
    UpdateViews(NULL, UPDATE_DELETE_ROUTE, (Hint *) &hint);
}

void
Scene::errorf(const char *fmt, ...)
{
    va_list ap;
    char buf[1024], buf2[1024];
    const char *url = "";  

    va_start(ap, fmt);
    myvsnprintf(buf, 1024, fmt, ap);
    if (TheApp->getImportURL() != NULL)
        url = TheApp->getImportURL();
    mysnprintf(buf2, 1024, "%s %d: %s", url, lineno, buf);
#ifndef _WIN32
    fprintf(stderr, "%s", buf2);
    fflush(stderr);
#endif
    _compileErrors += buf2;
}

void
Scene::invalidNode(const char *name)
{
    errorf("invalid DEF name \"%s\"\n", name);
}

void 
Scene::invalidField(const char *node, const char *field)
{
    errorf("node \"%s\" has no field \"%s\"\n", node, field);
}

static bool reInitNode(Node *node, void *data)
{
    node->reInit();
    return true;     
}

void
Scene::reInitAll(void)
{
    if (_root != NULL)
        _root->doWithBranch(reInitNode, NULL);
}

const char *
Scene::parse(FILE *f, bool protoLibrary, Node* target, int field)
{
    bool canRewind = true;
    if (isatty(fileno(f)))
        canRewind = false;
#ifdef HAVE_LIBZ
    inputFile = gzdopen(fileno(f),"rb");
#else
    inputFile = f;   
#endif    
    isInProtoLibrary = protoLibrary ? 1 : 0;
    scene = this;
    targetNode = target;
    targetField = field;
    lineno = 1;
    _compileErrors = "";
    _isParsing = true;
    ::setVrml();
    bool isXml = false;
    bool isOther = false;
    if (canRewind) {
        int firstChar = -1;
        int end;
        do {
#ifdef HAVE_LIBZ
           end = -1;
           firstChar = gzgetc(inputFile);
#else
           end = EOF;
           firstChar = getc(inputFile);
#endif    
           if (firstChar == '<')
               isXml = true;
           else if (!isspace(firstChar))
               isOther = true;
        } while (isspace(firstChar) && (firstChar != end));
#ifdef HAVE_LIBZ
        gzrewind(inputFile);
#else
        rewind(inputFile);
#endif    
    } else {
        // no rewind possible, check current mode
        isXml = isX3dXml();
        isOther = !isXml;
    }
#ifdef HAVE_LIBEXPAT
    if (isXml)
        parseX3d();
    if (isOther) 
#endif
        yyparse();
    _isParsing = false;
    return _compileErrors;
}

void
Scene::add(Command *cmd)
{
    _undoStack.push(cmd);

    while (!_redoStack.empty()) {
        delete _redoStack.pop();
    }
}

void
Scene::execute(Command *cmd)
{
    cmd->execute();
    add(cmd);
}

void
Scene::backupFieldsStart(void)
{
    // check for forbidden recursive usage or not missing backupFieldsDone()
    assert(_backupCommandList == NULL);
    _backupCommandList = new CommandList();
}

void
Scene::backupFieldsAppend(Node *node, int field)
{
    if (isRecording()) {
        Interpolator *interp = findUpstreamInterpolator(node, field);

        if (interp) {
            interp->backup(_backupCommandList);
        }
    }
    _backupCommandList->append(new FieldCommand(node, field));
}

void 
Scene::backupFieldsDone(void)
{
    add(_backupCommandList);
    _backupCommandList = NULL;
}

void
Scene::backupField(Node *node, int field)
{
    backupFieldsStart();
    backupFieldsAppend(node, field);
    backupFieldsDone();
}

Interpolator *
Scene::findUpstreamInterpolator(Node *node, int field) const
{
    // is this field an ExposedField?
    int eventIn = -1;
    Field *f = node->getProto()->getField(field);
    ExposedField *e = f->getExposedField();
    if (e)
        eventIn = e->getEventIn();
    else if (f->getEventIn() != -1)
        eventIn = f->getEventIn();
    if (eventIn != -1) {
        const SocketList::Iterator *i;

        // check for interpolator routed to the corresponding EventIn;

        for (i = node->getInput(eventIn).first(); i != NULL; i = i->next()) {
            if ((i->item().getNode()->getMaskedNodeClass() == 
                 INTERPOLATOR_NODE) && i->item().getNode()->isInterpolator())
                return ((Interpolator *) i->item().getNode());
        }
    }

    return NULL;
}

void
Scene::setField(Node *node, int field, FieldValue *value)
{
    if (isRecording()) {
        Interpolator *interp = findUpstreamInterpolator(node, field);

        if (interp) {
            interp->recordKey(value,isRunning());
        }
    }
    node->setField(field, value);
    if (TheApp->is4Catt()) {
        if ((field != -1) && (node->getSolidField() == field)) {
            node->generateTreeLabel();
            NodeUpdate *hint= new NodeUpdate(node, NULL, 0);
            UpdateViews(NULL, UPDATE_SOLID_CHANGED, (Hint*) hint);
        }
    }
    if (node->getProto() == _protos["TimeSensor"]) {
       NodeTimeSensor *nodeTimeSensor = (NodeTimeSensor *) node;
       nodeTimeSensor->updateStart(field, value, swGetCurrentTime());
    }
    OnFieldChange(node, field);
}

void
Scene::undo()
{
    if (_undoStack.empty()) {
        assert(0);
        return;
    }

    Command *change = _undoStack.pop();
    change->undo();
    _redoStack.push(change);
}

void
Scene::redo()
{
    if (_redoStack.empty()) {
        assert(0);
        return;
    }

    Command *change = _redoStack.pop();
    change->execute();
    _undoStack.push(change);
}

void
Scene::drawScene(bool pick, int x, int y)
{
    GLint v[4];
    float aspect;
    int i;

    glGetIntegerv(GL_VIEWPORT, v);

    if (v[3])
        aspect = (GLfloat)v[2]/v[3];
    else  // don't divide by zero, not that we should ever run into that...
        aspect = 1.0f;

    _numLights = 0;
    _headlight = true;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float pickRegionSize = PICK_REGION_SIZE * TheApp->GetHandleSize();
    if (pick) gluPickMatrix(x, y, pickRegionSize, pickRegionSize, v);
    float fieldOfView = RAD2DEG(getCamera()->fieldOfView()->getValue());
    if (TheApp->hasFixFieldOfView())
        fieldOfView = TheApp->getFixFieldOfView();
    gluPerspective(fieldOfView, aspect, TheApp->GetNearClippingPlaneDist(),
                                        TheApp->GetFarClippingPlaneDist());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glEnable(GL_LIGHTING);
    glEnable(GL_NORMALIZE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    if (TheApp->getRenderFaster()) {
        glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
        glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
        glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
    } else {
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
        glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
    }
 
    // no scene ambient light, please
    GLint    zero[4] = { 0, 0, 0, 0 };
    glLightModeliv(GL_LIGHT_MODEL_AMBIENT, zero);

    // first pass:  pre-draw traversal
    // enable PointLights and SpotLights; pick up ViewPoints, Fogs.
    // Backgrounds and TimeSensors

    glPushMatrix();

    applyCamera();

    _timeSensors.resize(0);

    _headlightIsSet = false;
    _root->preDraw();

    if (_currentFog)
        _currentFog->apply();
    glPopMatrix();

    // second pass:  main drawing traversal

    glPushMatrix();
    if (_headlight) enableHeadlight();
    glPushName(PICKED_NODE);
    if (_backgrounds.size() > 0) {
        glPushName(NO_HANDLE);    // FIXME:  can't pick backgrounds, yet
        ((NodeBackground *) _backgrounds[0])->apply();
        glPopName();
    }
    applyCamera();
    if (_xrayRendering)
        glDepthMask(GL_FALSE);
    else
        glDepthMask(GL_TRUE);
    glDepthFunc(GL_LESS);
    _root->draw(RENDER_PASS_NON_TRANSPARENT);
    glEnable(GL_BLEND);
    glDepthFunc(GL_LEQUAL);
    _root->draw(RENDER_PASS_TRANSPARENT);

    for (i = 0; i < _numLights; i++) {
        glDisable((GLenum) (GL_LIGHT0 + i));
    }

    _numLights = 0;
    glDisable(GL_FOG);
    glPopMatrix();

    // post-draw phase
    // draw handles 

    enableHeadlight();

    glPushMatrix();
    applyCamera();
    glLoadName(PICKED_RIGID_BODY_HANDLE);
    drawHandles(true);
    glPopMatrix();

    glPushMatrix();
    applyCamera();
    glLoadName(PICKED_HANDLE);
    drawHandles(false);
    glPopMatrix();

    glDisable(GL_LIGHT0);

    glPopName();
    TheApp->printRenderErrors();
}

void
Scene::draw3dCursor(int x, int y)
{
    if (!use3dCursor())
         return;

    float objX;
    float objY;
    float objZ;

    float eyeposition=0; 
    float eyeangle=0; 
    float nearPlane=TheApp->GetNearClippingPlaneDist();
    
    glPushMatrix();

    if (TheApp->useStereo())
       {
       // inexact "toe in" stereo method 
       if (TheApp->getEyeMode()==EM_RIGHT)
          {
          eyeposition= - TheApp->getEyeHalfDist();
          eyeangle= - TheApp->getEyeAngle();
          }
       else if (TheApp->getEyeMode()==EM_LEFT)
          {
          eyeposition= + TheApp->getEyeHalfDist();
          eyeangle= + TheApp->getEyeAngle();
          }
       }
    glTranslatef(eyeposition, 0, 0);
    glRotatef(eyeangle, 0,1,0);

    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);

    float xSize = (viewport[2] - viewport[0]) * TheApp->GetEyeAngleFactor() *
                  (viewport[3] - viewport[1]) / (viewport[2] - viewport[0]);

    unProjectPoint(x - eyeposition * xSize, y, nearPlane, &objX, &objY, &objZ);

    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_LINE_SMOOTH);
    glDisable(GL_BLEND);
    glLineWidth(TheApp->Get3dCursorWidth());

    glBegin(GL_LINE_STRIP);
    if (TheApp->isAnaglyphStereo())
        Util::myGlColor3f(1, 1, 1);
    else
        Util::myGlColor3f(0, 1, 0);
    glVertex3f(objX, -objY, objZ);    
    glVertex3f(objX, -objY, objZ - TheApp->Get3dCursorLength());
    glEnd();
    glEnable(GL_LIGHTING);
    glLineWidth(1);
    glPopAttrib();

    glPopMatrix();
}

Path *
Scene::pick(int x, int y)
{
    GLuint pickBuffer[PICK_BUFFER_SIZE];
#ifdef DEBUG
    for (GLuint i = 0; i < PICK_BUFFER_SIZE; i++) 
        pickBuffer[i] = 0;
#endif
    glSelectBuffer(PICK_BUFFER_SIZE, pickBuffer);
    glRenderMode(GL_SELECT);
    glInitNames();

    TheApp->setEyeMode(EM_NONE);
    drawScene(true, x, y);

    GLint hits = glRenderMode(GL_RENDER);
    Path *path = NULL;
    if (hits < 0) // overflow flag has been set, ignore
        hits = - hits;
    if (hits)
        path = processHits(hits, pickBuffer);
    if (path != NULL) {
        return path;
    } else {
        return new Path(NULL, 0, this);
    }
}

Path *
Scene::processHits(GLint hits, GLuint *pickBuffer)
{
    GLuint *glPath = NULL;
    int glPathLen = 0;
    GLuint depth = UINT_MAX;
    int selectedLevel = 0;
    bool pickedHandle = false;
    bool pickedNode = false;
    int handle = -1;

    for (int i = 0; i < hits; i++) {
        unsigned numNames = *pickBuffer++;
        unsigned minDepth = *pickBuffer++;
        unsigned maxDepth = *pickBuffer++;
        if (*pickBuffer == PICKED_NODE) {
            pickedNode = true;
            pickedHandle = false;
            if (_selectedHandles.size() == 0 && minDepth < depth) {
                glPath = pickBuffer + 1;
                glPathLen = numNames - 1;
                depth = minDepth;
            }
        } else if (*pickBuffer == PICKED_RIGID_BODY_HANDLE) {
            if (_rigidBodyHandleNode == NULL)
                return NULL;
            handle = pickBuffer[numNames - 1];
            return _rigidBodyHandleNode->getPath();
        } else if (*pickBuffer == PICKED_HANDLE) {
            pickedHandle = true;
            pickedNode = false;
            glPath = pickBuffer + 1;
            glPathLen = numNames - 2;
            handle = pickBuffer[numNames - 1];
            depth = minDepth;
        } else if (*pickBuffer == PICKED_3DCURSOR) {
            return NULL;
        } else {
            return NULL;
        }
        pickBuffer += numNames;
    }

    if (pickedNode)
        _lastSelectedHandle = -1;
    else if (handle != -1)
        setSelectedHandle(handle);

    if (glPath == NULL)
        return NULL;

    return new Path((int *) glPath, glPathLen, this);
}

void
Scene::transform(const Path *path)
{
    assert(path != NULL);
    applyCamera();
    const NodeList    *nodes = path->getNodes();
    int size = nodes->size() - 1;
    for (int i = 0; i < size; i++) {
        nodes->get(i)->transform();
    }
}

// search for a Transform node in a path
// return new path to Transform node or NULL if not found

Path* Scene::searchTransform(void)
{
    Path* transform = new Path(*_selection);
    if (transform != NULL) { 
        if ((transform->getNode()->getType() == VRML_TRANSFORM) &&
             !transform->getNode()->isInsideProto())
            return transform;
        while (transform->getNode() != _root) {
            Path* old_transform = transform;
//            transform = newPath(transform->getNode()->getParent());
            transform = new Path(*(transform->getNode()->getParent()->getPath()));
            if ((transform->getNode()->getType() == VRML_TRANSFORM) &&
                !transform->getNode()->isInsideProto())
                break;
            // delete old_transform; // bug: deleting a path cause a crash
        }
        if (transform->getNode()==_root)
            transform = NULL;
    }
    return transform;
}

static bool checkHandleParentsForRigidBody(Node *node, void *data) {
    Scene *scene = (Scene *)data;
    if (node == NULL)
        return true;
    if (node->getType() == X3D_RIGID_BODY)
        if (node->isInScene(scene))
            scene->setRigidBodyHandleNode(node);
    return true;
}

void 
Scene::finishDrawHandles(void)
{
    int glPushCount;
    glGetIntegerv(GL_NAME_STACK_DEPTH, &glPushCount);
    glPushCount--;
    for (int i = 0; i < glPushCount; i++)
        glPopName();
}

void
Scene::drawHandles(bool drawRigidBodyHandles)
{
    glDisable(GL_DEPTH_TEST);
    switch (TheApp->GetHandleMode()) {
      case HM_NONE:
        break;
      case HM_TREE:
        // draw handles for all nodes in the current selection path
        if ((_selection != NULL) && (!drawRigidBodyHandles)) {
            glPushMatrix();
            int len = _selection->getPathLen();
            const int *path = _selection->getPath();
            Node *node = _root;
            for (int i = 0; i < len;) {
                int field = path[i++];
                if (i >= len)
                    break;
                node = _selection->getNextNode(node, field, i);
                if (node == NULL)
                    continue;
                glPushName(field);
                glPushName(path[i++]);
                node->drawHandles();
                node->transform();
            }
            glPopMatrix();
        }
        break;
      case HM_SELECTED:
        if (_selection != NULL) {
            int i = 0;
            glPushMatrix();
            int len = _selection->getPathLen();
            const int *path = _selection->getPath();
            Node *node = _root;
            Node *handlenode = _root;
            Node *lastnode = _root;
            _rigidBodyHandleNode = NULL;
            for (i = 0; i < len;) {
                int field = path[i++];
                if (i >= len) {
                    break;
                }
                node = _selection->getNextNode(node, field, i++);
                if (node == NULL) {
                    finishDrawHandles();
                    return;
                }
                /* search last transform node in path */
                if ((node->getType() == VRML_TRANSFORM) &&
                    (!node->isInsideProto()))
                   handlenode = node;
                // search for a RigidBody node in the Parents of a 
                // X3DNBodyCollidableNode
                if (node->matchNodeClass(BODY_COLLIDABLE_NODE) &&
                    (!node->isInsideProto())) {
                    _rigidBodyHandleNode = NULL;
                    node->doWithParents(checkHandleParentsForRigidBody, this);
                    if (_rigidBodyHandleNode != NULL)
                        handlenode = node;
                }
            }
            if (!node->isInsideProto())
                lastnode = node;
            node = _root;
            for (i = 0; i < len;) {
                int field = path[i++];
                if (i >= len)
                    break;
                node = _selection->getNextNode(node, field, i);
                if (node == NULL) {
                    finishDrawHandles();
                    return;
                }
                glPushName(field);
                glPushName(path[i++]);
                /* display last transform node in path */
                if ((node == handlenode) || (node == lastnode)) {
                     if ((node == handlenode) && 
                         (_rigidBodyHandleNode != NULL)) {
                         if (drawRigidBodyHandles)
                             _rigidBodyHandleNode->drawHandles();
                     } else {
                         // draw joints ?
                         if (!drawRigidBodyHandles)
                             node->drawHandles();    
                     }
                }
                if (node->getType() != X3D_RIGID_BODY)
                    node->transform();                
            }
            glPopMatrix();
        }
//        if (_showJoints)
//            drawHandlesRec(_root, true);
        break;
      case HM_ALL:
        if (!drawRigidBodyHandles)
            drawHandlesRec(_root);
        break;
    }
    finishDrawHandles();   
}

void
Scene::drawHandlesRec(Node *node, bool drawOnlyJoints) const
{
    int numFields = node->getProto()->getNumFields();

    glPushMatrix();
    bool draw = true;
    if (drawOnlyJoints)
        if (!node->isJoint())
            draw = false;
    if (draw)
        if (node->isJoint() && !node->isFirstUSE())
            draw = false;
    if (draw)
        node->drawHandles();
    node->transform();
    for (int i = 0; i < numFields; i++) {
        FieldValue *value = node->getField(i);
        if (value->getType() == SFNODE) {
            Node *child = ((SFNode *) value)->getValue();
            if (child) {
                glPushName(i);
                glPushName(0);
                drawHandlesRec(child, drawOnlyJoints);
                glPopName();
                glPopName();
            }
        } else if (value->getType() == MFNODE) {
            glPushName(i);
            glPushName(0);
            MFNode  *v = (MFNode *) value;
            int n = v->getSize();
            for (int j = 0; j < n; j++) {
                glLoadName(j);
                drawHandlesRec(v->getValue(j), drawOnlyJoints);
            }
            glPopName();
            glPopName();
        }
    }
    glPopMatrix();
}

void
Scene::enableHeadlight()
{
    GLenum light = (GLenum) allocateLight();
    static float pos[4] = {0.0f, 0.0f, 1.0f, 0.0f};
    static float ambientColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
    static float diffuseColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};

    glLightfv(light, GL_AMBIENT, ambientColor);
    glLightfv(light, GL_DIFFUSE, diffuseColor);
    glLightfv(light, GL_POSITION, pos);
    glLightfv(light, GL_SPECULAR, diffuseColor);
    glLightf(light, GL_SPOT_CUTOFF, 180.0f);
    glLightf(light, GL_SPOT_EXPONENT, 0.0f);
    glLightf(light, GL_CONSTANT_ATTENUATION, 1.0f);
    glLightf(light, GL_LINEAR_ATTENUATION, 0.0f);
    glLightf(light, GL_QUADRATIC_ATTENUATION, 0.0f);
    glEnable(light);
}

// allocateLight()
//
// reserve an openGL light

int
Scene::allocateLight()
{
    GLint maxLights;
    glGetIntegerv(GL_MAX_LIGHTS, &maxLights);

    if (_numLights >= maxLights) {
        errorf("too many lights!\n");
        return GL_LIGHT0;
    }
    
    return (GL_LIGHT0 + _numLights++);
}

int
Scene::freeLight()
{
    return GL_LIGHT0 + --_numLights;
}

void
Scene::projectPoint(float x, float y, float z, float *wx, float *wy, float *wz)
{
    GLdouble mmat[16], pmat[16];
    GLdouble winx, winy, winz;
    GLint viewport[4];

    glGetDoublev(GL_MODELVIEW_MATRIX, mmat);
    glGetDoublev(GL_PROJECTION_MATRIX, pmat);
    glGetIntegerv(GL_VIEWPORT, viewport);

    gluProject(x, y, z, mmat, pmat, viewport, &winx, &winy, &winz);

    *wx = (float) winx;
    *wy = (float) winy;
    *wz = (float) winz;
}

void
Scene::unProjectPoint(float wx, float wy, float wz, float *x, float *y, float *z)
{
    GLdouble mmat[16], pmat[16];
    GLdouble objx, objy, objz;
    GLint viewport[4];

    glGetDoublev(GL_MODELVIEW_MATRIX, mmat);
    glGetDoublev(GL_PROJECTION_MATRIX, pmat);
    glGetIntegerv(GL_VIEWPORT, viewport);

    gluUnProject(wx, wy, wz, mmat, pmat, viewport, &objx, &objy, &objz);

    *x = (float) objx;
    *y = (float) objy;
    *z = (float) objz;
}

void
Scene::addViewpoint(Node *viewpoint)
{
    _viewpoints.append(viewpoint);
}

void
Scene::addNavigationInfo(Node *navigationInfo)
{
    _navigationinfos.append(navigationInfo);
}

void
Scene::addFog(Node *fog)
{
    _fogs.append(fog);
}

void
Scene::addBackground(Node *background)
{
    _backgrounds.append(background);
}

void
Scene::addTimeSensor(Node *timeSensor)
{
    _timeSensors.append(timeSensor);
}

void
Scene::startWalking()
{
    _oldWalkTime = swGetCurrentTime();
}

void
Scene::walkCamera(Vec3f walk, bool forward)
{
    Vec3f pos(_currentViewpoint->getPosition());
    Quaternion rot(_currentViewpoint->getOrientation());
    float fspeed = _currentNavigationInfo->speed()->getValue();
    double dt = swGetCurrentTime() - _oldWalkTime;

    if (forward) {
        Quaternion around(Vec3f(0.0f, 1.0f, 0.0f), -DEG2RAD(walk.x * 2.0f));
        Quaternion newRot(around * rot);
        newRot.normalize();
        rot.normalize();
        _currentViewpoint->setOrientation(newRot);
        float z = dt * walk.z * 2.0f;
        _currentViewpoint->setPosition(pos + rot * fspeed * Vec3f(0, 0, z));
    } else {
         Vec3f vec(dt * fspeed * walk.x * 2.0f, dt * fspeed * walk.y * 2.0f, 0);
        _currentViewpoint->setPosition(pos + rot * vec);
    }
}

void               
Scene::changeTurnPointDistance(float factor)
{
    if (_currentNavigationInfo) {
        MFFloat *mfAvatarSize = _currentNavigationInfo->avatarSize();
        float fdist = _currentNavigationInfo->speed()->getValue() * 10;
        if (mfAvatarSize->getSize() > 3)
            fdist = mfAvatarSize->getValue(3) * factor;
        MFFloat *newAvatarSize = new MFFloat();
        for (int i = 0; i < mfAvatarSize->getSize(); i++)
            newAvatarSize->setValue(i, mfAvatarSize->getValue(i));
        float values[] = { 0.25f, 1.6f, 0.75f };
        if (newAvatarSize->getSize() < 3)
            for (int i = newAvatarSize->getSize() - 1; i < 3; i++)
                 newAvatarSize->setValue(i, values[i]);
        newAvatarSize->setValue(3, fdist);
        setField(_currentNavigationInfo, 
                 _currentNavigationInfo->avatarSize_Field(), newAvatarSize);
        TheApp->PrintMessageWindowsFloat(IDS_TURN_POINT_DISTANCE, fdist);
    }    
}

void
Scene::moveCamera(float dx, float dy, float dz)
{
    Vec3f pos = _currentViewpoint->getPosition();
    Quaternion rot = _currentViewpoint->getOrientation();

    float fspeed = _currentNavigationInfo->speed()->getValue();
    _currentViewpoint->setPosition(pos + rot * fspeed * Vec3f(dx, dy, dz));
}

void
Scene::turnCamera(float x, float y, float z, float ang)
{
    Quaternion rot = _currentViewpoint->getOrientation();
    Quaternion r(Vec3f(x, y, z), ang);

    _currentViewpoint->setOrientation(r * rot);
}

Quaternion oldRot;

void
Scene::setTurnPoint(void)
{    
    _turnPointPos = _currentViewpoint->getPosition();
    _turnPointRot = _currentViewpoint->getOrientation();
}

void
Scene::orbitCamera(float dtheta, float dphi)
{    
    Vec3f pos(_currentViewpoint->getPosition());
    Quaternion rot(_currentViewpoint->getOrientation());
    Quaternion up(Vec3f(0.0f, 1.0f, 0.0f), dtheta);
    Quaternion around(Vec3f(1.0f, 0.0f, 0.0f), dphi);
    Quaternion newRot(up * around * rot);

    Vec3f zAxis(0, 0, 10);
    Vec3f dist(_turnPointRot * zAxis);
    dist.normalize();
    float fdist = 10;
    MFFloat *mfAvatarSize = _currentNavigationInfo->avatarSize();
    if (mfAvatarSize->getSize() > 3)
        fdist = mfAvatarSize->getValue(3);
    else
        fdist =  _currentNavigationInfo->speed()->getValue() * 10;

    dist = dist * fdist;

    Vec3f newPos(_turnPointPos - dist + dist * _turnPointRot.conj() * newRot);

    if (TheApp->GetMouseMode() == MOUSE_EXAMINE)
        _currentViewpoint->setPosition(newPos);
    _currentViewpoint->setOrientation(newRot);

}

void
Scene::rollCamera(float dtheta)
{    
    Vec3f pos(_currentViewpoint->getPosition());
    Quaternion rot(_currentViewpoint->getOrientation());
    Quaternion roll(Vec3f(0.0f, 0.0f, -1.0f), dtheta);
    Quaternion newRot(roll * rot);
    newRot.normalize();
    rot.normalize();

    Vec3f newPos(rot.conj() * pos * newRot);

    if (TheApp->GetMouseMode() == MOUSE_EXAMINE) {
        _currentViewpoint->setPosition(newPos);
    }
    _currentViewpoint->setOrientation(newRot);
    applyCamera();
}

void
Scene::standUpCamera(void)
{
    Quaternion rot(_currentViewpoint->getOrientation());
    rot.x = 0;
    rot.z = 0;
    _currentViewpoint->setOrientation(rot);
    UpdateViews(NULL, UPDATE_PREVIEW);
}

Node *
Scene::createNode(const char *nodeType)
{
    Proto *def = NULL;
    if (isX3d() && strcmp(nodeType, "NurbsPatchSurface") == 0)
        def = _protos["NurbsSurface"];
    else
        def = _protos[nodeType];

    return def ? def->create(this) : (Node *) NULL;
}


Node *
Scene::createNode(int nodeType)
{
    ProtoMap::Chain::Iterator *j;
    // search for proto with correct nodeType
    for (int i = 0; i < _protos.width(); i++) {
        for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) {
            if (j->item()->getData()->getType() == nodeType)
                return createNode(j->item()->getKey());
        }
    }
    return (Node *) NULL;
}

void
Scene::addNode(Node *node)
{
    _nodes.append(node);
    
}

MyString
Scene::getUniqueNodeName(const char *name)
{
    static char buf[512];
    for (int i = 1; ; i++) {
        mysnprintf(buf, 512, "%s%d", name, i);
        if (!hasAlreadyName(buf)) break;
    }
    MyString ret = "";
    ret += buf;
    return ret;
}

MyString
Scene::getUniqueNodeName(Node *node)
{
    const char *name = node->getProto()->getName(isX3d());

    return getUniqueNodeName(name);
}

MyString
Scene::generateUniqueNodeName(Node *node)
{
    MyString name = mystrdup(getUniqueNodeName(node));
    def(name, node);
    return name;
}

MyString Scene::generateVariableName(Node *node)
{
     MyString ret = "";
     ret += node->getProto()->getName(isX3d());
     _variableCounter++;
     char number[1024];
     mysnprintf(number, 1023, "%d", _variableCounter);
     ret += "_";
     ret += number;
     return ret;
}

void
Scene::removeNode(Node *node)
{
    int index = _nodes.findBackward(node);
    if ((index >= 0) && (index < _nodes.size()))
        _nodes.remove(index);
}

void
Scene::setSelection(const Path *path)
{
    _selection_is_in_scene = true;
    _viewOfLastSelection = NULL;
    if (_selection != path) {
        delete _selection;
        _selection = path;
        Node *node = _selection->getNode();
        if (node == NULL) {
            setSelection(getRoot());
            return;
        } 
        if (node->getType() == VRML_VIEWPOINT) {
            _currentViewpoint = (NodeViewpoint *)node;
//            applyCamera();
        }
        if (node->getType() == VRML_NAVIGATION_INFO) {
            _currentNavigationInfo = (NodeNavigationInfo *)node;
            applyNavigationInfo();
        }
    }
}

Path*
Scene::newPath(Node *node)
{
    Path* ret;
    int len = 0;
    Node *n;

    for (n = node; n->hasParent() ; n = n->getParent()) {
        len += 2;
    }

    if (len > 0) {
        int *list = new int[len];

        int i = len-1;
    
        for (n = node; n->hasParent(); n = n->getParent()) {
            Node *parent = n->getParent();

            if (parent->isInsideProto())
                return new Path(NULL, 0, this);

            int field = n->getParentField();
            list[i--] = parent->findChild(n, field);
            list[i--] = field;
        }

        ret=new Path(list, len, this);
        delete [] list;
    } else {
        // select root node
        ret=new Path(NULL, 0, this);
    }
    return ret;
}

void
Scene::setSelection(Node *node)
{
    setSelection(new Path(*(node->getPath())));
}

bool
Scene::isModified() const
{
    if (_undoStack.empty()) {
        if (_unmodified != NULL)
            return TRUE;
        else
            return _extraModifiedFlag;
    } else {
        return _unmodified != _undoStack.peek();
    }
}

void
Scene::applyCamera()
{
    _currentViewpoint->apply();
}

void
Scene::setCamera(NodeViewpoint *camera)
{
    _currentViewpoint = camera;
    applyCamera();
}

void
Scene::applyNavigationInfo(void)
{
    _currentNavigationInfo->apply();
}

void
Scene::setFirstNavigationInfo(void)
{
    if (!_setNavigationInfo) {
        _setNavigationInfo = true;
        if (_navigationinfos.size() > 0) {
            _currentNavigationInfo = (NodeNavigationInfo *) _navigationinfos[0];
        } else {
            _currentNavigationInfo = _defaultNavigationInfo;
        }
    }
    applyNavigationInfo();
}

void
Scene::start()
{
    _running = true;
    if (!_setViewpoint) {
        _setViewpoint = true;
        if (_viewpoints.size() > 0) {
            _currentViewpoint = (NodeViewpoint *) _viewpoints[0];
        } else {
            _currentViewpoint = _defaultViewpoint;
        }
    }

    double t = swGetCurrentTime();
    updateTimeAt(t);
}

void
Scene::stop()
{
    _running = false;
}

double
Scene::timeSinceStart(void)
{
    return swGetCurrentTime() - _timeStart;
}

void
Scene::updateTimeAt(double t)
{
    for (int i = 0; i < _timeSensors.size(); i++)
        ((NodeTimeSensor *) _timeSensors[i])->setTime(t);
}

void
Scene::updateTime()
{
    double t = swGetCurrentTime();
    updateTimeAt(t);
    UpdateViews(NULL, UPDATE_TIME);
}

NodeViewpoint *
Scene::getCamera() const
{
    return _currentViewpoint;
}

NodeNavigationInfo *
Scene::getNavigationInfo() const
{
    return _currentNavigationInfo;
}

void
Scene::AddView(SceneView *view)
{
    _views.append(view);
}

void
Scene::RemoveView(SceneView *view)
{
    _views.remove(_views.find(view));
}

void
Scene::OnFieldChange(Node *node, int field, int index)
{
    FieldUpdate hint(node, field, index);

    UpdateViews(NULL, UPDATE_FIELD, (Hint *) &hint);
}

void
Scene::OnAddNode(Node *node, Node *dest, int field)
{
    NodeUpdate hint(node, dest, field);

    UpdateViews(NULL, UPDATE_ADD_NODE, (Hint *) &hint);
}

void
Scene::OnAddNodeSceneGraph(Node *node, Node *dest, int field)
{
    NodeUpdate hint(node, dest, field);

    UpdateViews(NULL, UPDATE_ADD_NODE_SCENE_GRAPH_VIEW, (Hint *) &hint);
}

void
Scene::OnRemoveNode(Node *node, Node *src, int field)
{
    NodeUpdate hint(node, src, field);
    // a deleted DEF'ed node can not be USE'd
    if (node == _defNode)
        _defNode = NULL;
    UpdateViews(NULL, UPDATE_REMOVE_NODE, (Hint *) &hint);
}

void
Scene::UpdateViews(SceneView *sender, int type, Hint *hint)
{
    static bool alreadyInUpdate = false;
    if (alreadyInUpdate) // forbid recursive update chains
        return;
    if (!_canUpdateViewsSelection)
        if (type == UPDATE_SELECTION)
            return;
    alreadyInUpdate = true;
    for (List<SceneView *>::Iterator *i = _views.first(); i != NULL; 
         i = i->next()) {
        SceneView *view = i->item();
        if (view != sender)
            view->OnUpdate(sender, type, hint);
    }
    alreadyInUpdate = false;
}

void
BackupRoutesRec(Node *node, CommandList *list)
{
    int                         i;
    SocketList::Iterator       *j;

    if (!node) return;

    if (node->getNumParents() > 1) return;

    for (i = 0; i < node->getProto()->getNumEventIns(); i++) {
        for (j = node->getInput(i).first(); j != NULL; j = j->next()) {
            const RouteSocket &s = j->item();
            list->append(new UnRouteCommand(s.getNode(), s.getField(), 
                                            node, i));
        }
    }
    for (i = 0; i < node->getProto()->getNumEventOuts(); i++) {
        for (j = node->getOutput(i).first(); j != NULL; j = j->next()) {
            const RouteSocket &s = j->item();
            list->append(new UnRouteCommand(node, i, 
                                            s.getNode(), s.getField()));
        }
    }

    for (i = 0; i < node->getProto()->getNumFields(); i++) {
        FieldValue  *v = node->getField(i);
        if (v->getType() == SFNODE) {
            BackupRoutesRec(((SFNode *) v)->getValue(), list);
        } else if (v->getType() == MFNODE) {
            int size = ((MFNode *) v)->getSize();
            for (int k = 0; k < size; k++) {
                BackupRoutesRec(((MFNode *) v)->getValue(k), list);
            }
        }
    }
}

void 
Scene::selectNext()
{
    Node *node = _selection->getNode();
    int newIndex = node->getNextSiblingIndex();
    if (newIndex == -1)
        newIndex = node->getPrevSiblingIndex();
    Node *parent = _selection->getParent();
    int parentField = _selection->getParentField();
    Node *newSelection = parent;
    if (newIndex != -1) {
        MFNode *mfnode = (MFNode *)parent->getField(parentField);
        newSelection = mfnode->getValue(newIndex);
    }
    setSelection(newSelection);
}

void
Scene::DeleteSelected()
{
    Node *parent = _selection->getParent();
    int parentField = _selection->getParentField();
    if ((parent != NULL) && (parentField != -1)) {
        CommandList *list = new CommandList();
        DeleteSelectedAppend(list);
        selectNext();
        execute(list);
        UpdateViews(NULL, UPDATE_SELECTION);
    }    
}

void
Scene::DeleteSelectedAppend(CommandList* list)
{
    if (_selection && (_selection->getNode() != _root)) {
        Node *node = _selection->getNode();
        Node *parent = _selection->getParent();
        int parentField = _selection->getParentField();

        if ((parent != NULL) && (parentField != -1)) {
            if (node->getNumParents() == 1) {
                BackupRoutesRec(node, list);
            }
            list->append(new MoveCommand(node, parent, parentField, NULL, -1));
        }
    }
}

int
Scene::OnDragOver(Node *src, Node *srcParent, int src_field, 
                  Node *dest, int dest_field, int modifiers)
{
    int rc = 0;

    if (src && dest) {
        int destField = getDestField(src, dest, dest_field);
        if (destField >= 0) {
            if ((modifiers & SW_CONTROL) && (modifiers & SW_SHIFT)
                && dest != src && !dest->hasAncestor(src)) {
                rc = SW_DRAG_LINK;
            } else if (modifiers & SW_CONTROL) {
                rc = SW_DRAG_COPY;
            } else if (dest != src
                   && !dest->hasAncestor(src)
                   && dest->findChild(src, destField) == -1) {
                rc = SW_DRAG_MOVE;
            }
        }
    }
    return rc;
}

int
Scene::OnDrop(Node *src, Node *srcParent, int srcField, 
              Node *dest, int destField, int modifiers)
{
    int effect = OnDragOver(src, srcParent, srcField, dest, destField, modifiers);
    NodeUpdate *hint = NULL;
    if (src && dest) {
        if (destField == -1) destField = dest->findValidField(src);
        switch(effect) {
          case SW_DRAG_COPY:
            execute(new MoveCommand(src->copy(), NULL, -1, dest, destField));
            src->reInit();
            break;
          case SW_DRAG_MOVE:
            execute(new MoveCommand(src, srcParent, srcField, dest, destField));
            break;
          case SW_DRAG_LINK:
            execute(new MoveCommand(src, NULL, -1, dest, destField));
            hint = new NodeUpdate(src, NULL, 0);
            UpdateViews(NULL, UPDATE_NODE_NAME, (Hint*) hint);
            break;
        }
        return 1;
    } else {
        return 0;
    }
}

bool
Scene::Download(const URL &url, MyString *path)
{
    *path = url.ToPath();
    return true;
}

FontInfo *
Scene::LoadGLFont(const char *fontName, const char *style)
{
    int i;
    int styleId;

    // handle "special" font names
    if (!strcmp(fontName, "SERIF")) {
        fontName = "Times New Roman";
    } else if (!strcmp(fontName, "SANS")) {
        fontName = "Arial";
    } else if (!strcmp(fontName, "TYPEWRITER")) {
        fontName = "Courier New";
    }
    
    if (!strcmp(style, "BOLD")) {
        styleId = SW_BOLD;
    } else if (!strcmp(style, "ITALIC")) {
        styleId = SW_ITALIC;
    } else if (!strcmp(style, "BOLDITALIC")) {
        styleId = SW_BOLD | SW_ITALIC;
    } else {
        styleId = SW_PLAIN;
    }

    // look for font in cache

    for (i = 0; i < _fonts.size(); i++) {
        if (!strcmp(_fonts[i]->name, fontName) && _fonts[i]->style == styleId) {
            return _fonts[i];
        }
    }

    // create some font outlines

    FontInfo        *info = new FontInfo();

    info->displayListBase = 
          swLoadGLFont(fontName, styleId, info->kernX, info->kernY);

    if (info->displayListBase == 0) {
        delete info;
        return NULL;
    } else {
        info->name = fontName;
        info->style = styleId;
        _fonts.append(info);
        return info;
    }
}

bool                
Scene::addProtoName(MyString name)
{
   for (int i=0;i<_numProtoNames;i++)
       if (name == _protoNames[i]) {
           if (belongsToNodeWithExternProto(name))           
               return true;
           if (_protos[_protoNames[i]]->isExternProto())
               return true;  
           return false;
       }
   _protoNames[_numProtoNames++] = name;
   return true;
}

void
Scene::deleteProtoName(MyString name)
{
   for (int i=0;i<_numProtoNames;i++)
       if (name == _protoNames[i])
           _protoNames.remove(i);
}

void
Scene::addProtoDefinition(void)
{
   _protoDefinitions[_numProtoDefinitions++] = "";
   _isNestedProto[_numProtoDefinitions - 1] = false;
}

void 
Scene::addToProtoDefinition(char* string)
{
   if (_numProtoDefinitions > 0)
       _protoDefinitions[_numProtoDefinitions - 1] += string;
}

void
Scene::setNestedProto(void)
{
    if (_numProtoDefinitions > 0)
        _isNestedProto[_numProtoDefinitions - 1] = true;
}

MyString            
Scene::createRouteString(const char *fromNodeName, const char *fromFieldName,
                         const char *toNodeName, const char *toFieldName)
{
    MyString route = "";
    if (::isX3dXml(_writeFlags)) {
        route += "<ROUTE fromNode='";
        route += fromNodeName;
        route += "' fromField='";
        route += fromFieldName;
        route += "' toNode='";
        route += toNodeName;
        route += "' toField='";
        route += toFieldName;
        route += "'/>";
    } else {
        route += "ROUTE ";
        route += fromNodeName;
        route += ".";
        route += fromFieldName;
        route += " TO ";
        route += toNodeName;
        route += ".";
        route += toFieldName;
    }
    return route;
}


void 
Scene::addRouteString(MyString string)
{
   // do not store double route strings
   for (List<MyString>::Iterator* routepointer = _routeList.first();
        routepointer != NULL; routepointer = routepointer->next() )
       if (strcmp(routepointer->item(),string) == 0)
           return;
      
   _routeList.append(string);
}

void 
Scene::addEndRouteString(MyString string)
{
   _endRouteList.append(string);
}

void                
Scene::setViewOfLastSelection(SceneView* view)
{ 
   _viewOfLastSelection=view; 
   _selection_is_in_scene=false;
}

SceneView* Scene::getViewOfLastSelection(void)
{ 
   return _viewOfLastSelection; 
}

void Scene::DeleteLastSelection(void)
{
   if (_selection_is_in_scene) {
      // do delete in scene 
      DeleteSelected();      
   } else {
      // do delete in View
      SceneView* view = getViewOfLastSelection();
      if (view != NULL)
          view->DeleteLastSelection();
   }
}

/* update URLs in Nodes of scene to new url */

void Scene::updateURLs(Node* node)
{
    if (node==NULL) return;
    for (int i=0;i<node->getProto()->getNumFields();i++) {
        Field *field = node->getProto()->getField(i);
        FieldValue *value = node->getField(i);
        if ((field->getFlags() & FF_URL) && 
            (!TheApp->GetKeepURLs())) {
             if (value && 
                 !value->equals(field->getDefault(isX3d()))) {
                FieldValue *newValue;
                newValue = rewriteField(value, 
                                        TheApp->getImportURL(),
                                        getURL());
                node->setField(i, newValue);
            }
        } 
    }
}

void Scene::saveProtoStatus(void)
{
   _statusNumProtoNames=_numProtoNames;
   _statusNumProtoDefinitions=_numProtoDefinitions;
}

void Scene::restoreProtoStatus(void)
{
   int i;
   for (i=_statusNumProtoNames+1;i<_numProtoNames;i++)
       _protoNames.remove(i);
   _numProtoNames=_statusNumProtoNames;
   for (i=_statusNumProtoDefinitions+1;i<_numProtoDefinitions;i++)
       _protoDefinitions.remove(i);
   _numProtoDefinitions=_statusNumProtoDefinitions;
}

StringArray *
Scene::getAllNodeNames(void)
{
    StringArray *ret = new StringArray();
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++) {
        for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) {
            ret->append(j->item()->getKey());
        }
    }
    return ret;
}

bool
Scene::use3dCursor(void) 
{ 
    switch (TheApp->Get3dCursorMode()) {
      case CM_3DCURSOR_ALWAYS:
         return _use3dCursor;
      case CM_3DCURSOR_RECORDING:
        if (isRecording())
            return _use3dCursor; 
        break;
      case CM_3DCURSOR_NOT_RUN:
        if (!isRunning())
            return _use3dCursor; 
        break;
    }
    return false;            
}

// the proto PREFIX is also needed for the illegal2vrml program
// undefined nodes named "something" are renamed to "PREFIXsomething"
// when the proto "PREFIXsomething" is defined

bool
Scene::setPrefix(char* prefix) 
{ 
    if (prefix != NULL)
       TheApp->setPrefix(prefix); 
    else {
       // compare all protonames to find out a common prefix
       bool prefixFound = false;
       if (getNumProtoNames() > 1) {
           MyString prefix = "";
           for (int numChar = 0; numChar < _protoNames[0].length(); 
               numChar++) {
               char character = _protoNames[0][numChar];
               bool sameCharacter = true; 
               for (int i = 1; i < getNumProtoNames(); i++) {
                   if ((numChar >= _protoNames[i].length()) ||
                       (_protoNames[i][numChar] != character)) {
                       sameCharacter = false;
                       break;
                   }
               }
               if (sameCharacter) {
                   prefixFound = true;
                   prefix += character;
               } else 
                   break;
           }
           if (prefixFound) 
               TheApp->setPrefix(mystrdup(prefix));
       } 
       if (!prefixFound) { 
           errorf("can not find out prefix from only one node\n");
           errorf("prefix missing, use \"-prefix\" in commandline\n");
           return false;
       }
    }
    return true;
}

MyString
Scene::getNodeWithPrefix(const MyString &nodeType)
{
    MyString newNodeType = "";
    newNodeType += TheApp->getPrefix();
    newNodeType += nodeType;
    return newNodeType;
}

void
Scene::setPathAllURL(const char *path)
{
    const NodeList *nodes = getNodes();
    for (int i = 0; i < nodes->size(); i++) {
        Node    *node = nodes->get(i);
        if (node->isInScene(this))
            for (int j = 0; j < node->getProto()->getNumFields(); j++) {
                Field *field = node->getProto()->getField(j);
                if ((field->getType() == MFSTRING) &&
                    ((field->getFlags() & FF_URL) != 0)) {
                    MFString* urls = (MFString *)node->getField(j);
                    for (int k = 0; k < urls->getSize(); k++) {
                        const char *urlk =  urls->getValue(k);
                        if (!isSortOfEcmascript(urlk) && notURN(urlk)) {
                            URL url(getURL(), urlk);
                            MyString *newURL = new MyString("");
                            if (strlen(path) != 0) {
                                *newURL += path;
                                *newURL += "/";
                            }
                            *newURL += url.GetFileName();
                            urls->setValue(k, *newURL);
                        }
                    }
                } 
            }
    }
}

Node *
Scene::replaceNode(Node *oldNode, Node* newNode)
{
    if (newNode == NULL)
        return NULL;
    int numParents = oldNode->getNumParents();
    for (int i = 0; i <= numParents; i++) {
        bool isFirstUse = oldNode->isFirstUSE();
        Node *parent = ((NodeData *)oldNode)->getParent(i);
        int field = ((NodeData *)oldNode)->getParentField(i);
        if ((parent != NULL) && (field != -1)) {
            execute(new MoveCommand(oldNode, parent, field, NULL, -1));
            execute(new MoveCommand(newNode, NULL, -1, parent, field));
        }
    }
    if (numParents == 0)
        return NULL;
    return newNode;
}

Node *
Scene::convertToIndexedFaceSet(Node* node)
{
    if (!node->canConvertToIndexedFaceSet())
        return NULL;

    NodeIndexedFaceSet *meshNode = (NodeIndexedFaceSet *)
                                   node->toIndexedFaceSet();

    return replaceNode(node, meshNode);
}

int
Scene::writeIndexedFaceSet(int f, Node* node)
{
    NodeIndexedFaceSet *meshNode = (NodeIndexedFaceSet *)
                                   node->toIndexedFaceSet();

    meshNode->setVariableName(node->getVariableName());
    int ret = meshNode->write(f, _writeFlags);
    meshNode->unref();
    return ret;
}

Node *
Scene::convertToIndexedLineSet(Node* node)
{
    if (!node->canConvertToIndexedLineSet())
        return NULL;

    NodeIndexedLineSet *chainNode = (NodeIndexedLineSet *)
                                    node->toIndexedLineSet();
    return replaceNode(node, chainNode);
}

void
Scene::recalibrate(void)
{
    TheApp->calibrateInputDevices();
    if (_viewpoints.size() > 0) {
        _currentViewpoint = (NodeViewpoint *) _viewpoints[0];
    } else {
        _currentViewpoint = _defaultViewpoint;
    }    
    applyCamera();
    UpdateViews(NULL, UPDATE_TIME);
}

void 
Scene::makeSimilarName(Node *node, const char *name)
{
    int i = 0;
    char* reducedName = mystrdup(name);
    // strip _ number (e.g. _0 or _12) constructs at end
    for (i = strlen(name) - 1;i > 0; i--)
        if ((name[i] == '_') || ((name[i] >= '0') && (name[i] <= '9'))) {
            if (name[i] == '_')
                reducedName[i] = 0;
        } else
            break;
    int len = strlen(name) + 512;
    char* buf = (char*) malloc(len);
    while (true) {
        mysnprintf(buf, len, "%s_%d", reducedName, i++);
        if (!hasAlreadyName(buf)) {
            MyString* newName = new MyString(buf);
            def(*newName, node);
            break;
        }
    }
    free(buf);
    free(reducedName);
}

ProtoArray *
Scene::getInteractiveProtos(int type)
{
    switch(type)
      {
        case SFBOOL:
          return &_interactiveSFBoolProtos;
        case SFROTATION:
          return &_interactiveSFRotationProtos;
        case SFTIME:
          return &_interactiveSFTimeProtos;
        case SFVEC3F:
          return &_interactiveSFVec3fProtos;
      }
    return &_emptyProtoArray;
}        

void
Scene::buildInteractiveProtos(void)
{
    int i = 0;
    _interactiveSFBoolProtos[i++] = _protos["CylinderSensor"];   
    _interactiveSFBoolProtos[i++] = _protos["PlaneSensor"];   
    _interactiveSFBoolProtos[i++] = _protos["ProximitySensor"];   
    _interactiveSFBoolProtos[i++] = _protos["SphereSensor"];   
    _interactiveSFBoolProtos[i++] = _protos["TouchSensor"];   
    _interactiveSFBoolProtos[i++] = _protos["VisibilitySensor"];   
    i = 0;
    _interactiveSFRotationProtos[i++] = _protos["CylinderSensor"];
    _interactiveSFRotationProtos[i++] = _protos["ProximitySensor"];
    _interactiveSFRotationProtos[i++] = _protos["SphereSensor"];
    if (TheApp->getCoverMode()) {
        _interactiveSFRotationProtos[i++] = _protos["COVER"];
        _interactiveSFRotationProtos[i++] = _protos["SpaceSensor"];
        _interactiveSFRotationProtos[i++] = _protos["ARSensor"];
    }
    i = 0;
    _interactiveSFTimeProtos[i++] = _protos["Collision"];
    _interactiveSFTimeProtos[i++] = _protos["ProximitySensor"];
    _interactiveSFTimeProtos[i++] = _protos["TouchSensor"];
    _interactiveSFTimeProtos[i++] = _protos["VisibilitySensor"];
    i = 0;
    _interactiveSFVec3fProtos[i++] = _protos["CylinderSensor"];
    _interactiveSFVec3fProtos[i++] = _protos["PlaneSensor"];
    _interactiveSFVec3fProtos[i++] = _protos["ProximitySensor"];
    _interactiveSFVec3fProtos[i++] = _protos["SphereSensor"];
    _interactiveSFVec3fProtos[i++] = _protos["TouchSensor"];
    if (TheApp->getCoverMode()) {
        _interactiveSFVec3fProtos[i++] = _protos["COVER"];
        _interactiveSFVec3fProtos[i++] = _protos["SpaceSensor"];
        _interactiveSFVec3fProtos[i++] = _protos["ARSensor"];
    }
}

void
Scene::setHeadlight(int headlight)
{ 
    // only use headlight of first NavigationInfo
    if (_headlightIsSet == false) {
        _headlight = headlight;
        _headlightIsSet = true;
    }
}

int
Scene::getConstrain(void)
{
    int ret = CONSTRAIN_NONE;
    if (!_xOnly && !_yOnly && !_zOnly)
        ret = CONSTRAIN_NONE; 
    else if (_xOnly && !_yOnly && !_zOnly)
        ret = CONSTRAIN_X; 
    else if (!_xOnly && _yOnly && !_zOnly)
        ret = CONSTRAIN_Y; 
    else if (!_xOnly && !_yOnly && _zOnly)
        ret = CONSTRAIN_Z; 
    else if (_xOnly && _yOnly && !_zOnly)
        ret = CONSTRAIN_XY; 
    else if (_xOnly && !_yOnly && _zOnly)
        ret = CONSTRAIN_ZX; 
    else if (!_xOnly && _yOnly && _zOnly)
        ret = CONSTRAIN_YZ; 
    return ret;
}

Vec3f 
Scene::constrainVec(Vec3f vec) 
{
    Vec3f v(vec);    
    if ((!_xOnly) && (_yOnly || _zOnly))
        v[0] = 0;
    if ((!_yOnly) && (_xOnly || _zOnly))
        v[1]=0;
    if ((!_zOnly) && (_xOnly || _yOnly))
        v[2]=0;
    return v;
}

void
Scene::setX3d(void)
{
    getNodes()->clearFlag(NODE_FLAG_CONVERTED);
    _writeFlags = _writeFlags & ~(0xFFFF & CONVERT2VRML); 
    _writeFlags |= CONVERT2X3D;
    if (::isX3d(_writeFlags) && (_root != NULL)) {
        _root->convert2X3d();
    }
}

void
Scene::setX3dv(void) 
{
    setX3d();
    _writeFlags |= X3DV; 
}

void
Scene::setX3dXml(void) 
{
    setX3d();
    _writeFlags |= X3D_XML; 
}

void
Scene::setVrml(void) 
{
    getNodes()->clearFlag(NODE_FLAG_CONVERTED);
    _writeFlags = _writeFlags & ~(0xFFFF & CONVERT2X3D); 
    _writeFlags |= CONVERT2VRML;
    if (!::isX3d(_writeFlags) && (_root != NULL))
        _root->convert2Vrml();    
}

void
Scene::convertProtos2X3d(void) {
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++)
        for (j = _protos.chain(i).first(); j != NULL; j = j->next())
            j->item()->getData()->convert2X3d();
}

void
Scene::convertProtos2Vrml(void) {
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++)
        for (j = _protos.chain(i).first(); j != NULL; j = j->next())
            j->item()->getData()->convert2Vrml();
}

bool
Scene::isInvalidElement(Element *element)
{
    bool x3d = isX3d();
    int flags = element->getFlags();
    if (
        ((flags & FF_X3D_ONLY) && (!x3d)) ||
        ((flags & FF_VRML_ONLY) && x3d) ||
        ((flags & FF_KAMBI_ONLY) && (!TheApp->getKambiMode())) ||
        ((flags & FF_COVER_ONLY) && (!TheApp->getCoverMode()))
       )
        return true;
    return false;
}


void
Scene::resetWriteFlags(int flags)
{
    if (::isX3d(_writeFlags) && (!::isX3d(flags)) && (_root != NULL)) {
        _writeFlags = flags & ~(0xFFFF & CONVERT2X3D); 
        _writeFlags |= CONVERT2VRML;
        _root->convert2Vrml();    
    }
    if ((!::isX3d(_writeFlags)) && ::isX3d(flags) && (_root != NULL)) {
        _writeFlags = flags & ~(0xFFFF & CONVERT2VRML); 
        _writeFlags |= CONVERT2X3D;
        _root->convert2X3d();    
    }
    _writeFlags = flags & ~(0xFFFF & (CONVERT2VRML | CONVERT2X3D)); 
}

void
Scene::changeRoutes(Node *toNode, int toField, Node *fromNode, int fromField,
                    bool fromX3d)
{
    SocketList::Iterator *i = NULL;
    CommandList *list = NULL;
    Proto *proto = fromNode->getProto();
    Field *field = proto->getField(fromField);
    int eventOut = proto->lookupEventOut(field->getName(fromX3d), fromX3d);
    if (eventOut != -1)
        for (i = fromNode->getOutput(eventOut).first(); i != NULL; 
             i = i->next()) {
            if (list == NULL) 
                list = new CommandList();
            list->append(new UnRouteCommand(i->item().getNode(), 
                                            i->item().getField(), 
                                            fromNode, fromField));
            list->append(new RouteCommand(i->item().getNode(), 
                                          i->item().getField(),
                                          toNode, toField));
        }
    int eventIn = proto->lookupEventIn(field->getName(fromX3d), fromX3d);
    if (eventIn != -1)
        for (i = fromNode->getInput(eventIn).first(); i != NULL; 
             i = i->next()) {
            if (list == NULL) 
                list = new CommandList();
            list->append(new UnRouteCommand(i->item().getNode(), 
                                            i->item().getField(),
                                            fromNode, fromField));
            list->append(new RouteCommand(i->item().getNode(), 
                                          i->item().getField(),
                                          toNode, toField));
        }                                        
    if (list) execute(list);
}

static bool searchBindableNodes(Node *node, void *data)
{
    Scene *scene = (Scene *)data;
    if (node->getType() == VRML_VIEWPOINT)
        scene->addViewpoint(node);
    else if (node->getType() == VRML_NAVIGATION_INFO)
        scene->addNavigationInfo(node);
    else if (node->getType() == VRML_BACKGROUND)
        scene->addBackground(node);
    else if (node->getType() == VRML_FOG)
        scene->addFog(node);
    return true;     
}

static bool convertToTriangleSet(Node *node, void *data)
{
    Scene *scene = (Scene *)data;
    if (node->canConvertToTriangleSet())
        scene->convertToTriangleSet(node);
    return true;     
}

Node *
Scene::convertToTriangleSet(Node* node)
{
    if (!node->canConvertToTriangleSet())
        return NULL;

    NodeTriangleSet *triangleNode = (NodeTriangleSet *)node->toTriangleSet();

    return replaceNode(node, triangleNode);
}

void Scene::branchConvertToTriangleSet(Node *node)
{
    node->doWithBranch(::convertToTriangleSet, this);
}

Node *
Scene::convertToIndexedTriangleSet(Node* node)
{
    if (!node->canConvertToIndexedTriangleSet())
        return NULL;

    NodeIndexedTriangleSet *meshNode = (NodeIndexedTriangleSet *)
                                       node->toIndexedTriangleSet();

    return replaceNode(node, meshNode);
}

static bool convertToIndexedTriangleSet(Node *node, void *data)
{
    Scene *scene = (Scene *)data;
    if (node->canConvertToIndexedTriangleSet())
        scene->convertToIndexedTriangleSet(node);
    return true;     
}

void Scene::branchConvertToIndexedTriangleSet(Node *node)
{
    node->doWithBranch(::convertToIndexedTriangleSet, this);
}

static bool convertToIndexedFaceSet(Node *node, void *data)
{
    Scene *scene = (Scene *)data;
    if (node->canConvertToIndexedFaceSet())
        scene->convertToIndexedFaceSet(node);
    return true;     
}

void Scene::branchConvertToIndexedFaceSet(Node *node)
{
    node->doWithBranch(::convertToIndexedFaceSet, this);
}

void
Scene::findBindableNodes(void)
{
    if (_root != NULL)
        _root->doWithBranch(searchBindableNodes, this);
    setFirstNavigationInfo();
}

static bool createNormals(Node *node, void *data)
{
    int normalField = node->getNormalField();
    if (normalField == -1)
        return true; 
    // ignore Nodes with already set normal field
    if (((SFNode *)node->getField(normalField))->getValue() == NULL) {
        Node *nnormal = node->getScene()->createNode("Normal");
        if (nnormal != NULL) {
            MoveCommand *command = new MoveCommand(nnormal, NULL, -1,
                                                   node, normalField);
            command->execute();
            node->setNormalFromMesh(nnormal);
        }
    }
    return true;     
}

void Scene::branchCreateNormals(Node *node)
{
    node->doWithBranch(createNormals, NULL);
}

void Scene::createNormal(Node *node)
{
    ::createNormals(node, NULL);
}

static bool createTextureCoordinates(Node *node, void *data)
{
    int texCoordField = node->getTexCoordField();
    if (texCoordField == -1)
        return true; 
    // ignore Nodes with already set texCoord field
    if (((SFNode *)node->getField(texCoordField))->getValue() == NULL) {
        Node *ntexCoord = node->getScene()->createNode("TextureCoordinate");
        if (ntexCoord != NULL) {
            MoveCommand *command = new MoveCommand(ntexCoord, NULL, -1,
                                                   node, texCoordField); 
            command->execute();
            node->setTexCoordFromMesh(ntexCoord);
        }
    }
    return true;     
}

void
Scene::branchCreateTextureCoordinates(Node *node) 
{
    node->doWithBranch(createTextureCoordinates, NULL);
}

void 
Scene::createTextureCoordinate(Node *node)
{
    ::createTextureCoordinates(node, NULL);
}

bool
Scene::checkSameOrXSymetricHandle(int handle, MFVec3f *points)
{
    if (handle >= points->getSFSize())
        return true;
    Vec3f vIndex = points->getValue(handle);
    float epsilon = TheApp->GetHandleEpsilon();
    int numPoints = points->getSize() / 3;
    for (int i = 0; i < getSelectedHandlesSize(); i++) {
        int checkedHandle = getSelectedHandle(i);
        if (checkedHandle != handle) {
            Vec3f vPoint = points->getValue(checkedHandle);
            if (   (fabs(vPoint.z - vIndex.z) < epsilon) 
                && (fabs(vPoint.y - vIndex.y) < epsilon)) {
                if (fabs(vPoint.x + vIndex.x) < epsilon) {
                    return true;
                } else if (fabs(vPoint.x - vIndex.x) < epsilon) {
                    return true;
                }         
            }                 
        }
    }
    return false;
}

void
Scene::addMeta(const char *metaKey, const char* metaValue)
{
    _metaKeys.append(metaKey);
    _metaValues.append(metaValue);
}

