Last active
October 11, 2017 18:20
-
-
Save kylemcdonald/7f414f4d9a70fc6a233eeebd27bb56eb to your computer and use it in GitHub Desktop.
Seene Viewer with C++ in openFrameworks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on https://github.com/detunized/seene-viewer | |
#include "ofMain.h" | |
inline bool ends_with(std::string const & value, std::string const & ending) | |
{ | |
if (ending.size() > value.size()) return false; | |
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); | |
} | |
class Seene { | |
private: | |
int readInt(ofBuffer buffer, int offset) { | |
return ((int*) (buffer.getData() + offset))[0]; | |
} | |
float readFloat(ofBuffer buffer, int offset) { | |
return ((float*) (buffer.getData() + offset))[0]; | |
} | |
public: | |
ofImage tex; | |
ofMesh mesh; | |
ofBuffer buffer; | |
int version; | |
int cameraWidth, cameraHeight; | |
float cameraFx, cameraFy, cameraK1, cameraK2; | |
int depthMapWidth, depthMapHeight; | |
int headerSize; | |
float* depthMap; | |
void load(string sceneFilename, string textureFilename="", bool rotateTexture=true) { | |
if(textureFilename != "") { | |
loadTexture(textureFilename, rotateTexture); | |
} | |
loadScene(sceneFilename); | |
buildMesh(); | |
} | |
void loadScene(string sceneFilename) { | |
buffer = ofBufferFromFile(sceneFilename); | |
ofLogVerbose("Seene") << "Loaded " << sceneFilename; | |
// read header | |
version = readInt(buffer, 0); | |
cameraWidth = readInt(buffer, 4); | |
cameraHeight = readInt(buffer, 8); | |
cameraFx = readFloat(buffer, 12); | |
cameraFy = readFloat(buffer, 16); | |
cameraK1 = readFloat(buffer, 20); | |
cameraK2 = readFloat(buffer, 24); | |
depthMapWidth = readInt(buffer, 28); | |
depthMapHeight = readInt(buffer, 32); | |
headerSize = 36; | |
if(version == 3) { | |
float unknownA = readFloat(buffer, 36); // e.g. 0.4 | |
float unknownB = readFloat(buffer, 40); // e.g. 110 | |
ofLogVerbose() << "Unknown " << unknownA << ", " << unknownB; | |
headerSize = 44; | |
} | |
depthMap = (float*) (buffer.getData() + headerSize); | |
ofLogVerbose("Seene") << "Version " << version; | |
ofLogVerbose("Seene") << "Camera " << cameraWidth << "x" << cameraHeight << | |
" fx: " << cameraFx << " " << | |
" fy: " << cameraFy << " " << | |
" k1: " << cameraK1 << " " << | |
" k2: " << cameraK2; | |
ofLogVerbose("Seene") << "Depth " << depthMapWidth << "x" << depthMapHeight; | |
// read camera | |
// var camera = new THREE.PerspectiveCamera( | |
// Math.atan2(0.495 * header.camera_width, header.camera_fx) * 360 / Math.PI, | |
// this.width / this.height, | |
// .01, | |
// 100 | |
// ); | |
} | |
void loadTexture(string textureFilename, bool rotateTexture=true) { | |
tex.load(textureFilename); | |
if(rotateTexture) { | |
tex.rotate90(3); | |
tex.update(); | |
} | |
} | |
void buildMesh() { | |
// read scene | |
float xk = cameraFx / cameraWidth; | |
float yk = cameraFy / cameraHeight; | |
mesh = ofMesh::plane(0, 0, depthMapWidth, depthMapHeight, OF_PRIMITIVE_TRIANGLES); | |
int i = 0; | |
// the depth image is stored with a -90 degree rotation | |
for (int y = 0; y < depthMapHeight; y++) { | |
for (int x = 0; x < depthMapWidth; x++) { | |
float depth = depthMap[i]; | |
mesh.setVertex(i, ofVec3f( | |
depth * ((x + 0.5) / depthMapWidth - 0.5) / xk, | |
depth * ((y + 0.5) / depthMapHeight - 0.5) / yk, | |
-(depth - 1) | |
)); | |
mesh.setTexCoord(i, tex.getTexture().getCoordFromPercent(x / (float) depthMapWidth, y / (float) depthMapHeight)); | |
i++; | |
} | |
} | |
} | |
void saveDepthMap(string filename) { | |
ofFloatPixels img; | |
img.allocate(depthMapWidth, depthMapHeight, OF_IMAGE_GRAYSCALE); | |
img.setFromPixels(depthMap, depthMapWidth, depthMapHeight, 1); | |
if(ends_with(filename, ".exr")) { | |
ofSaveImage(img, filename); | |
} else { | |
int n = depthMapWidth * depthMapHeight; | |
float* data = img.getData(); | |
float min = data[0], max = data[0]; | |
for(int i = 0; i < n; i++) { | |
float& p = data[i]; | |
min = p < min ? p : min; | |
max = p > max ? p : max; | |
} | |
float range = max - min; | |
for(int i = 0; i < n; i++) { | |
float& p = data[i]; | |
p -= min; | |
p /= range; | |
} | |
ofSaveImage(ofPixels(img), filename); | |
} | |
} | |
void draw() { | |
if(tex.isAllocated()) { | |
tex.bind(); | |
mesh.draw(); | |
tex.unbind(); | |
} else { | |
mesh.draw(); | |
} | |
} | |
}; | |
class ofApp : public ofBaseApp { | |
public: | |
Seene scene; | |
ofEasyCam cam; | |
void setup() { | |
ofSetLogLevel(OF_LOG_VERBOSE); | |
scene.loadScene("another.oemodel"); | |
scene.saveDepthMap("out.exr"); | |
scene.saveDepthMap("out.jpg"); | |
scene.saveDepthMap("out.png"); | |
scene.load("scene.oemodel", "texture.jpg"); | |
ofBackground(240); | |
} | |
void update() { | |
} | |
void draw() { | |
ofEnableDepthTest(); | |
cam.begin(); | |
float scale = 800; | |
ofScale(scale, -scale, scale); | |
ofRotate(90); | |
scene.draw(); | |
cam.end(); | |
} | |
}; | |
int main() { | |
ofSetupOpenGL(800, 800, OF_WINDOW); | |
ofRunApp(new ofApp()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment