566 lines
17 KiB
C++
566 lines
17 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// 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<int, NODE*>::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<int, NODE*>::value_type(index, newNode));
|
|
}
|
|
else
|
|
{
|
|
// make the root
|
|
_root = new NODE(neighbor);
|
|
_hash.insert(map<int, NODE*>::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<int, NODE*>::value_type(index, newNode));
|
|
}
|
|
|
|
_totalNodes++;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// build the leader chain
|
|
//////////////////////////////////////////////////////////////////////
|
|
void DAG::buildLeader(int bottomHit)
|
|
{
|
|
_bottomHit = bottomHit;
|
|
|
|
// get pointer to bottommost node
|
|
map<int, NODE*>::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<int, NODE*>::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<int, NODE*>::value_type(index, node));
|
|
|
|
// search for root node
|
|
if (parent == -1)
|
|
{
|
|
node->parent = NULL;
|
|
_root = node;
|
|
}
|
|
}
|