Пример #1
0
void PropertyBrowser::applyObjectGroupValue(PropertyId id, const QVariant &val)
{
    ObjectGroup *objectGroup = static_cast<ObjectGroup*>(mObject);
    QUndoCommand *command = 0;

    switch (id) {
    case ColorProperty: {
        QColor color = val.value<QColor>();
        if (color == Qt::gray)
            color = QColor();

        command = new ChangeObjectGroupProperties(mMapDocument,
                                                  objectGroup,
                                                  color,
                                                  objectGroup->drawOrder());
        break;
    }
    case DrawOrderProperty: {
        ObjectGroup::DrawOrder drawOrder = static_cast<ObjectGroup::DrawOrder>(val.toInt());
        command = new ChangeObjectGroupProperties(mMapDocument,
                                                  objectGroup,
                                                  objectGroup->color(),
                                                  drawOrder);
        break;
    }
    default:
        break;
    }

    if (command)
        mMapDocument->undoStack()->push(command);
}
Пример #2
0
void CreateObjectTool::mousePressed(const QPointF &pos,
                                    Qt::MouseButton button,
                                    Qt::KeyboardModifiers modifiers)
{
    // Check if we are already creating a new map object
    if (mNewMapObjectItem) {
        if (button == Qt::RightButton)
            cancelNewMapObject();
        return;
    }

    if (button != Qt::LeftButton)
        return;

    ObjectGroup *objectGroup = currentObjectGroup();
    if (objectGroup && objectGroup->isVisible() && !mNewMapObjectItem) {
        const MapRenderer *renderer = mMapScene->mapDocument()->renderer();

        QPointF tileCoords = renderer->pixelToTileCoords(pos);
        if (modifiers & Qt::ControlModifier)
            tileCoords = tileCoords.toPoint();

        startNewMapObject(tileCoords, objectGroup);
    }
}
Пример #3
0
void TileCollisionDock::tileObjectGroupChanged(Tile *tile)
{
    if (mTile != tile)
        return;
    if (mApplyingChanges)
        return;

    mSynchronizing = true;

    mDummyMapDocument->undoStack()->clear();
    auto selectedTool = mToolManager->selectedTool();

    LayerModel *layerModel = mDummyMapDocument->layerModel();
    delete layerModel->takeLayerAt(nullptr, 1);

    ObjectGroup *objectGroup;
    if (tile->objectGroup())
        objectGroup = tile->objectGroup()->clone();
    else
        objectGroup = new ObjectGroup;

    objectGroup->setDrawOrder(ObjectGroup::IndexOrder);

    layerModel->insertLayer(nullptr, 1, objectGroup);
    mDummyMapDocument->setCurrentLayer(objectGroup);

    mToolManager->selectTool(selectedTool);

    mSynchronizing = false;
}
Пример #4
0
void MapDocument::resizeMap(const QSize &size, const QPoint &offset, bool removeObjects)
{
    const QRegion movedSelection = mSelectedArea.translated(offset);
    const QRect newArea = QRect(-offset, size);
    const QRectF visibleArea = mRenderer->boundingRect(newArea);

    const QPointF origin = mRenderer->tileToPixelCoords(QPointF());
    const QPointF newOrigin = mRenderer->tileToPixelCoords(-offset);
    const QPointF pixelOffset = origin - newOrigin;

    // Resize the map and each layer
    QUndoCommand *command = new QUndoCommand(tr("Resize Map"));

    LayerIterator iterator(mMap);
    while (Layer *layer = iterator.next()) {
        switch (layer->layerType()) {
        case Layer::TileLayerType: {
            TileLayer *tileLayer = static_cast<TileLayer*>(layer);
            new ResizeTileLayer(this, tileLayer, size, offset, command);
            break;
        }
        case Layer::ObjectGroupType: {
            ObjectGroup *objectGroup = static_cast<ObjectGroup*>(layer);

            for (MapObject *o : objectGroup->objects()) {
                if (removeObjects && !visibleIn(visibleArea, o, mRenderer)) {
                    // Remove objects that will fall outside of the map
                    new RemoveMapObject(this, o, command);
                } else {
                    QPointF oldPos = o->position();
                    QPointF newPos = oldPos + pixelOffset;
                    new MoveMapObject(this, o, newPos, oldPos, command);
                }
            }
            break;
        }
        case Layer::ImageLayerType: {
            // Adjust image layer by changing its offset
            auto imageLayer = static_cast<ImageLayer*>(layer);
            new SetLayerOffset(this, layer,
                               imageLayer->offset() + pixelOffset,
                               command);
            break;
        }
        case Layer::GroupLayerType: {
            // Recursion handled by LayerIterator
            break;
        }
        }
    }

    new ResizeMap(this, size, command);
    new ChangeSelectedArea(this, movedSelection, command);

    mUndoStack->push(command);

    // TODO: Handle layers that don't match the map size correctly
}
Пример #5
0
void
GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
                                                   Handle<GlobalObject*> global,
                                                   JSCompartment* dest)
{
    // After we call LeaveParseTaskZone() it's not safe to GC until we have
    // finished merging the contents of the parse task's compartment into the
    // destination compartment.  Finish any ongoing incremental GC first and
    // assert that no allocation can occur.
    gc::AutoFinishGC finishGC(rt);
    JS::AutoAssertNoAlloc noAlloc(rt);

    LeaveParseTaskZone(rt, parseTask);

    {
        gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);

        // Generator functions don't have Function.prototype as prototype but a
        // different function object, so the IdentifyStandardPrototype trick
        // below won't work.  Just special-case it.
        JSObject* parseTaskStarGenFunctionProto =
            parseTask->exclusiveContextGlobal->as<GlobalObject>().getStarGeneratorFunctionPrototype();

        // Point the prototypes of any objects in the script's compartment to refer
        // to the corresponding prototype in the new compartment. This will briefly
        // create cross compartment pointers, which will be fixed by the
        // MergeCompartments call below.
        for (; !iter.done(); iter.next()) {
            ObjectGroup* group = iter.get<ObjectGroup>();
            TaggedProto proto(group->proto());
            if (!proto.isObject())
                continue;

            JSObject* protoObj = proto.toObject();

            JSObject* newProto;
            if (protoObj == parseTaskStarGenFunctionProto) {
                newProto = global->getStarGeneratorFunctionPrototype();
            } else {
                JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
                if (key == JSProto_Null)
                    continue;

                MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
                           key == JSProto_Function || key == JSProto_RegExp ||
                           key == JSProto_Iterator);

                newProto = GetBuiltinPrototypePure(global, key);
            }

            MOZ_ASSERT(newProto);
            group->setProtoUnchecked(TaggedProto(newProto));
        }
    }

    // Move the parsed script and all its contents into the desired compartment.
    gc::MergeCompartments(parseTask->cx->compartment(), dest);
}
Пример #6
0
void CreateObjectTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
    // Check if we are already creating a new map object
    if (mNewMapObjectItem) {
        switch (mMode) {
        case CreateArea:
            if (event->button() == Qt::RightButton)
                cancelNewMapObject();
            break;
        case CreatePolygon:
        case CreatePolyline:
            if (event->button() == Qt::RightButton) {
                // The polygon needs to have at least three points and a
                // polyline needs at least two.
                int min = mMode == CreatePolygon ? 3 : 2;
                if (mNewMapObjectItem->mapObject()->polygon().size() >= min)
                    finishNewMapObject();
                else
                    cancelNewMapObject();
            } else if (event->button() == Qt::LeftButton) {
                // Assign current overlay polygon to the new object
                QPolygonF polygon = mOverlayPolygonObject->polygon();
                mNewMapObjectItem->setPolygon(polygon);

                // Add a new editable point to the overlay
                polygon.append(polygon.last());
                mOverlayPolygonItem->setPolygon(polygon);
            }
            break;
        }
        return;
    }

    if (event->button() != Qt::LeftButton) {
        AbstractObjectTool::mousePressed(event);
        return;
    }

    ObjectGroup *objectGroup = currentObjectGroup();
    if (!objectGroup || !objectGroup->isVisible())
        return;

    const MapRenderer *renderer = mapDocument()->renderer();
    QPointF tileCoords = renderer->pixelToTileCoords(event->scenePos());

    bool snapToGrid = Preferences::instance()->snapToGrid();
    if (event->modifiers() & Qt::ControlModifier)
        snapToGrid = !snapToGrid;

    if (snapToGrid)
        tileCoords = tileCoords.toPoint();

    startNewMapObject(tileCoords, objectGroup);
}
Пример #7
0
Layer *ObjectGroup::mergedWith(Layer *other) const
{
    Q_ASSERT(canMergeWith(other));

    const ObjectGroup *og = static_cast<ObjectGroup*>(other);

    ObjectGroup *merged = static_cast<ObjectGroup*>(clone());
    for (const MapObject *mapObject : og->objects())
        merged->addObject(mapObject->clone());
    return merged;
}
Пример #8
0
static Ranges computeRanges(const QList<MapObject *> &objects)
{
    Ranges ranges;

    for (MapObject *object : objects) {
        ObjectGroup *group = object->objectGroup();
        auto &set = ranges[group];
        set.insert(group->objects().indexOf(object));
    }

    return ranges;
}
Пример #9
0
void TileCollisionDock::applyChanges()
{
    if (mSynchronizing)
        return;

    ObjectGroup *objectGroup = static_cast<ObjectGroup*>(mDummyMapDocument->map()->layerAt(1));
    ObjectGroup *clonedGroup = objectGroup->clone();

    QUndoStack *undoStack = mTilesetDocument->undoStack();
    mApplyingChanges = true;
    undoStack->push(new ChangeTileObjectGroup(mTilesetDocument, mTile, clonedGroup));
    mApplyingChanges = false;
}
Пример #10
0
void MapDocument::onMapObjectModelRowsInsertedOrRemoved(const QModelIndex &parent,
                                                        int first, int last)
{
    Q_UNUSED(first)

    ObjectGroup *objectGroup = mMapObjectModel->toObjectGroup(parent);
    if (!objectGroup)
        return;

    // Inserting or removing objects changes the index of any that come after
    const int lastIndex = objectGroup->objectCount() - 1;
    if (last < lastIndex)
        emit objectsIndexChanged(objectGroup, last + 1, lastIndex);
}
Пример #11
0
void ObjectSelectionItem::layerChanged(int index)
{
    ObjectGroup *objectGroup = mMapDocument->map()->layerAt(index)->asObjectGroup();
    if (!objectGroup)
        return;

    // If labels for all objects are visible, some labels may need to be added
    // removed based on layer visibility.
    if (Preferences::instance()->objectLabelVisibility() == Preferences::AllObjectLabels)
        addRemoveObjectLabels();

    // If an object layer changed, that means its offset may have changed,
    // which affects the outlines of selected objects on that layer and the
    // positions of any name labels that are shown.
    syncOverlayItems(objectGroup->objects());
}
Пример #12
0
MapObject *CreateObjectTool::clearNewMapObjectItem()
{
    Q_ASSERT(mNewMapObjectItem);

    MapObject *newMapObject = mNewMapObjectItem->mapObject();

    ObjectGroup *objectGroup = newMapObject->objectGroup();
    objectGroup->removeObject(newMapObject);

    delete mNewMapObjectItem;
    mNewMapObjectItem = 0;

    delete mOverlayPolygonItem;
    mOverlayPolygonItem = 0;

    return newMapObject;
}
Пример #13
0
void
gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group)
{
    MOZ_ASSERT(trc->isCallbackTracer());

    // Early return if this group is not required to be in an ObjectGroup chain.
    if (!group->maybeUnboxedLayout())
        return group->traceChildren(trc);

    ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer());
    group->traceChildren(&groupTracer);

    while (!groupTracer.worklist.empty()) {
        ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
        innerGroup->traceChildren(&groupTracer);
    }
}
Пример #14
0
/**
 * Convenience method to copy the current selection to the clipboard.
 * Deals with either tile selection or object selection.
 */
void ClipboardManager::copySelection(const MapDocument *mapDocument)
{
    const Layer *currentLayer = mapDocument->currentLayer();
    if (!currentLayer)
        return;

    const Map *map = mapDocument->map();
    const QRegion &selectedArea = mapDocument->selectedArea();
    const QList<MapObject*> &selectedObjects = mapDocument->selectedObjects();
    const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(currentLayer);
    Layer *copyLayer = nullptr;

    if (!selectedArea.isEmpty() && tileLayer) {
        const QRegion area = selectedArea.intersected(tileLayer->bounds());

        // Copy the selected part of the layer
        copyLayer = tileLayer->copy(area.translated(-tileLayer->position()));
        copyLayer->setPosition(area.boundingRect().topLeft());

    } else if (!selectedObjects.isEmpty()) {
        // Create a new object group with clones of the selected objects
        ObjectGroup *objectGroup = new ObjectGroup;
        for (const MapObject *mapObject : selectedObjects)
            objectGroup->addObject(mapObject->clone());
        copyLayer = objectGroup;
    } else {
        return;
    }

    // Create a temporary map to write to the clipboard
    Map copyMap(map->orientation(),
                0, 0,
                map->tileWidth(), map->tileHeight());

    copyMap.setRenderOrder(map->renderOrder());

    // Resolve the set of tilesets used by this layer
    foreach (const SharedTileset &tileset, copyLayer->usedTilesets())
        copyMap.addTileset(tileset);

    copyMap.addLayer(copyLayer);

    setMap(copyMap);
}
Пример #15
0
QVariant MapToVariantConverter::toVariant(const ObjectGroup &objectGroup) const
{
    QVariantMap objectGroupVariant;
    objectGroupVariant[QLatin1String("type")] = QLatin1String("objectgroup");

    if (objectGroup.color().isValid())
        objectGroupVariant[QLatin1String("color")] = colorToString(objectGroup.color());

    objectGroupVariant[QLatin1String("draworder")] = drawOrderToString(objectGroup.drawOrder());

    addLayerAttributes(objectGroupVariant, objectGroup);
    QVariantList objectVariants;
    for (const MapObject *object : objectGroup.objects())
        objectVariants << toVariant(*object);

    objectGroupVariant[QLatin1String("objects")] = objectVariants;

    return objectGroupVariant;
}
Пример #16
0
QModelIndex MapObjectModel::index(int row, int column,
                                  const QModelIndex &parent) const
{
    if (!parent.isValid()) {
        if (row < mObjectGroups.count())
            return createIndex(row, column, mGroups[mObjectGroups.at(row)]);
        return QModelIndex();
    }

    ObjectGroup *og = toObjectGroup(parent);

    // happens when deleting the last item in a parent
    if (row >= og->objectCount())
        return QModelIndex();

    // Paranoia: sometimes "fake" objects are in use (see createobjecttool)
    if (!mObjects.contains(og->objects().at(row)))
        return QModelIndex();

    return createIndex(row, column, mObjects[og->objects()[row]]);
}
Пример #17
0
void ObjectGroup::addObject(IObject* object)
{
	ObjectGroup* group = dynamic_cast<ObjectGroup*>(object);
	if(group)
	{
		std::vector<Object*>& objectList = group->getObjects();
		for(auto it = objectList.begin(); it != objectList.end(); ++it)		
			addObject(*it);
		
		objectList.clear();
	}
	else
	{
		Object* obj = dynamic_cast<Object*>(object);
		if(obj)
			addObject(obj);
		else
			LOGERROR("Cast fail: Object type should only be Object*");
	}

	
}
Пример #18
0
void ClipboardManager::copySelection(const MapDocument *mapDocument)
{
    const Layer *currentLayer = mapDocument->currentLayer();
    if (!currentLayer)
        return;

    const Map *map = mapDocument->map();
    const QRegion &selectedArea = mapDocument->selectedArea();
    const QList<MapObject*> &selectedObjects = mapDocument->selectedObjects();
    const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(currentLayer);
    Layer *copyLayer = 0;

    if (!selectedArea.isEmpty() && tileLayer) {
        // Copy the selected part of the layer
        copyLayer = tileLayer->copy(selectedArea.translated(-tileLayer->x(),
                                    -tileLayer->y()));
    } else if (!selectedObjects.isEmpty()) {
        // Create a new object group with clones of the selected objects
        ObjectGroup *objectGroup = new ObjectGroup;
        foreach (const MapObject *mapObject, selectedObjects)
            objectGroup->addObject(mapObject->clone());
        copyLayer = objectGroup;
    } else {
Пример #19
0
void MapDocument::moveObjectsUp(const QList<MapObject *> &objects)
{
    if (objects.isEmpty())
        return;

    const auto ranges = computeRanges(objects);

    QScopedPointer<QUndoCommand> command(new QUndoCommand(tr("Move %n Object(s) Up",
                                                             "", objects.size())));

    RangesIterator rangesIterator(ranges);
    while (rangesIterator.hasNext()) {
        rangesIterator.next();

        ObjectGroup *group = rangesIterator.key();
        const RangeSet<int> &rangeSet = rangesIterator.value();

        const RangeSet<int>::Range it_begin = rangeSet.begin();
        RangeSet<int>::Range it = rangeSet.end();
        Q_ASSERT(it != it_begin);

        do {
            --it;

            int from = it.first();
            int count = it.length();
            int to = from + count + 1;

            if (to <= group->objectCount())
                new ChangeMapObjectsOrder(this, group, from, to, count, command.data());

        } while (it != it_begin);
    }

    if (command->childCount() > 0)
        mUndoStack->push(command.take());
}
    /// Add a reference to render an object.
    ///
    /// \param object Object to queue for rendering.
    /// \param transform Object transformation.
    /// \param pass Render pass id.
    /// \param optimistic Can we render in an optimistic manner? (default: true)
    void addObject(const Object &object, const mat4 &transform, unsigned pass = 0, bool optimistic = true,
        const AnimationState *state = NULL)
    {
      ObjectGroup *grp = object.getGroup();      

      ObjectReference &vv = getPass(pass).emplace_back(object, m_screen_transform, m_light_transform,
          transform, state);

      if(grp)
      {
        if(optimistic)
        {
          grp->addObjectReference(m_frame_count, vv);
        }
        else
        {
          vv.setOptimistic(false);
        }
      }
      else
      {
        vv.setOptimistic(true);
      }
    }
Пример #21
0
void test_MapReader::loadMap()
{
    MapReader reader;
    Map *map = reader.readMap("../data/mapobject.tmx");

    // TODO: Also test tilesets (internal and external), properties and tile
    // layer data.

    QVERIFY(map);
    QCOMPARE(map->layerCount(), 2);
    QCOMPARE(map->width(), 100);
    QCOMPARE(map->height(), 80);
    QCOMPARE(map->tileWidth(), 32);
    QCOMPARE(map->tileHeight(), 32);

    TileLayer *tileLayer = dynamic_cast<TileLayer*>(map->layerAt(0));

    QVERIFY(tileLayer);
    QCOMPARE(tileLayer->width(), 100);
    QCOMPARE(tileLayer->height(), 80);

    ObjectGroup *objectGroup = dynamic_cast<ObjectGroup*>(map->layerAt(1));

    QVERIFY(objectGroup);
    QCOMPARE(objectGroup->name(), QLatin1String("Objects"));
    QCOMPARE(objectGroup->objects().count(), 1);

    MapObject *mapObject = objectGroup->objects().at(0);

    QCOMPARE(mapObject->name(), QLatin1String("Some object"));
    QCOMPARE(mapObject->type(), QLatin1String("WARP"));
    QCOMPARE(mapObject->x(), qreal(200));
    QCOMPARE(mapObject->y(), qreal(200));
    QCOMPARE(mapObject->width(), qreal(128));
    QCOMPARE(mapObject->height(), qreal(64));
}
	void TiledMapLoader::loadObjectGroupObjects(Map &map, ObjectGroup &objectGroup, rapidxml::xml_node<> &objectGroupNode) {
		rapidxml::xml_node<> *objectNode = nullptr;
		rapidxml::xml_node<> *objectNature = nullptr;
		objectNode = objectGroupNode.first_node("object");
		while (objectNode) {
			Object::Ptr object(new Object);
			XMLElement objectGroupElement(*objectNode);
			object->setGid(objectGroupElement.getInt("gid", -1));
			object->setId(objectGroupElement.getInt("id", -1));
			object->setName(objectGroupElement.getString("name"));
			object->setType(objectGroupElement.getString("type"));
			object->setX(objectGroupElement.getFloat("x"));
			object->setY(objectGroupElement.getFloat("y"));
			object->setWidth(objectGroupElement.getFloat("width"));
			object->setHeight(objectGroupElement.getFloat("height"));
			object->setRotation(objectGroupElement.getFloat("rotation"));
			object->setVisible(objectGroupElement.getInt("visible", 1));
			objectNature = objectNode->first_node("ellipse");
			if (objectNature) {
				object->setPolygonType("ellipse");
			}
			objectNature = objectNode->first_node("polygon");
			if (objectNature) {
				XMLElement objectNatureElement(*objectNature);

				object->setPolygonType("polygon");
				object->setVertices(objectNatureElement.getString("points"));
			}
			objectNature = objectNode->first_node("polyline");
			if (objectNature) {
				XMLElement objectNatureElement(*objectNature);

				object->setPolygonType("polyline");
				object->setVertices(objectNatureElement.getString("points"));
			}
			object->parseProperties(*objectNode);
			objectGroup.addObject(std::move(object));
			objectNode = objectNode->next_sibling("object");
		}
	}
Пример #23
0
/**
 * Shows the context menu for map objects. The menu allows you to duplicate and
 * remove the map objects, or to edit their properties.
 */
void AbstractObjectTool::showContextMenu(MapObjectItem *clickedObjectItem,
                                         QPoint screenPos)
{
    QSet<MapObjectItem *> selection = mMapScene->selectedObjectItems();
    if (clickedObjectItem && !selection.contains(clickedObjectItem)) {
        selection.clear();
        selection.insert(clickedObjectItem);
        mMapScene->setSelectedObjectItems(selection);
    }
    if (selection.isEmpty())
        return;

    const QList<MapObject*> &selectedObjects = mapDocument()->selectedObjects();
    const QList<ObjectGroup*> objectGroups = mapDocument()->map()->objectGroups();

    QMenu menu;
    QAction *duplicateAction = menu.addAction(tr("Duplicate %n Object(s)", "", selection.size()),
                                              this, SLOT(duplicateObjects()));
    QAction *removeAction = menu.addAction(tr("Remove %n Object(s)", "", selection.size()),
                                           this, SLOT(removeObjects()));

    duplicateAction->setIcon(QIcon(QLatin1String(":/images/16x16/stock-duplicate-16.png")));
    removeAction->setIcon(QIcon(QLatin1String(":/images/16x16/edit-delete.png")));

    menu.addSeparator();
    menu.addAction(tr("Flip Horizontally"), this, SLOT(flipHorizontally()), QKeySequence(tr("X")));
    menu.addAction(tr("Flip Vertically"), this, SLOT(flipVertically()), QKeySequence(tr("Y")));

    ObjectGroup *objectGroup = RaiseLowerHelper::sameObjectGroup(selection);
    if (objectGroup && objectGroup->drawOrder() == ObjectGroup::IndexOrder) {
        menu.addSeparator();
        menu.addAction(tr("Raise Object"), this, SLOT(raise()), QKeySequence(tr("PgUp")));
        menu.addAction(tr("Lower Object"), this, SLOT(lower()), QKeySequence(tr("PgDown")));
        menu.addAction(tr("Raise Object to Top"), this, SLOT(raiseToTop()), QKeySequence(tr("Home")));
        menu.addAction(tr("Lower Object to Bottom"), this, SLOT(lowerToBottom()), QKeySequence(tr("End")));
    }

    if (objectGroups.size() > 1) {
        menu.addSeparator();
        QMenu *moveToLayerMenu = menu.addMenu(tr("Move %n Object(s) to Layer",
                                                 "", selectedObjects.size()));
        for (ObjectGroup *objectGroup : objectGroups) {
            QAction *action = moveToLayerMenu->addAction(objectGroup->name());
            action->setData(QVariant::fromValue(objectGroup));
        }
    }

    menu.addSeparator();
    QIcon propIcon(QLatin1String(":images/16x16/document-properties.png"));
    QAction *propertiesAction = menu.addAction(propIcon,
                                               tr("Object &Properties..."));
    // TODO: Implement editing of properties for multiple objects
    propertiesAction->setEnabled(selectedObjects.size() == 1);

    Utils::setThemeIcon(removeAction, "edit-delete");
    Utils::setThemeIcon(propertiesAction, "document-properties");

    QAction *action = menu.exec(screenPos);
    if (!action)
        return;

    if (action == propertiesAction) {
        MapObject *mapObject = selectedObjects.first();
        mapDocument()->setCurrentObject(mapObject);
        mapDocument()->emitEditCurrentObject();
        return;
    }

    if (ObjectGroup *objectGroup = action->data().value<ObjectGroup*>()) {
        mapDocument()->moveObjectsToGroup(mapDocument()->selectedObjects(),
                                          objectGroup);
    }
}
Пример #24
0
    void Map::ParseText(const string &text) 
    {
        // Create a tiny xml document and use it to parse the text.
        tinyxml2::XMLDocument doc;
        doc.Parse(text.c_str());
    
        // Check for parsing errors.
        if (doc.Error()) 
        {
            has_error = true;
            error_code = TMX_PARSING_ERROR;
            error_text = doc.GetErrorStr1();
            return;
        }

        tinyxml2::XMLNode *mapNode = doc.FirstChildElement("map");
        tinyxml2::XMLElement* mapElem = mapNode->ToElement();

        // Read the map attributes.
        version = mapElem->IntAttribute("version");
        width = mapElem->IntAttribute("width");
        height = mapElem->IntAttribute("height");
        tile_width = mapElem->IntAttribute("tilewidth");
        tile_height = mapElem->IntAttribute("tileheight");
        next_object_id = mapElem->IntAttribute("nextobjectid");

        if (mapElem->Attribute("backgroundcolor"))
        {
            background_color = mapElem->Attribute("backgroundcolor");
        }

        // Read the orientation
        std::string orientationStr = mapElem->Attribute("orientation");

        if (!orientationStr.compare("orthogonal")) 
        {
            orientation = TMX_MO_ORTHOGONAL;
        } 
        else if (!orientationStr.compare("isometric")) 
        {
            orientation = TMX_MO_ISOMETRIC;
        }
        else if (!orientationStr.compare("staggered")) 
        {
            orientation = TMX_MO_STAGGERED;
        }
        

        // Read the render order
        if (mapElem->Attribute("renderorder"))
        {
            std::string renderorderStr = mapElem->Attribute("renderorder");
            if (!renderorderStr.compare("right-down")) 
            {
                render_order = TMX_RIGHT_DOWN;
            } 
            else if (!renderorderStr.compare("right-up")) 
            {
                render_order = TMX_RIGHT_UP;
            }
            else if (!renderorderStr.compare("left-down")) 
            {
                render_order = TMX_LEFT_DOWN;
            }
            else if (!renderorderStr.compare("left-down")) 
            {
                render_order = TMX_LEFT_UP;
            }        
        }

        const tinyxml2::XMLNode *node = mapElem->FirstChild();
        while( node )
        {
            // Read the map properties.
            if( strcmp( node->Value(), "properties" ) == 0 )
            {
                properties.Parse(node);         
            }

            // Iterate through all of the tileset elements.
            if( strcmp( node->Value(), "tileset" ) == 0 )
            {
                // Allocate a new tileset and parse it.
                Tileset *tileset = new Tileset();
                tileset->Parse(node->ToElement());

                // Add the tileset to the list.
                tilesets.push_back(tileset);
            }

            // Iterate through all of the "layer" (tile layer) elements.           
            if( strcmp( node->Value(), "layer" ) == 0 )
            {
                // Allocate a new tile layer and parse it.
                TileLayer *tileLayer = new TileLayer(this);
                tileLayer->Parse(node);

                // Add the tile layer to the lists.
                tile_layers.push_back(tileLayer);
                layers.push_back(tileLayer);
            }

            // Iterate through all of the "imagelayer" (image layer) elements.            
            if( strcmp( node->Value(), "imagelayer" ) == 0 )
            {
                // Allocate a new image layer and parse it.
                ImageLayer *imageLayer = new ImageLayer(this);
                imageLayer->Parse(node);

                // Add the image layer to the lists.
                image_layers.push_back(imageLayer);
                layers.push_back(imageLayer);
            }

            // Iterate through all of the "objectgroup" (object layer) elements.
            if( strcmp( node->Value(), "objectgroup" ) == 0 )
            {
                // Allocate a new object group and parse it.
                ObjectGroup *objectGroup = new ObjectGroup(this);
                objectGroup->Parse(node);
        
                // Add the object group to the lists.
                object_groups.push_back(objectGroup);
                layers.push_back(objectGroup);
            }

            node = node->NextSibling();
        }
    }
Пример #25
0
void TileCollisionDock::setTile(Tile *tile)
{
    if (mTile == tile)
        return;

    mTile = tile;

    mMapScene->disableSelectedTool();
    MapDocument *previousDocument = mDummyMapDocument;

    mMapView->setEnabled(tile);

    if (tile) {
        Map::Orientation orientation = Map::Orthogonal;
        QSize tileSize = tile->size();

        if (tile->tileset()->orientation() == Tileset::Isometric) {
            orientation = Map::Isometric;
            tileSize = tile->tileset()->gridSize();
        }

        Map *map = new Map(orientation, 1, 1, tileSize.width(), tileSize.height());
        map->addTileset(tile->sharedTileset());

        TileLayer *tileLayer = new TileLayer(QString(), 0, 0, 1, 1);
        tileLayer->setCell(0, 0, Cell(tile));
        map->addLayer(tileLayer);

        ObjectGroup *objectGroup;
        if (tile->objectGroup())
            objectGroup = tile->objectGroup()->clone();
        else
            objectGroup = new ObjectGroup;

        objectGroup->setDrawOrder(ObjectGroup::IndexOrder);
        map->setNextObjectId(objectGroup->highestObjectId() + 1);
        map->addLayer(objectGroup);

        mDummyMapDocument = new MapDocument(map);
        mDummyMapDocument->setCurrentLayer(objectGroup);

        mMapScene->setMapDocument(mDummyMapDocument);
        mToolManager->setMapDocument(mDummyMapDocument);

        mMapScene->enableSelectedTool();

        connect(mDummyMapDocument->undoStack(), &QUndoStack::indexChanged,
                this, &TileCollisionDock::applyChanges);

        connect(mDummyMapDocument, &MapDocument::selectedObjectsChanged,
                this, &TileCollisionDock::selectedObjectsChanged);

    } else {
        mDummyMapDocument = nullptr;
        mMapScene->setMapDocument(nullptr);
        mToolManager->setMapDocument(nullptr);
    }

    emit dummyMapDocumentChanged(mDummyMapDocument);

    setHasSelectedObjects(false);

    if (previousDocument) {
        // Explicitly disconnect early from this signal, since it can get fired
        // from the QUndoStack destructor.
        disconnect(previousDocument->undoStack(), &QUndoStack::indexChanged,
                   this, &TileCollisionDock::applyChanges);

        delete previousDocument;
    }
}
Пример #26
0
	void Map::ParseText(const string &text) 
	{
		// Create a tiny xml document and use it to parse the text.
		TiXmlDocument doc;
		doc.Parse(text.c_str());
	
		// Check for parsing errors.
		if (doc.Error()) 
		{
			has_error = true;
			error_code = TMX_PARSING_ERROR;
			error_text = doc.ErrorDesc();
			return;
		}

		TiXmlNode *mapNode = doc.FirstChild("map");
		TiXmlElement* mapElem = mapNode->ToElement();

		// Read the map attributes.
		mapElem->Attribute("version", &version);
		mapElem->Attribute("width", &width);
		mapElem->Attribute("height", &height);
		mapElem->Attribute("tilewidth", &tile_width);
		mapElem->Attribute("tileheight", &tile_height);

		// Read the orientation
		std::string orientationStr = mapElem->Attribute("orientation");

		if (!orientationStr.compare("orthogonal")) 
		{
			orientation = TMX_MO_ORTHOGONAL;
		} 
		else if (!orientationStr.compare("isometric")) 
		{
			orientation = TMX_MO_ISOMETRIC;
		}

		// Read the map properties.
		const TiXmlNode *propertiesNode = mapElem->FirstChild("properties");
		if (propertiesNode) 
		{
			properties.Parse(propertiesNode);
		}

		// Iterate through all of the tileset elements.
		const TiXmlNode *tilesetNode = mapNode->FirstChild("tileset");
		while (tilesetNode) 
		{
			// Allocate a new tileset and parse it.
			Tileset *tileset = new Tileset();
			tileset->Parse(tilesetNode->ToElement());

			// Add the tileset to the list.
			tilesets.push_back(tileset);

			tilesetNode = mapNode->IterateChildren("tileset", tilesetNode);
		}

		// Iterate through all of the layer elements.
		TiXmlNode *layerNode = mapNode->FirstChild("layer");
		while (layerNode) 
		{
			// Allocate a new layer and parse it.
			Layer *layer = new Layer(this);
			layer->Parse(layerNode);

			// Add the layer to the list.
			layers.push_back(layer);

			layerNode = mapNode->IterateChildren("layer", layerNode);
		}

		// Iterate through all of the objectgroup elements.
		TiXmlNode *objectGroupNode = mapNode->FirstChild("objectgroup");
		while (objectGroupNode) 
		{
			// Allocate a new object group and parse it.
			ObjectGroup *objectGroup = new ObjectGroup();
			objectGroup->Parse(objectGroupNode);
		
			// Add the object group to the list.
			object_groups.push_back(objectGroup);

			objectGroupNode = mapNode->IterateChildren("objectgroup", objectGroupNode);
		}
	}
Пример #27
0
/**
 * Shows the context menu for map objects. The menu allows you to duplicate and
 * remove the map objects, or to edit their properties.
 */
void AbstractObjectTool::showContextMenu(MapObjectItem *clickedObjectItem,
                                         QPoint screenPos)
{
    QSet<MapObjectItem *> selection = mMapScene->selectedObjectItems();
    if (clickedObjectItem && !selection.contains(clickedObjectItem)) {
        selection.clear();
        selection.insert(clickedObjectItem);
        mMapScene->setSelectedObjectItems(selection);
    }
    if (selection.isEmpty())
        return;

    const QList<MapObject*> &selectedObjects = mapDocument()->selectedObjects();
    const QList<ObjectGroup*> objectGroups = mapDocument()->map()->objectGroups();

    QMenu menu;
    QAction *duplicateAction = menu.addAction(tr("Duplicate %n Object(s)", "", selection.size()),
                                              this, SLOT(duplicateObjects()));
    QAction *removeAction = menu.addAction(tr("Remove %n Object(s)", "", selection.size()),
                                           this, SLOT(removeObjects()));

    duplicateAction->setIcon(QIcon(QLatin1String(":/images/16x16/stock-duplicate-16.png")));
    removeAction->setIcon(QIcon(QLatin1String(":/images/16x16/edit-delete.png")));

    bool anyTileObjectSelected = std::any_of(selectedObjects.begin(),
                                             selectedObjects.end(),
                                             isTileObject);

    if (anyTileObjectSelected) {
        auto resetTileSizeAction = menu.addAction(tr("Reset Tile Size"), this, SLOT(resetTileSize()));
        resetTileSizeAction->setEnabled(std::any_of(selectedObjects.begin(),
                                                    selectedObjects.end(),
                                                    isResizedTileObject));

        auto changeTileAction = menu.addAction(tr("Replace Tile"), this, SLOT(changeTile()));
        changeTileAction->setEnabled(tile());
    }

    // Create action for replacing an object with a template
    auto selectedTemplate = objectTemplate();
    auto replaceTemplateAction = menu.addAction(tr("Replace With Template"), this, SLOT(replaceObjectsWithTemplate()));

    if (selectedTemplate) {
        QString name = QFileInfo(selectedTemplate->fileName()).fileName();
        replaceTemplateAction->setText(tr("Replace With Template \"%1\"").arg(name));
    } else {
        replaceTemplateAction->setEnabled(false);
    }

    if (selectedObjects.size() == 1) {
        MapObject *currentObject = selectedObjects.first();

        if (!(currentObject->isTemplateBase() || currentObject->isTemplateInstance())) {
            const Cell cell = selectedObjects.first()->cell();
            // Saving objects with embedded tilesets is disabled
            if (cell.isEmpty() || cell.tileset()->isExternal())
                menu.addAction(tr("Save As Template"), this, SLOT(saveSelectedObject()));
        }

        if (currentObject->isTemplateBase()) { // Hide this operations for template base
            duplicateAction->setVisible(false);
            removeAction->setVisible(false);
            replaceTemplateAction->setVisible(false);
        }
    }

    bool anyIsTemplateInstance = std::any_of(selectedObjects.begin(),
                                             selectedObjects.end(),
                                             [](MapObject *object) { return object->isTemplateInstance(); });

    if (anyIsTemplateInstance) {
        menu.addAction(tr("Detach"), this, SLOT(detachSelectedObjects()));

        auto resetToTemplateAction = menu.addAction(tr("Reset Template Instance(s)"), this, SLOT(resetInstances()));
        resetToTemplateAction->setEnabled(std::any_of(selectedObjects.begin(),
                                                      selectedObjects.end(),
                                                      isChangedTemplateInstance));
    }

    menu.addSeparator();
    menu.addAction(tr("Flip Horizontally"), this, SLOT(flipHorizontally()), QKeySequence(tr("X")));
    menu.addAction(tr("Flip Vertically"), this, SLOT(flipVertically()), QKeySequence(tr("Y")));

    ObjectGroup *objectGroup = RaiseLowerHelper::sameObjectGroup(selection);
    if (objectGroup && objectGroup->drawOrder() == ObjectGroup::IndexOrder) {
        menu.addSeparator();
        menu.addAction(tr("Raise Object"), this, SLOT(raise()), QKeySequence(tr("PgUp")));
        menu.addAction(tr("Lower Object"), this, SLOT(lower()), QKeySequence(tr("PgDown")));
        menu.addAction(tr("Raise Object to Top"), this, SLOT(raiseToTop()), QKeySequence(tr("Home")));
        menu.addAction(tr("Lower Object to Bottom"), this, SLOT(lowerToBottom()), QKeySequence(tr("End")));
    }

    if (objectGroups.size() > 1) {
        menu.addSeparator();
        QMenu *moveToLayerMenu = menu.addMenu(tr("Move %n Object(s) to Layer",
                                                 "", selectedObjects.size()));
        for (ObjectGroup *objectGroup : objectGroups) {
            QAction *action = moveToLayerMenu->addAction(objectGroup->name());
            action->setData(QVariant::fromValue(objectGroup));
        }
    }

    menu.addSeparator();
    QIcon propIcon(QLatin1String(":images/16x16/document-properties.png"));
    QAction *propertiesAction = menu.addAction(propIcon,
                                               tr("Object &Properties..."));

    Utils::setThemeIcon(removeAction, "edit-delete");
    Utils::setThemeIcon(propertiesAction, "document-properties");

    QAction *action = menu.exec(screenPos);
    if (!action)
        return;

    if (action == propertiesAction) {
        MapObject *mapObject = selectedObjects.first();
        mapDocument()->setCurrentObject(mapObject);
        emit mapDocument()->editCurrentObject();
        return;
    }

    if (ObjectGroup *objectGroup = action->data().value<ObjectGroup*>()) {
        mapDocument()->moveObjectsToGroup(mapDocument()->selectedObjects(),
                                          objectGroup);
    }
}
Пример #28
0
	void Map::ParseText(const string &text)
	{
		// Create a tiny xml document and use it to parse the text.
		TiXmlDocument doc;
		doc.Parse(text.c_str());

		// Check for parsing errors.
		if (doc.Error())
		{
			has_error = true;
			error_code = TMX_PARSING_ERROR;
			error_text = doc.ErrorDesc();
			return;
		}

		TiXmlNode *mapNode = doc.FirstChild("map");
		TiXmlElement* mapElem = mapNode->ToElement();

		// Read the map attributes.
		mapElem->Attribute("version", &version);
		mapElem->Attribute("width", &width);
		mapElem->Attribute("height", &height);
		mapElem->Attribute("tilewidth", &tile_width);
		mapElem->Attribute("tileheight", &tile_height);

		// Read the orientation
		std::string orientationStr = mapElem->Attribute("orientation");

		if (!orientationStr.compare("orthogonal"))
		{
			orientation = TMX_MO_ORTHOGONAL;
		}
		else if (!orientationStr.compare("isometric"))
		{
			orientation = TMX_MO_ISOMETRIC;
		}
		else if (!orientationStr.compare("staggered"))
		{
			orientation = TMX_MO_STAGGERED;
		}


		const TiXmlNode *node = mapElem->FirstChild();
		int zOrder = 0;
		while( node )
		{
			// Read the map properties.
			if( strcmp( node->Value(), "properties" ) == 0 )
			{
				properties.Parse(node);
			}

			// Iterate through all of the tileset elements.
			if( strcmp( node->Value(), "tileset" ) == 0 )
			{
				// Allocate a new tileset and parse it.
				Tileset *tileset = new Tileset();
				tileset->Parse(node->ToElement());

				// Add the tileset to the list.
				tilesets.push_back(tileset);
			}

			// Iterate through all of the layer elements.
			if( strcmp( node->Value(), "layer" ) == 0 )
			{
				// Allocate a new layer and parse it.
				Layer *layer = new Layer(this);
				layer->Parse(node);
				layer->SetZOrder( zOrder );
				++zOrder;

				// Add the layer to the list.
				layers.push_back(layer);
			}

			// Iterate through all of the imagen layer elements.
			if( strcmp( node->Value(), "imagelayer" ) == 0 )
			{
				// Allocate a new layer and parse it.
				ImageLayer *imageLayer = new ImageLayer(this);
				imageLayer->Parse(node);
				imageLayer->SetZOrder( zOrder );
				++zOrder;

				// Add the layer to the list.
				image_layers.push_back(imageLayer);
			}

			// Iterate through all of the objectgroup elements.
			if( strcmp( node->Value(), "objectgroup" ) == 0 )
			{
				// Allocate a new object group and parse it.
				ObjectGroup *objectGroup = new ObjectGroup();
				objectGroup->Parse(node);
				objectGroup->SetZOrder( zOrder );
				++zOrder;

				// Add the object group to the list.
				object_groups.push_back(objectGroup);
			}

			node = node->NextSibling();
		}
	}
Пример #29
0
int main() {

  assert(sizeof(BHVNode)==32);  // in case we mess it up one day


  auto startTime = std::chrono::high_resolution_clock::now();

  int nthreads = 1;
  /* int nthreads = 2; */
  /* int nthreads = 4; */

  /* int width = 400; */
  /* int height = 200; */

  /* int width = 1600; */
  /* int height = 800; */

  /* int width = 1920/8; */
  /* int height = 1080/8; */

  /* int width = 1920/4; */
  /* int height = 1080/4; */
  /* int height = 50; */

  /* int width = 1920/2; */
  /* int height = 1080/2; */

  /* int width = 1920; */
  int height = 1080;

  /* int height = 1920; */
  /* int width = 1080; */

  /* int width = 1200; */
  /* int height = 600; */

  /* int width = 600; */
  /* int height = 300; */

  int width = height;

  /* int nsamples = 1; */
  /* int nsamples = 4; */
  int nsamples = 20;
  /* int nsamples = 50; */
  /* int nsamples = 100; */
  /* int nsamples = 200; */
  /* int nsamples = 500; */
  /* int nsamples = 1000; */
  /* int nsamples = 2000; */
  /* int nsamples = 5000; */
  /* int nsamples = 10000; */

  /* int max_bounces = 1; */
  /* int max_bounces = 2; */
  int max_bounces = 5;
  /* int max_bounces = 10; */
  /* int max_bounces = 100; */

  // above on the left
  /* Camera camera( */
  /*     width, */
  /*     height, */
  /*     Vector(-2, 2, 1), */
  /*     Vector(0, 0, -1), */
  /*     Vector(0, 1, 0), */
  /*     90 */
  /* ); */

  // in front
  /* Camera camera( */
  /*     width, */
  /*     height, */
  /*     Vector(-1.8, 1., -2), */
  /*     Vector(0, 0, -1), */
  /*     Vector(0, 1, 0), */
  /*     90 */
  /* ); */

  // inside the box, looking in front
  Camera camera(
      width,
      height,
      /* Vector(0, 0, -3.f),  // cow */
      /* Vector(-0.5f, 1.f, -6.f),  // bunny */
      Vector(0, 0, -3.8f),  // cornell box
      /* Vector(0, 0, -1.1f), */
      /* Vector(-0.5f, 1.f, 0),  // bunny pos */
      Vector(0.f, 0.f, 0.f),
      Vector(0, 1, 0),
      40
      );

#if 0

  // dragon
  Camera camera(
      width,
      height,
      Vector(0.f, 0.1f, 0.22f),  // profile
      /* Vector(-0.072f, 0.14f, -0.03f),  // quarter zoomed in */
      /* Vector(-0.2f, 0.2f, -0.2f),  // quarter */
      Vector(0.f, 0.12f, 0),
      Vector(0, 1, 0),
      40
      );

#elif 0

  /* // buddha */
  Camera camera(
      width,
      height,
      Vector(0.f, 0.2f, 0.35f),  // quarter
      Vector(0.f, 0.15f, 0),
      Vector(0, 1, 0),
      40
      );

#endif

  ObjectGroup *world = new ObjectGroup();

  // 2 metal + 1 lamb in the middle
  /* world->add( new Sphere(Vector(0, -100.5, -1), 100, new Lambertian(Vector(0.8, 0.8, 0))) ); */
  /* world->add( new Sphere(Vector(0, 0, -1), 0.5, new Lambertian(Vector(0.8, 0.3, 0.3))) ); */
  /* world->add( new Sphere(Vector(1, 0, -1), 0.5, new Metal(Vector(0.8, 0.6, 0.2), 0)) ); */
  /* world->add( new Sphere(Vector(-1, 0, -1), 0.5, new Metal(Vector(0.8, 0.8, 0.8), 0.1)) ); */

  // bubble-lamb-metal
  //
  /* world->add( new Plane(Vector(0, -0.5f, 0), Vector(0.f, 1.f, 0.f), new Lambertian(new ConstantTexture(0.8, 0.8, 0))) ); */
  /* world->add( new Sphere(Vector(0, -100.5, -1), 100, new Lambertian(new ConstantTexture(0.8, 0.8, 0))) ); */

  /* world->add( new Sphere(Vector(0, 0, -1), */
  /*                        0.5, */
  /*                        new Lambertian( */
  /*                          new CheckerTexture( */
  /*                            new ConstantTexture(0.f, 0.f, 0.f), */
  /*                            new ConstantTexture(1.f, 1.f, 1.f))))); */

  /* world->add( new Sphere(Vector(0, 0, -1), */
  /*       0.5, */
  /*       new Lambertian( */
  /*         new ImageTexture("earth.bmp")))); */

  /* world->add( new Triangle(Vector(0.2f, 0.5f, -1), Vector(0, 1.5f, -1), Vector(0, 0.5f, -2), new Metal(new ConstantTexture(0.8f, 0.8f, 0.8f), 0.0f)) ); */

  /* world->add( new Sphere(Vector(-0.5, 0, -2.2), 0.5, new Metal(new ConstantTexture(0.9, 0.9, 0.9), 0)) ); */
  /* world->add( new Sphere(Vector(-1, 0, -1), 0.5, new Dielectric(1.5f)) ); */
  /* world->add( new Sphere(Vector(-1, 0, -1), 0.45, new Dielectric(1.f, 1.5f)) ); */
  /* world->add( new Sphere(Vector(-2, 0, -1), 0.5, new Light(new ConstantTexture(20.f, 20.f, 20.f))) ); */
  /* world->add( new Sphere(Vector(0, 2, -1.5), 0.5, new Light(new ConstantTexture(20.f, 20.f, 20.f))) ); */


  // cornell box
  //
  //   /B---C
  //  A---D/|
  //  | F-|-G
  //  E/--H/

  Vector A = Vector(-1, 1, -1);
  Vector B = Vector(-1, 1, 1);
  Vector C = Vector(1, 1, 1);
  Vector D = Vector(1, 1, -1);
  Vector E = Vector(-1, -1, -1);
  Vector F = Vector(-1, -1, 1);
  Vector G = Vector(1, -1, 1);
  Vector H = Vector(1, -1, -1);

  // back
  Material* backMat = new Lambertian(new ConstantTexture(1.f, 1.f, 1.f));
  world->add( new Triangle(F, B, G, backMat));
  world->add( new Triangle(C, G, B, backMat));

  // front - letting ray in from the camera
  // /!\ not part of the original cornell box
  /* Material* frontMat = new Lambertian(new ConstantTexture(1.f, 1.f, 1.f)); */
  /* world->add( new Triangle(E, H, A, frontMat)); */
  /* world->add( new Triangle(D, A, H, frontMat)); */

  // floor
  Material* floorkMat = new Lambertian(new ConstantTexture(1.f, 1.f, 1.f));
  world->add( new Triangle(E, F, G, floorkMat));
  world->add( new Triangle(G, H, E, floorkMat));

  // ceiling
  Material* ceilingMat = new Lambertian(new ConstantTexture(1.f, 1.f, 1.f));
  /* Material* ceilingMat = new Metal(new ConstantTexture(1.f, 1.f, 1.f), 0); */
  world->add( new Triangle(A, C, B, ceilingMat));
  world->add( new Triangle(C, A, D, ceilingMat));

  // left
  Material* leftMat = new Lambertian(new ConstantTexture(1.f, 0.f, 0.f));
  world->add( new Triangle(A, B, E, leftMat));
  world->add( new Triangle(B, F, E, leftMat));

  // right
  Material* rightMat = new Lambertian(new ConstantTexture(0.f, 1.f, 0.f));
  world->add( new Triangle(D, H, C, rightMat));
  world->add( new Triangle(C, H, G, rightMat));

  // light
  float lightIntensity = 10.f;
  float lightSize = 0.3f;

  Material* lightMat = new Light(new ConstantTexture(lightIntensity, lightIntensity, lightIntensity));
  Vector lA = Vector(-lightSize, 0.99f, -lightSize);
  Vector lB = Vector(-lightSize, 0.99f, lightSize);
  Vector lC = Vector(lightSize, 0.99f, lightSize);
  Vector lD = Vector(lightSize, 0.99f, -lightSize);
  world->add( new Triangle(lA, lC, lB, lightMat));
  world->add( new Triangle(lC, lA, lD, lightMat));
  /* world->add( new Sphere(Vector(0, 0, 0), 0.2, new Light(new ConstantTexture(20.f, 20.f, 20.f))) ); */


  // stuff inside the box

  /* world->add( box(Vector(-0.7, -0.2, 0.2), 0.5, 0.5, 0.5, new Metal(new ConstantTexture(0.9, 0.9, 0.9), 0.01)) ); */
  /* for(int i=0 ; i<100 ; i++){ */
    world->extend( box(new Lambertian(new ConstantTexture(0.1, 0.2, 0.95)),
                    Vector(-0.6, -0.1, 0.1),
                    Vector(0.5, 0.8, 0.5),
                    Vector(0.f, -20.f, 0.f))->list );

    world->extend( box(new Lambertian(new ConstantTexture(1, 1, 1)),
          Vector(0., -0.5, -0.7),
          Vector(0.5, 0.5, 0.5),
          Vector(0.f, 15.f, 0.f))->list );
  /* } */


  // obj loading tests
  // http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/
  //
    auto checkpoint = std::chrono::high_resolution_clock::now();

    Material* objMat = new Lambertian(new ConstantTexture(0.1f, 0.2f, 0.8f));
  /* Material* objMat = new Metal(new ConstantTexture(0.6f, 0.6f, 0.2f), 0.1f); */
  /* Material* objMat = new Light(new ConstantTexture(0.1f, 0.1f, 0.8f)); */

  std::vector<Vector> vertices;
  std::vector<Vector> normals;
  /* std::vector<Vector> texturecoords; */
  std::vector<std::array<long double, 3>> faces;  // computed normals indexed by vertices
  std::vector<int> nfaces;  // number of faces by vertices - to average out the normal
  std::vector<std::array<int, 3>> faceLinks;  // for each face, link to vertice ids

  ObjectGroup *obj = new ObjectGroup();
  std::vector<SmoothedTriangle*> tosmooth;

  int ignoredObj = 0;

#if 1  // obj loading

  std::ifstream file("cow.obj");
  /* std::ifstream file("bunny.obj"); */
  std::string str;
  while (std::getline(file, str)) {

    bool gotnormals = false;

    // vertices
    if( str.find("v ") == 0){
      float a, b, c;
      sscanf(str.c_str(), "v %f %f %f", &a, &b, &c);
      vertices.push_back(Vector(a, b, c));

      faces.push_back( {{0.L, 0.L, 0.L}} );
      nfaces.push_back(0);

    // vertice normals
    } else if( str.find("vn ") == 0){
        float na, nb, nc;
        sscanf(str.c_str(), "vn %f %f %f", &na, &nb, &nc);
        normals.push_back(Vector(na, nb, nc));
        /* std::cout << Vector(na, nb, nc).length() << " na=" << na << " nb=" << nb << " nc=" << nc << std::endl; */


    // faces (ie triangles)
    } else if( str.find("f ") == 0){
      long a, b, c;
      long na, nb, nc;
      if(str.find("//") != std::string::npos){
        gotnormals = true;
        sscanf(str.c_str(), "f %ld//%ld %ld//%ld %ld//%ld", &a, &na, &b, &nb, &c, &nc);
      } else if(str.find("/") != std::string::npos) {
        gotnormals = true;
        sscanf(str.c_str(), "f %ld/%*s/%ld %ld/%*s/%ld %ld/%*s/%ld", &a, &na, &b, &nb, &c, &nc);
      } else {
        sscanf(str.c_str(), "f %ld %ld %ld", &a, &b, &c);
      }

      unsigned long A, B, C;
      A = a > 0 ? a-1 : vertices.size()+a;
      B = b > 0 ? b-1 : vertices.size()+b;
      C = c > 0 ? c-1 : vertices.size()+c;

      if(A > vertices.size()
          || B > vertices.size()
          || C > vertices.size()){
        std::cerr << "NOPE vertices " << A  << " " << B << " "  << C << " "  << vertices.size() << std::endl;
        std::cerr << str << std::endl;
        exit(1);
      }

      if(gotnormals) {
        unsigned long NA, NB, NC;
        NA = na > 0 ? na-1 : normals.size()+na;
        NB = nb > 0 ? nb-1 : normals.size()+nb;
        NC = nc > 0 ? nc-1 : normals.size()+nc;

        if(NA > normals.size()
            || NB > normals.size()
            || NC > normals.size()){
          std::cerr << "NOPE normals " << NA  << " " << NB << " "  << NC << " "  << normals.size() << std::endl;
          std::cerr << str << std::endl;
          exit(1);
        }

        obj->add( new SmoothedTriangle(  vertices[A],
                                         vertices[B],
                                         vertices[C],
                                         normals[NA],
                                         normals[NB],
                                         normals[NC],
                                         objMat) );
      } else {
        SmoothedTriangle* triangle = new SmoothedTriangle(
             vertices[A],
             vertices[B],
             vertices[C],
             objMat);
        tosmooth.push_back(triangle);

        long axis[] = {a, b, c};
        for(long axes : axis){
          for(int i=0 ; i<3 ; i++)
            faces[axes][i] += (long double) triangle->norm[i];

          nfaces[axes] += 1;
        }

        std::array<int, 3> link { {(int)a, (int)b, (int)c} };
        faceLinks.push_back(link);
      }

    }
  }

  long face_count = faces.size();

#else  // ply loading  -- pretty dumb one
  // assume the only elements are vertices & faces
  // + faces are composed of exactly 3 vertices
  // + ascii format

  std::ifstream file("ply/dragon_recon/dragon_vrip.ply");
  /* std::ifstream file("ply/dragon_recon/dragon_vrip_res2.ply"); */

  /* std::ifstream file("ply/happy_recon/happy_vrip.ply"); */
  /* std::ifstream file("ply/happy_recon/happy_vrip_res4.ply"); */

  std::string str;

  long face_count = -1;
  long vertex_count = -1;

  // read header
  while(std::getline(file, str)){
    if(str == "ply")
      continue;  // ignore

    else if(str.find("format") == 0){
      assert(str == "format ascii 1.0");  // only format supported atm

    } else if(str.find("element") == 0){
      if(str.find("element vertex") == 0) {
        assert(face_count == -1);
        sscanf(str.c_str(), "element vertex %ld", &vertex_count);

      } else if(str.find("element face") == 0) {
        assert(vertex_count != -1);
        sscanf(str.c_str(), "element face %ld", &face_count);

      } else {
        std::cout << "unknow element type in ply: " << str << std::endl;
        exit(1);
      }


    } else if(str.find("property") == 0){
      continue;  // ignore atm - assume vertices are x,y,z floats ; faces as 3 ints

    } else if(str.find("comment") == 0) {
      continue;  // ignore

    } else if(str == "end_header"){
      break;

    } else {
      std::cout << "UNKNOWN LINE IN PLY: " << str << std::endl;
    }
  }

  std::cout << "PLY header: vertices = " << vertex_count << " ; faces = " << face_count << std::endl;

  std::cout << "PLY header loaded in: " << (std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - checkpoint)).count() << " ms" << std::endl;
  checkpoint = std::chrono::high_resolution_clock::now();

  vertices.reserve(vertex_count);
  faces.reserve(vertex_count);
  nfaces.reserve(vertex_count);
  /* faceLinks.reserve(face_count); */
  /* tosmooth.reserve(face_count); */

  std::cout << "PLY struct reservation in: " << (std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - checkpoint)).count() << " ms" << std::endl;
  checkpoint = std::chrono::high_resolution_clock::now();

  for(int ivertex=0 ; ivertex < vertex_count ; ivertex++){
    std::getline(file, str);
    float a, b, c;
    sscanf(str.c_str(), "%f %f %f", &a, &b, &c);
    vertices.push_back(Vector(a,b,c));

    faces.push_back( {{0.L, 0.L, 0.L}} );
    nfaces.push_back(0);
  }

  std::cout << "PLY vertices loaded in: " << (std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - checkpoint)).count() << " ms" << std::endl;
  checkpoint = std::chrono::high_resolution_clock::now();

  for(int iface=0 ; iface < face_count ; iface++){
    std::getline(file, str);
    long a, b, c;
    sscanf(str.c_str(), "3 %ld %ld %ld", &a, &b, &c);

    /* std::cout << "str \"" << str << "\"  => a: " << a << " b: " << b << " c: " << c << std::endl; */

    assert(a >= 0 && b >= 0 && c >= 0
        && a < vertex_count
        && b < vertex_count
        && c < vertex_count);

    SmoothedTriangle* triangle = new SmoothedTriangle(
      vertices[a],
      vertices[b],
      vertices[c],
      objMat);

    // check for NaN in file
    bool toignore = false;
    for(int iaxis=0 ; iaxis<3 ; iaxis++)
      if(isnan(triangle->norm[iaxis]) || triangle->norm[iaxis] < -1.1f) {
        toignore = true;
        /* std::cout << "problem in ply file - incorrect normals: " << triangle->norm << " vertices: a=" << vertices[a] << " b=" << vertices[b] << " c= " << vertices[c] << "  indices: (" << a << ", " << b << ", " << c << ")" << std::endl; */
      }

    if(!toignore) {
      long axis[] = {a, b, c};
      for(long axes : axis){
        for(int i=0 ; i<3 ; i++)
          faces[axes][i] += (long double) triangle->norm[i];

        nfaces[axes] += 1;
      }

      tosmooth.push_back(triangle);

      std::array<int, 3> link { {(int)a, (int)b, (int)c} };
      faceLinks.push_back(link);

    } else {
      ignoredObj++;
    }
  }

  std::cout << "PLY faces loaded in: " << (std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - checkpoint)).count() << " ms" << std::endl;
  checkpoint = std::chrono::high_resolution_clock::now();

#endif

  if(ignoredObj > 0)
    std::cout << "\n=============\n!!!! " << ignoredObj << " FACES HAVE BEEN IGNORED ON THE 1st PASS!!!\n==========\n" << std::endl;

  int secondPassIgnoredObj = 0;

  file.close();

  for(unsigned int iface=0 ; iface<face_count-ignoredObj ; iface++){
    SmoothedTriangle* triangle = tosmooth[iface];
    std::array<int, 3> link = faceLinks[iface];

    for(int iaxes = 0 ; iaxes<3 ; iaxes++){
      triangle->na[iaxes] = float( faces[link[0]][iaxes] / nfaces[link[0]] );
      triangle->nb[iaxes] = float( faces[link[1]][iaxes] / nfaces[link[1]] );
      triangle->nc[iaxes] = float( faces[link[2]][iaxes] / nfaces[link[2]] );
    }

    /* std::cout << "link: " << link[0] << " " << link[1] << " " << link[2] << std::endl; */

    bool ignoreobj = false;  // if some problem occurs
    for(int iaxis=0 ; iaxis<3 ; iaxis++){
      /* bool toignorethis = true; */
      /* if(nfaces[link[iaxis]] == 0) */
      /*   std::cout << "problem in ply file: " << link[iaxis] << std::endl; */

      /* else if(isnan(triangle->na[iaxis]) || triangle->na[iaxis] < -1.1f) */
      /*   std::cout << "problem in ply file: " << faces[link[0]] << std::endl; */

      /* else if(isnan(triangle->nb[iaxis]) || triangle->nb[iaxis] < -1.1f) */
      /*   std::cout << "problem in ply file: " << faces[link[1]] << std::endl; */

      /* else if(isnan(triangle->nc[iaxis]) || triangle->nc[iaxis] < -1.1f) */
      /*   std::cout << "problem in ply file: " << faces[link[2]] << std::endl; */

      /* else */
      /*   toignorethis = false; */

      /* if(toignorethis) */
      /*   ignoreobj = true; */

      if( !(nfaces[link[iaxis]] != 0
          && !isnan(triangle->na[iaxis]) && triangle->na[iaxis] > -1.f
          && !isnan(triangle->nb[iaxis]) && triangle->nb[iaxis] > -1.f
          && !isnan(triangle->nc[iaxis]) && triangle->nc[iaxis] > -1.f))

        ignoreobj = true;
    }

    if(!ignoreobj)
      obj->add(triangle);
    else
      secondPassIgnoredObj++;
  }

  std::cout << "Triangle smoothing in: " << (std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - checkpoint)).count() << " ms" << std::endl;

  auto fileReadingTime = std::chrono::high_resolution_clock::now();
  std::chrono::duration<double, std::milli> totalFileReadingTime = fileReadingTime - startTime;
  std::cout << "File loaded in: " << totalFileReadingTime.count() << " ms\n" << std::endl;

  if(secondPassIgnoredObj > 0)
    std::cout << "\n=============\n!!!! " << secondPassIgnoredObj  << " FACES HAVE BEEN IGNORED ON 2nd PASS!!!\n==========\n" << std::endl;

  /* world = new ObjectGroup(); */
  world->extend(obj->list);


  // debug cube
  /* world = new ObjectGroup(); */
  /* world->extend( box(new Metal(new ConstantTexture(0.8, 0.8, 0.8), 0.f))->list ); */


  // convert world to a BHV
  auto bhvStartTime = std::chrono::high_resolution_clock::now();

  /* BHV* bhv_world = new BHV(&world->list[0], world->list.size()); */
  const BHV bhv_world(world->list);

  std::cout << "Triangle count: " << world->list.size() << std::endl;
  /* std::cout << "BHV depth: " << bhv_world.depth() << std::endl; */

  std::cout << "\nWorld center at: " << bhv_world.bounding_box()->getCenter().str() << std::endl;
  std::cout << "World bounding box: " << bhv_world.bounding_box()->str() << std::endl;

  /* std::cout << bhv_world->str() << std::endl; */
  /* std::cout << bhv_world->bounding_box()->str() << std::endl; */
  /* std::cout << bhv_world->left->bounding_box()->str() << std::endl; */
  /* std::cout << bhv_world->right->bounding_box()->str() << std::endl; */


  auto bhvTime = std::chrono::high_resolution_clock::now();
  std::chrono::duration<double, std::milli> totalBHVtime = bhvTime - bhvStartTime;
  std::cout << "\nBHV in: " << totalBHVtime.count() << " ms" << std::endl;
  startTime = std::chrono::high_resolution_clock::now();


  // start job threads
  std::thread *threads = new std::thread[nthreads];
  Vector*** out = new Vector**[nthreads];
  for(int ithread=0 ; ithread<nthreads ; ithread++) {
    out[ithread] = new Vector*[width];
    for(int i=0 ; i<width ; i++)
      out[ithread][i] = new Vector[height];

    threads[ithread] = std::thread(rt_pass, camera, bhv_world, width, height, (ithread*width)/nthreads, max_bounces, nsamples/nthreads, out[ithread]);
    /* threads[ithread] = std::thread(rt_pass, camera, bhv_world, width, height, 0, max_bounces, nsamples/nthreads, out[ithread]); */
  }


  long totalDirectRay = width*height*(nsamples-nsamples%nthreads);
  int perc = 0;
  long lastTotalRay = 0;
  int pauseCount = 0;  // keep track of the pause number: allow to display average rays for the last second - TODO rolling second
  const int pauseTime = 1; // in ms

  while(perc != 100) {

    perc = (nDirectRay*100)/totalDirectRay;

    std::this_thread::sleep_for(std::chrono::milliseconds(pauseTime));

    pauseCount++;
    if(pauseCount*pauseTime>=1000){
      pauseCount = 0;
      std::cout << "\r" << perc << " %" << "  -- " << (nTotalRay-lastTotalRay)/1000.f << "K ray per sec" << std::flush;
      lastTotalRay = nTotalRay;
    }

  }


  // join threads - just in case
  for(int ithread=0 ; ithread<nthreads ; ithread++)
    threads[ithread].join();

  auto endTime = std::chrono::high_resolution_clock::now();

  // average res & write the image
  std::ofstream image;
  image.open("image.ppm");
  /* image.open("image_"+std::to_string(nsamples)+".ppm"); */

  image << "P3\n" << width << " " << height << "\n255\n";

  // debug: bhv traversal count
  // compute maximum to display the right color panel
#if BHV_TRAVERSAL_COUNT
  int max_traversal = 0;
  long double average_traversal = 0.f;
  for(int j=height-1 ; j>=0; j--)
    for(int i=width-1 ; i>=0 ; i--)
      for(int ithread=0 ; ithread<nthreads ; ithread++){
        if(out[ithread][i][j].x > max_traversal)
           max_traversal = out[ithread][i][j].x;
        average_traversal += out[ithread][i][j].x;
      }

  std::cout << "\nmax traversal: " << max_traversal << std::endl;
  std::cout << "average direct traversal: " << (average_traversal/totalDirectRay) << std::endl;
#endif

  for(int j=height-1 ; j>=0; j--)
    for(int i=width-1 ; i>=0 ; i--) {
      Vector average = VECTOR_ZERO;
      for(int ithread=0 ; ithread<nthreads ; ithread++)
        average += out[ithread][i][j];
      average = average/nthreads;

#if BHV_TRAVERSAL_COUNT
      int *rgb = (average/max_traversal).toRGB(2);
#else
      int *rgb = average.tone_map().toRGB(2);
#endif
      // check for incoherent values
      for(int iaxis=0 ; iaxis<3 ; iaxis++)
        if(rgb[iaxis] < 0 || rgb[iaxis] > 255 || isnan(rgb[iaxis])){
          std::cout << "something's wrong: " << average << std::endl;
          for(int ithread=0 ; ithread<nthreads ; ithread++)
            std::cout << "on thread " << ithread << " => " << out[ithread][i][j] << std::endl;
          rgb[iaxis] = 0;  // =[
        }

      image << rgb[0] << " " << rgb[1] << " " << rgb[2] << "\n";
    }

  image.close();

  std::chrono::duration<double, std::milli> totalTime = endTime - startTime;
  std::cout << "\n\nTotal: " << totalTime.count() << " ms" << std::endl;
  std::cout << "\nTriangle intersections: " << nTriangleIntersection/1000000.f << "M" << std::endl;
  std::cout << "BHV intersection: " << nBoxIntersection/1000000.f << "M" << std::endl;

  std::cout << "\nTriangle intersection per ray: " << nTriangleIntersection/float(nTotalRay) << std::endl;
  std::cout << "BHV intersection per ray: " << nBoxIntersection/float(nTotalRay) << std::endl;

  std::cout << "\nAverage bounce: " << nTotalRay/float(nDirectRay) << std::endl;

  std::cout << "\nDirect rays: " << nDirectRay << std::endl;
  std::cout << "Total rays: " << nTotalRay << std::endl;
  std::cout << "\nRay per seconds: " << nTotalRay/totalTime.count() << " k" << std::endl;

  return 0;
}
Пример #30
0
    void Map::Parse(tinyxml2::XMLNode *mapNode)
    {
        tinyxml2::XMLElement* mapElem = mapNode->ToElement();

        // Read the map attributes.
        version = mapElem->IntAttribute("version");
        width = mapElem->IntAttribute("width");
        height = mapElem->IntAttribute("height");
        tile_width = mapElem->IntAttribute("tilewidth");
        tile_height = mapElem->IntAttribute("tileheight");
        next_object_id = mapElem->IntAttribute("nextobjectid");

        if (mapElem->Attribute("backgroundcolor"))
        {
            background_color = mapElem->Attribute("backgroundcolor");
        }

        // Read the orientation
        std::string orientationStr = mapElem->Attribute("orientation");

        if (!orientationStr.compare("orthogonal"))
        {
            orientation = TMX_MO_ORTHOGONAL;
        } 
        else if (!orientationStr.compare("isometric"))
        {
            orientation = TMX_MO_ISOMETRIC;
        }
        else if (!orientationStr.compare("staggered"))
        {
            orientation = TMX_MO_STAGGERED;
        }
        else if (!orientationStr.compare("hexagonal"))
        {
            orientation = TMX_MO_HEXAGONAL;
        }

        // Read the render order
        if (mapElem->Attribute("renderorder"))
        {
            std::string renderorderStr = mapElem->Attribute("renderorder");
            if (!renderorderStr.compare("right-down")) 
            {
                render_order = TMX_RIGHT_DOWN;
            } 
            else if (!renderorderStr.compare("right-up")) 
            {
                render_order = TMX_RIGHT_UP;
            }
            else if (!renderorderStr.compare("left-down")) 
            {
                render_order = TMX_LEFT_DOWN;
            }
            else if (!renderorderStr.compare("left-down")) 
            {
                render_order = TMX_LEFT_UP;
            }        
        }

        // Read the stagger axis
        if (mapElem->Attribute("staggeraxis"))
        {
            std::string staggerAxisStr = mapElem->Attribute("staggeraxis");
            if (!staggerAxisStr.compare("x"))
            {
                stagger_axis = TMX_SA_X;
            }
            else if (!staggerAxisStr.compare("y"))
            {
                stagger_axis = TMX_SA_Y;
            }
        }

        // Read the stagger index
        if (mapElem->Attribute("staggerindex"))
        {
            std::string staggerIndexStr = mapElem->Attribute("staggerindex");
            if (!staggerIndexStr.compare("even"))
            {
                stagger_index = TMX_SI_EVEN;
            }
            else if (!staggerIndexStr.compare("odd"))
            {
                stagger_index = TMX_SI_ODD;
            }
        }

        // read the hexside length
        if (mapElem->IntAttribute("hexsidelength"))
        {
            hexside_length = mapElem->IntAttribute("hexsidelength");
        }

        // read all other attributes
        const tinyxml2::XMLNode *node = mapElem->FirstChild();
        while( node )
        {
            // Read the map properties.
            if( strcmp( node->Value(), "properties" ) == 0 )
            {
                properties.Parse(node);         
            }

            // Iterate through all of the tileset elements.
            if( strcmp( node->Value(), "tileset" ) == 0 )
            {
                // Allocate a new tileset and parse it.
                Tileset *tileset = new Tileset();
                tileset->Parse(node->ToElement(), file_path);

                // Add the tileset to the list.
                tilesets.push_back(tileset);
            }

            // Iterate through all of the "layer" (tile layer) elements.           
            if( strcmp( node->Value(), "layer" ) == 0 )
            {
                // Allocate a new tile layer and parse it.
                TileLayer *tileLayer = new TileLayer(this);
                tileLayer->Parse(node);

                // Add the tile layer to the lists.
                tile_layers.push_back(tileLayer);
                layers.push_back(tileLayer);
            }

            // Iterate through all of the "imagelayer" (image layer) elements.            
            if( strcmp( node->Value(), "imagelayer" ) == 0 )
            {
                // Allocate a new image layer and parse it.
                ImageLayer *imageLayer = new ImageLayer(this);
                imageLayer->Parse(node);

                // Add the image layer to the lists.
                image_layers.push_back(imageLayer);
                layers.push_back(imageLayer);
            }

            // Iterate through all of the "objectgroup" (object layer) elements.
            if( strcmp( node->Value(), "objectgroup" ) == 0 )
            {
                // Allocate a new object group and parse it.
                ObjectGroup *objectGroup = new ObjectGroup(this);
                objectGroup->Parse(node);
        
                // Add the object group to the lists.
                object_groups.push_back(objectGroup);
                layers.push_back(objectGroup);
            }

            node = node->NextSibling();
        }
    }