/////////////////////////////////////////////////////////////////////////////////// // File : DAG.cpp /////////////////////////////////////////////////////////////////////////////////// // // LumosQuad - A Lightning Generator // Copyright 2007 // The University of North Carolina at Chapel Hill // /////////////////////////////////////////////////////////////////////////////////// // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // The University of North Carolina at Chapel Hill makes no representations // about the suitability of this software for any purpose. It is provided // "as is" without express or implied warranty. // // Permission to use, copy, modify and distribute this software and its // documentation for educational, research and non-profit purposes, without // fee, and without a written agreement is hereby granted, provided that the // above copyright notice and the following three paragraphs appear in all // copies. // // THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN // "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION TO // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // // Please send questions and comments about LumosQuad to kim@cs.unc.edu. // /////////////////////////////////////////////////////////////////////////////////// // // This program uses OpenEXR, which has the following restrictions: // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // #include "DAG.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// DAG::DAG(int xRes, int yRes) : _xRes(xRes), _yRes(yRes), _width(xRes), _height(yRes), _dx(1.0f / xRes), _dy(1.0f / yRes), _root(NULL), _totalNodes(0), _bottomHit(-1), _secondaryIntensity(0.3f), _leaderIntensity(0.75f) { (_dx < _dy) ? _dy = _dx : _dy = _dx; _offscreenBuffer = NULL; } DAG::~DAG() { if (_offscreenBuffer) delete[] _offscreenBuffer; deleteNode(_root); } ////////////////////////////////////////////////////////////////////// // delete the nodes ////////////////////////////////////////////////////////////////////// void DAG::deleteNode(NODE* root) { if (root == NULL) return; for (int x = 0; x < root->neighbors.size(); x++) deleteNode(root->neighbors[x]); delete root; root = NULL; } ////////////////////////////////////////////////////////////////////// // add line segment 'index' to segment list ////////////////////////////////////////////////////////////////////// bool DAG::addSegment(int index, int neighbor) { if (_root) { // find corresponding root in DAG map::iterator iter = _hash.find(neighbor); NODE* root = (*iter).second; if (root == NULL) return false; // add to DAG NODE* newNode = new NODE(index); newNode->parent = root; root->neighbors.push_back(newNode); // add to hash table _hash.insert(map::value_type(index, newNode)); } else { // make the root _root = new NODE(neighbor); _hash.insert(map::value_type(neighbor, _root)); // then do the add NODE* newNode = new NODE(index); newNode->parent = _root; _root->neighbors.push_back(newNode); // add to hash table _hash.insert(map::value_type(index, newNode)); } _totalNodes++; return true; } ////////////////////////////////////////////////////////////////////// // build the leader chain ////////////////////////////////////////////////////////////////////// void DAG::buildLeader(int bottomHit) { _bottomHit = bottomHit; // get pointer to bottommost node map::iterator iter = _hash.find(bottomHit); NODE* child = (*iter).second; // crawl up the tree while (child != NULL) { // tag segment child->leader = true; child->secondary = false; // look for side branches for (int x = 0; x < child->neighbors.size(); x++) if (!(child->neighbors[x]->leader)) buildBranch(child->neighbors[x], 1); // advance child child = child->parent; } buildIntensity(_root); } ////////////////////////////////////////////////////////////////////// // build the side branch ////////////////////////////////////////////////////////////////////// void DAG::buildBranch(NODE* node, int depth) { node->depth = depth; node->leader = false; // look for side branches for (int x = 0; x < node->neighbors.size(); x++) if (!(node->neighbors[x]->leader)) buildBranch(node->neighbors[x], depth + 1); } ////////////////////////////////////////////////////////////////////// // draw the DAG segments ////////////////////////////////////////////////////////////////////// void DAG::drawNode(NODE* root) { if (root == NULL) return; // draw segments int beginIndex = root->index; int begin[2]; int end[2]; float dWidth = _dx; float dHeight = _dy; for (int x = 0; x < root->neighbors.size(); x++) { // get end node NODE* endNode = root->neighbors[x]; int endIndex = endNode->index; // draw segments begin[1] = beginIndex / _xRes; begin[0] = beginIndex - begin[1] * _xRes; end[1] = endIndex / _xRes; end[0] = endIndex - end[1] * _xRes; if (_bottomHit != -1) glColor4f(endNode->intensity, endNode->intensity, endNode->intensity,1.0f); else glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glBegin(GL_LINES); glVertex3f(begin[0] * dWidth + dWidth * 0.5f, 1.0f - begin[1] * dHeight + dHeight * 0.5f, 0.1f); glVertex3f(end[0] * dWidth + dWidth * 0.5f, 1.0f - end[1] * dHeight + dHeight * 0.5f, 0.1f); glEnd(); // call recursively if (endNode->neighbors.size() > 0) drawNode(endNode); } } ////////////////////////////////////////////////////////////////////// // set the intensity of the node ////////////////////////////////////////////////////////////////////// void DAG::buildIntensity(NODE* root) { if (root == NULL) return; // draw segments int beginIndex = root->index; int begin[2]; int end[2]; float dWidth = _dx; float dHeight = _dy; for (int x = 0; x < root->neighbors.size(); x++) { // get end node NODE* endNode = root->neighbors[x]; int endIndex = endNode->index; // draw segments begin[1] = beginIndex / _xRes; begin[0] = beginIndex - begin[1] * _xRes; end[1] = endIndex / _xRes; end[0] = endIndex - end[1] * _xRes; // set color if (endNode->leader) endNode->intensity = _leaderIntensity; else { // find max depth of current channel if (endNode->maxDepthNode == NULL) findDeepest(endNode, endNode->maxDepthNode); int maxDepth = endNode->maxDepthNode->depth; // calc standard deviation float stdDev = -(float)(maxDepth * maxDepth) / (float)(log(_secondaryIntensity) * 2.0f); // calc falloff float eTerm = -(float)(endNode->depth) * (float)(endNode->depth); eTerm /= (2.0f * stdDev); eTerm = exp(eTerm) * 0.5f; endNode->intensity = eTerm; } // call recursively if (endNode->neighbors.size() > 0) buildIntensity(endNode); } } ////////////////////////////////////////////////////////////////////// // draw the tree offscreen ////////////////////////////////////////////////////////////////////// float*& DAG::drawOffscreen(int scale) { // allocate buffer _width = _xRes * scale; _height = _yRes * scale; _scale = scale; if (_offscreenBuffer) delete[] _offscreenBuffer; _offscreenBuffer = new float[_width * _height]; // wipe the buffer for (int x = 0; x < _width * _height; x++) _offscreenBuffer[x] = 0.0f; // recursively draw the tree drawOffscreenNode(_root); return _offscreenBuffer; } ////////////////////////////////////////////////////////////////////// // draw the DAG segments ////////////////////////////////////////////////////////////////////// void DAG::drawOffscreenNode(NODE* root) { if (root == NULL) return; // draw segments int beginIndex = root->index; int begin[2]; int end[2]; float dWidth = _dx; float dHeight = _dy; for (int x = 0; x < root->neighbors.size(); x++) { // get end node NODE* endNode = root->neighbors[x]; int endIndex = endNode->index; // get endpoints begin[0] = beginIndex % _xRes * _scale; begin[1] = beginIndex / _xRes * _scale; end[0] = endIndex % _xRes * _scale; end[1] = endIndex / _xRes * _scale; // make sure the one with the smaller x comes first if (end[0] < begin[0]) { int temp[] = {end[0], end[1]}; end[0] = begin[0]; end[1] = begin[1]; begin[0] = temp[0]; begin[1] = temp[1]; } // rasterize drawLine(begin, end, endNode->intensity); // call recursively if (endNode->neighbors.size() > 0) drawOffscreenNode(endNode); } } ////////////////////////////////////////////////////////////////////// // rasterize the line // I assume all the lines are purely horizontal, vertical or diagonal // to avoid using something like Bresenham ////////////////////////////////////////////////////////////////////// void DAG::drawLine(int begin[], int end[], float intensity) { int scaledX = _xRes * _scale; // if it is a horizontal line if (begin[1] == end[1]) { for (int x = begin[0]; x < end[0]; x++) { int index = x + end[1] * scaledX; if (intensity > _offscreenBuffer[index]) _offscreenBuffer[index] = intensity; } return; } // if it is a vertical line if (begin[0] == end[0]) { int bottom = (begin[1] > end[1]) ? end[1] : begin[1]; int top = (begin[1] > end[1]) ? begin[1] : end[1]; for (int y = bottom; y < top; y++) { int index = begin[0] + y * scaledX; if (intensity > _offscreenBuffer[index]) _offscreenBuffer[index] = intensity; } return; } // else it is diagonal int slope = (begin[1] < end[1]) ? 1 : -1; int interval = end[0] - begin[0]; for (int x = 0; x <= interval; x++) { int index = begin[0] + x + (begin[1] + x * slope) * scaledX; if (intensity > _offscreenBuffer[index]) _offscreenBuffer[index] = intensity; } } ////////////////////////////////////////////////////////////////////// // find deepest depth from current root ////////////////////////////////////////////////////////////////////// void DAG::findDeepest(NODE* root, NODE*& deepest) { deepest = root; for (int x = 0; x < root->neighbors.size(); x++) { NODE* child = root->neighbors[x]; NODE* candidate = NULL; findDeepest(child, candidate); if (candidate->depth > deepest->depth) deepest = candidate; } } ////////////////////////////////////////////////////////////////////// // dump out line segments ////////////////////////////////////////////////////////////////////// void DAG::write(const char* filename) { // open file FILE* file; file = fopen(filename, "wb"); // write out total number of DAG nodes fwrite((void*)&_totalNodes, sizeof(int), 1, file); fwrite((void*)&_xRes, sizeof(int), 1, file); fwrite((void*)&_yRes, sizeof(int), 1, file); fwrite((void*)&_dx, sizeof(float), 1, file); fwrite((void*)&_dy, sizeof(float), 1, file); fwrite((void*)&_bottomHit, sizeof(int), 1, file); fwrite((void*)&_inputWidth, sizeof(int), 1, file); fwrite((void*)&_inputHeight, sizeof(int), 1, file); // write out nodes writeNode(_root, file); fclose(file); } ////////////////////////////////////////////////////////////////////// // read in line segments ////////////////////////////////////////////////////////////////////// void DAG::read(const char* filename) { // erase old DAG deleteNode(_root); // open file FILE* file; file = fopen(filename, "rb"); if (file == NULL) { cout << "ERROR: " << filename << " is invalid." << endl; exit(1); } // read in total number of DAG nodes fread((void*)&_totalNodes, sizeof(int), 1, file); fread((void*)&_xRes, sizeof(int), 1, file); fread((void*)&_yRes, sizeof(int), 1, file); fread((void*)&_dx, sizeof(float), 1, file); fread((void*)&_dy, sizeof(float), 1, file); fread((void*)&_bottomHit, sizeof(int), 1, file); fread((void*)&_inputWidth, sizeof(int), 1, file); fread((void*)&_inputHeight, sizeof(int), 1, file); // clear _hash for use _hash.clear(); // read in all the DAG nodes for (int x = 0; x <= _totalNodes; x++) readNode(file); fclose(file); if (_bottomHit != -1) buildLeader(_bottomHit); } ////////////////////////////////////////////////////////////////////// // write out a DAG node ////////////////////////////////////////////////////////////////////// void DAG::writeNode(NODE* root, FILE* file) { int x; // write out neighbors int numNeighbors = root->neighbors.size(); for (x = 0; x < numNeighbors; x++) writeNode(root->neighbors[x], file); // write out this node fwrite((void*)&(root->index), sizeof(int), 1, file); int parent = (root->parent == NULL) ? -1 : root->parent->index; fwrite((void*)&parent, sizeof(int), 1, file); fwrite((void*)&(root->leader), sizeof(bool), 1, file); fwrite((void*)&(root->secondary), sizeof(bool), 1, file); fwrite((void*)&(root->depth), sizeof(int), 1, file); fwrite((void*)&numNeighbors, sizeof(int), 1, file); for (x = 0; x < numNeighbors; x++) fwrite((void*)&(root->neighbors[x]->index), sizeof(int), 1, file); } ////////////////////////////////////////////////////////////////////// // read in a DAG node ////////////////////////////////////////////////////////////////////// void DAG::readNode(FILE* file) { // read in DAG data int index; int parent; bool leader; bool secondary; int depth; int numNeighbors; float potential; fread((void*)&index, sizeof(int), 1, file); fread((void*)&parent, sizeof(int), 1, file); fread((void*)&leader, sizeof(bool), 1, file); fread((void*)&secondary, sizeof(bool), 1, file); fread((void*)&depth, sizeof(int), 1, file); fread((void*)&numNeighbors, sizeof(int), 1, file); // create the node NODE* node = new NODE(index); node->leader = leader; node->secondary = secondary; node->depth = depth; // look up neighbors map::iterator iter; for (int x = 0; x < numNeighbors; x++) { // read in the child index int neighborIndex; fread((void*)&neighborIndex, sizeof(int), 1, file); // look up in hash table iter = _hash.find(neighborIndex); // push onto the neighbor vector node->neighbors.push_back((*iter).second); // set child's parent (*iter).second->parent = node; } // add to hash table _hash.insert(map::value_type(index, node)); // search for root node if (parent == -1) { node->parent = NULL; _root = node; } }