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); }
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); } }
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; }
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 }
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); }
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); }
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; }
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; }
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; }
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); }
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()); }
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; }
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); } }
/** * 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); }
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; }
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]]); }
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*"); } }
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 {
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); } }
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"); } }
/** * 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); } }
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(); } }
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; } }
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); } }
/** * 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); } }
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(); } }
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; }
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(); } }