Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added hps0 and psi2 parsers, updated makefile, added tests and tests …

…to Sundae.
  • Loading branch information...
commit af2f9a248a0029955893b7b81e0dc9e4a5cc6a3e 1 parent 81af4ab
Andor Salga authored
View
2  Makefile
@@ -13,7 +13,7 @@ create-release-files: create-release-docs create-user-docs minify create-example
# Minification strips out comments and most whitespace
minify: create-release-dir
- cat psapi.js ./libs/mjs.js ./parsers/asc.js ./parsers/pts.js ./parsers/psi.js > ./xbps-min/xbps-temp.js
+ cat psapi.js ./libs/mjs.js ./parsers/asc.js ./parsers/pts.js ./parsers/psi.js ./parsers/ply.js ./parsers/psi2.js ./parsers/hps0.js > ./xbps-min/xbps-temp.js
rm -fr tools-bin
mkdir tools-bin/
cc -o tools-bin/minifier tools/jsmin.c
View
BIN  clouds/mask_70K_n.psi
Binary file not shown
View
564 parsers/hps0.js
@@ -0,0 +1,564 @@
+/*
+ Copyright (c) 2010 Seneca College
+ MIT LICENSE
+*/
+/**
+ @class
+ If the hps0 files have normals, there will be
+
+ - 3 bytes for X
+ - 3 bytes for Y
+ - 3 bytes for Z
+
+ - 1 byte for Red
+ - 1 byte for Green
+ - 1 byte for Blue
+
+ If the hps0 files do NOT have normals, there will be
+
+ - 2 bytes for X
+ - 2 bytes for Y
+ - 2 bytes for Z
+
+ - 2 bytes for Red, Green and Blue
+
+ <pre>
+ <xml tags>
+ <that have relevant>
+ <information= about the file>
+ Binary Data...
+ (3 bytes for x, 3 bytes for y, 3 bytes for z and 3 bytes for rgb)
+ ...
+ ...
+ ...
+ ...
+ location and color data end for points
+ normal data start
+ (every 3 bytes is compressed normal data)
+ ...
+ ...
+ ...
+ <more tags>
+ <to close opening tags>
+ <and provide more information>
+
+ // HPS0
+ // <Level=0>
+ // <BinaryCloud>
+ // <Format=1>
+ // <NumPoints= 11158 0 >
+ // <SpotSize= 0.134696 >
+ // <Min= -24.1075 -28.9434 -16.8786 >
+ // <Max= -12.4364 -14.8525 -18.72375 >
+ // ...\
+ // ... }- binary data (vertices & colors)
+ // .../
+ // ...\
+ // ... }- possibly more binary data (normals)
+ // .../
+ // </Level=0>
+ // </PsCloudModel>
+
+</pre>
+
+*/
+var HPS0Parser = (function(){
+
+ /**
+ @private
+ */
+ function HPS0Parser(config){
+
+ var gotHeader = false;
+
+ var numTotalPoints;
+
+ // !!!
+ var normalsPresent = false;
+ var colorsPresent = true;
+
+ // If the PSI file has normals, this will be true.
+ var hasNormals = false;
+
+ // If the PSI file has normals, we'll have 9 bytes for XYZ
+ // and 3 for RGB. (12)
+ // If the PSI does NOT have normals, we'll have 6 bytes for XYZ
+ // and 2 for RGB. (8)
+ // So when we're streaming in the bytes, we'll need to know what
+ // parts are the vertices and which parts are the colors.
+ var byteIncrement;
+
+ // Values to be used in decompression of HPS0 files.
+ var diffX, diffY, diffZ;
+ var xMin, yMin, zMin;
+ var xMax, yMax, zMax;
+ var scaleX, scaleY, scaleZ;
+
+ const SFACTOR = 16777216; // 2^24
+ const NFACTOR = -0.5 + 1024; // 2^10
+
+ var startOfBin;
+ var last12Index;
+ var startOfNextChunk = 0;
+
+
+ // keep track if onprogress event handler was called to
+ // handle Chrome/WebKit vs. Firefox differences.
+ //
+ // Firefox will call onprogress zero or many times
+ // Chrome/WebKit will call onprogress one or many times
+ var onProgressCalled;
+
+
+ this.__defineGetter__("numTotalPoints", function(){
+ return numTotalPoints;
+ });
+
+
+ /**
+ */
+ var parseChunk = function(chunk){
+
+ // !! Fix this.
+ // This occurs over network connections, but not locally.
+ if(chunk){
+
+ var numVerts = chunk.length/byteIncrement;
+ var numBytes = chunk.length;
+
+ //
+ var verts, cols, norms;
+
+ // !!! COMMENT
+ if(onProgressCalled === true){
+
+ // !!! this needs to be changed.
+ // if colors are present, we know we're still
+ // dealing with vertices.
+ if(numVerts > 0 && colorsPresent){
+ // !!! only for debugging, remove on prduction
+ if(numVerts !== Math.floor(numVerts)){
+ console.log("invalid numVerts: " + numVerts);
+ numVerts = Math.floor(numVerts);
+ }
+ verts = new Float32Array(numVerts * 3);
+ cols = new Float32Array(numVerts * 3);
+ }
+
+ // parsing normal values, not sure the logic behind it (as it was never provided)
+ // we take 3 bytes and apply some bit shifting operations on it
+ // we then take the results and multiply it to some set values
+ // the normals are the resulting values
+ if(numBytes > 0 && normalsPresent){
+
+ if(numBytes !== Math.floor(numBytes)){
+ console.log('invalid num bytes');
+ }
+ norms = new Float32Array(numBytes);
+ parseNorms(chunk, numBytes, 0, norms);
+ }
+ // parsing xyz and rgb values, not sure behind the logic either
+ // 3 bytes are used for each x, y, z values
+ // each of the last 3 bytes of the 12 correspond to an rgb value
+ else{
+ var byteIdx = 0;
+ parseVertsCols(chunk, numBytes, byteIdx, verts, cols);
+ }
+ }
+
+ var attributes = {};
+ // ps vertex needs to be the first element!
+ if(verts){attributes["ps_Vertex"] = verts;}
+ if(cols){ attributes["ps_Color"] = cols;}
+ if(norms){ attributes["ps_Normal"] = norms;}
+
+ return attributes;
+ }
+ }
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset
+ */
+ var getByte = function(str, iOffset){
+ return str.charCodeAt(iOffset) & 0xFF;
+ }
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset
+ */
+ var getBytes2 = function(str, iOffset){
+ return ((getByte(str, iOffset + 1) << 8) + getByte(str, iOffset)) << 8;
+ };
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset - Must be an int.
+
+ @returns
+ */
+ var getBytes3 = function(str, iOffset){
+ return (((getByte(str, iOffset + 2) << 8) + getByte(str, iOffset + 1)) << 8) + getByte(str, iOffset);
+ };
+
+
+ /*
+
+ */
+ var parseVertsCols = function(chunk, numBytes, byteIdx, verts, cols){
+ var byte1, byte2, short;
+
+ for(var point = 0; point < numBytes/byteIncrement; byteIdx += byteIncrement, point++){
+ // If the PSI file has normals, there are 3 bytes for each component.
+ if(hasNormals){
+ verts[point*3 + 0] = (diffX * getBytes3(chunk, byteIdx )) / scaleX;
+ verts[point*3 + 1] = (diffY * getBytes3(chunk, byteIdx + 3)) / scaleY;
+ verts[point*3 + 2] = (diffZ * getBytes3(chunk, byteIdx + 6)) / scaleZ;
+
+ cols[point*3 + 0] = getByte(chunk, byteIdx + 9) / 255;
+ cols[point*3 + 1] = getByte(chunk, byteIdx + 10) / 255;
+ cols[point*3 + 2] = getByte(chunk, byteIdx + 11) / 255;
+ }
+ else{
+ verts[point*3 + 0] = (diffX * getBytes2(chunk, byteIdx )) / scaleX;
+ verts[point*3 + 1] = (diffY * getBytes2(chunk, byteIdx + 2)) / scaleY;
+ verts[point*3 + 2] = (diffZ * getBytes2(chunk, byteIdx + 4)) / scaleZ;
+
+ byte1 = getByte(chunk, byteIdx + 6);
+ byte2 = getByte(chunk, byteIdx + 7);
+
+ short = (byte2 << 8) + byte1;
+
+ cols[point*3] = (((short>>10) & 0x1F) << 3)/255;
+ cols[point*3 + 1] = (((short>>5) & 0x1F) << 3)/255;
+ cols[point*3 + 2] = ((short & 0x1F) << 3)/255;
+ }
+ }
+ };
+
+ /*
+ @param {String} chunk
+ @param {Number} numBytes
+ @param {Number} byteIdx
+ @param {ArrayBuffer} norms
+ */
+ var parseNorms = function(chunk, numBytes, byteIdx, norms){
+ var nzsign, nx11bits, ny11bits, ivalue;
+ var nvec = new Float32Array(3);
+
+ // Start reading the normals where we left off reading the
+ // vertex positions and colors.
+ // Each normal is 3 bytes.
+ for(var point = 0; byteIdx < numBytes; byteIdx += 3, point += 3){
+
+ ivalue = getBytes3(chunk, byteIdx);
+ nzsign = (ivalue >> 22) & 0x0001;
+ nx11bits = (ivalue) & 0x07ff;
+ ny11bits = (ivalue >> 11) & 0x07ff;
+
+ if(nx11bits >= 0 && nx11bits < 2048 && ny11bits >= 0 && ny11bits < 2048){
+
+ nvec[0] = (nx11bits/NFACTOR) - 1.0;
+ nvec[1] = (ny11bits/NFACTOR) - 1.0;
+
+ var nxnymag = nvec[0]*nvec[0] + nvec[1]*nvec[1];
+
+ // Clamp values.
+ nxnymag = Math.min(nxnymag, 1);
+ nxnymag = Math.max(nxnymag,-1);
+ nxnymag = 1 - nxnymag;
+
+ nxnymag = Math.min(nxnymag, 1);
+ nxnymag = Math.max(nxnymag,-1);
+
+ nvec[2] = Math.sqrt(nxnymag);
+
+ if (nzsign){
+ nvec[2] = -nvec[2];
+ }
+ var dNorm = nvec[0]*nvec[0] + nvec[1]*nvec[1] + nvec[2]*nvec[2];
+
+ dNorm = (dNorm > 0) ? Math.sqrt(dNorm) : 1;
+
+ norms[point] = nvec[0]/dNorm;
+ norms[point+1] = nvec[1]/dNorm;
+ norms[point+2] = nvec[2]/dNorm;
+ }
+ }
+ };
+
+
+
+ /**
+ */
+ var readHeader = function(textData){
+
+ // !!
+ // see if we have the entire Max tag, if we don't wait around
+ // until we do.
+ var maxTagIdx = textData.indexOf("<Max=");
+ if(maxTagIdx !== -1){
+ var endTag = textData.indexOf(">", maxTagIdx);
+ if(endTag == -1){
+ return;
+ }
+ }
+
+ var numPtsIdx = textData.indexOf("<NumPoints=");
+ endTagIdx = textData.indexOf(">", numPtsIdx);
+
+ // <NumPoints= 57507 2 0 57507 0 0 >
+ var numPtsValuesStr = textData.substring((numPtsIdx + "<NumPoints=".length), endTagIdx);
+ var numPtsValuesArr = numPtsValuesStr.split(" ");
+
+ // Multiply by 1 to convert to a Number type.
+ numTotalPoints = numPtsValuesArr[1] * 1;
+ // We can find out if there are normals by inspecting <NumPoints>
+ // <NumPoints= 11158 1 >
+ // <NumPoints= 11158 2 >
+ // If the second value is 0, the file does not contain normals.
+ if((numPtsValuesArr[2] * 1) !== 0){
+ hasNormals = true;
+ }
+
+ /// !!! var...
+ // posMinStr - lowest value in the file (used for decompression)
+ minIdx = textData.indexOf("<Min=");
+
+ endTagIdx = textData.indexOf(">", minIdx);
+ var temp = textData.substring((minIdx + "<Min=".length), endTagIdx);
+ var posMinArr = temp.split(" ");
+
+ // Multiply by 1 to convert to a Number type.
+ xMin = posMinArr[1] * 1;
+ yMin = posMinArr[2] * 1;
+ zMin = posMinArr[3] * 1;
+
+ // posMaxStr - highest value in the file (used for decompression)
+ maxIdx = textData.indexOf("<Max=");
+
+ endTagIdx = textData.indexOf(">", maxIdx);
+ var temp = textData.substring((maxIdx + "<Max=".length), endTagIdx);
+ var posMaxArr = temp.split(" ");
+
+ // Multiply by 1 to convert to a Number type.
+ xMax = posMaxArr[1] * 1;
+ yMax = posMaxArr[2] * 1;
+ zMax = posMaxArr[3] * 1;
+
+ bgnTag = textData.substring(maxIdx, endTagIdx + 1);
+
+ // !! fix me
+ startOfBin = textData.indexOf(">", maxIdx) + 3;
+
+ diffX = xMax - xMin;
+ diffY = yMax - yMin;
+ diffZ = zMax - zMin;
+
+ scaleX = SFACTOR + xMin;
+ scaleY = SFACTOR + yMin;
+ scaleZ = SFACTOR + zMin;
+
+ // If normals:
+ // 9 for XYZ
+ // 3 for RGB
+
+ // else:
+ // 6 for XYZ
+ // 2 for RGB
+ byteIncrement = hasNormals ? 12 : 8;
+
+ // If we got this far, we can start parsing values and we don't
+ // have to try running this function again.
+ gotHeader = true;
+ }
+
+
+
+
+
+
+
+
+
+ /*
+ // Checks if begin or end tags can be found using regex.
+ var binBeginIdx = textData.indexOf(bgnTag);
+ var infoEnd = textData.indexOf("</Level=");
+ // This contains our raw binary data.
+ // +2 bytes for offset values: \r\n
+ var binData = textData.substring(binBeginIdx + bgnTag.length + 2, infoEnd);
+ var numBytes = binData.length;
+ var attributes = {};
+ var verts = new Float32Array(numTotalPoints * 3);
+ var cols = new Float32Array(numTotalPoints * 3);
+ parseVertsCols_HPS0(binData, numBytes, 0, verts, cols);
+ // ps vertex needs to be the first element!
+ if(verts){attributes["ps_Vertex"] = verts;}
+ if(cols){ attributes["ps_Color"] = cols;}
+ var norms;
+ // Parse the normals if we have them.
+ if(hasNormals){
+ norms = new Float32Array(numTotalPoints * 3);
+ parseNorms_HPS0(binData, numBytes, numTotalPoints * byteIncrement, norms);
+ attributes["ps_Normal"] = norms;
+ }*/
+ /**
+ */
+ this.onload = function(textData){
+
+ // If we downloaded the entire file in one request.
+ if(!gotHeader){
+ readHeader(textData);
+
+ // Checks if begin or end tags can be found using regex.
+ var binBeginIdx = textData.indexOf(bgnTag);
+ var infoEnd = textData.indexOf("</Level=");
+
+ // This contains our raw binary data.
+ // +2 bytes for offset values: \r\n
+ var binData = textData.substring(binBeginIdx + bgnTag.length + 2, infoEnd);
+ var numBytes = binData.length;
+
+ var attributes = {};
+ var verts = new Float32Array(numTotalPoints * 3);
+ var cols = new Float32Array(numTotalPoints * 3);
+ parseVertsCols(binData, numBytes, 0, verts, cols);
+
+ // ps vertex needs to be the first element!
+ if(verts){attributes["ps_Vertex"] = verts;}
+ if(cols){ attributes["ps_Color"] = cols;}
+
+ var norms;
+
+ // Parse the normals if we have them.
+ if(hasNormals){
+ norms = new Float32Array(numTotalPoints * 3);
+ parseNorms(binData, numBytes, numTotalPoints * byteIncrement, norms);
+ attributes["ps_Normal"] = norms;
+ }
+ return attributes;
+ }
+
+
+ var infoEnd = textData.indexOf("</Level=");
+
+ var chunk = textData.substring(startOfNextChunk, infoEnd);
+
+ // If the file has normals as indicated at the start of the file.
+ if(hasNormals){
+ normalsPresent = true;
+ colorsPresent = false;
+ }
+ else{
+ chunk = textData.substring(AJAX.startOfNextChunk-1, infoEnd-1);
+ }
+
+ return parseChunk(chunk);
+ }
+
+ /**
+ */
+ this.onprogress = function(textData){
+
+ var chunkLength = textData.length;
+
+ // If this is the first call to onprogress, try to read in the header.
+ if(!gotHeader){
+ readHeader(textData);
+
+ // readHeader() will have attempted to read at least the <Min> and <Max>
+ // tags, if we aren't that far in the file, we'll need to try again.
+ if(!gotHeader){
+ return;
+ }
+
+ // If this is the first time we read the file, start reading the binary data
+ // at the start of the binary data rather than somewhere in the middle.
+ startOfNextChunk = infoStart = startOfBin;
+ }
+
+ // The attributes which will be returned.
+ var attr;
+
+ onProgressCalled = true;
+
+ // Try to find the <Level> and </Level=0> tags which means we would
+ // have all the data.
+ endTag = "</Level=";
+ var tagExists = textData.indexOf(bgnTag);
+ var infoEnd = textData.indexOf(endTag);
+ var infoStart;
+
+ // If the bgnTag exists then set the startOfNextChunk
+ // to the end of the bgnTag + 2 for offset values.
+ if(tagExists !== -1){
+ // +2 for offset values
+ tagLen = bgnTag.length + 2;
+ infoStart = tagExists + tagLen;
+ if(startOfNextChunk === 0){
+ startOfNextChunk = infoStart;
+ }
+ }
+
+ // Find the last multiple of 12 in the chunk
+ // this is because of the format shown at the top of this parser.
+ var last12 = Math.floor((chunkLength - infoStart) / byteIncrement);
+ last12Index = (last12 * byteIncrement) + infoStart;
+
+ // If the end tag was found.
+ if(infoEnd !== -1){
+ last12Index = infoEnd;
+ }
+
+ var totalPointsInBytes = (numTotalPoints * byteIncrement) + infoStart;
+
+ // Handles parsing up to the end of position and colors.
+ // Sets the next chunk at the start of normals.
+ if((totalPointsInBytes > startOfNextChunk) && (totalPointsInBytes < last12Index)){
+ var chunk = textData.substring(startOfNextChunk, totalPointsInBytes);
+
+ if(chunk.length > 0){
+ startOfNextChunk = totalPointsInBytes;
+ attr = parseChunk(chunk);
+ }
+ }
+
+ // Parse the normals.
+ else if((last12Index > totalPointsInBytes) && (startOfNextChunk >= totalPointsInBytes)){
+
+ var chunk = textData.substring(startOfNextChunk, last12Index);
+ normalsPresent = true;
+ colorsPresent = false;
+
+ if(chunk.length > 0){
+ startOfNextChunk = last12Index;
+ attr = parseChunk(chunk);
+ }
+ }
+
+ // Parse position and colors.
+ else{
+ var chunk = textData.substring(startOfNextChunk, last12Index);
+
+ // !! debug this
+ if(chunk.length > 0){
+ startOfNextChunk = last12Index;
+ attr = parseChunk(chunk);
+ }
+ }
+ return attr;
+ }
+
+
+ }// ctor
+
+ return HPS0Parser;
+}());
View
744 parsers/psi.js
@@ -7,53 +7,14 @@
proprietary files which have their data stored as a mixture of
ASCII and binary data.
- If the hps0 files have normals, there will be
-
- - 3 bytes for X
- - 3 bytes for Y
- - 3 bytes for Z
-
- - 1 byte for Red
- - 1 byte for Green
- - 1 byte for Blue
-
- If the hps0 files do NOT have normals, there will be
-
- - 2 bytes for X
- - 2 bytes for Y
- - 2 bytes for Z
-
- - 2 bytes for Red, Green and Blue
-
-<pre>
- <xml tags>
- <that have relevant>
- <information= about the file>
- Binary Data...
- (3 bytes for x, 3 bytes for y, 3 bytes for z and 3 bytes for rgb)
- ...
- ...
- ...
- ...
- location and color data end for points
- normal data start
- (every 3 bytes is compressed normal data)
- ...
- ...
- ...
- <more tags>
- <to close opening tags>
- <and provide more information>
-</pre>
-
- @version: 0.7
- @author: Mickael Medel
- asydik.wordpress.com
- Andor Salga
+ @version: 0.7
+ @author: Andor Salga
asalga.wordpress.com
-
+ Mickael Medel
+ asydik.wordpress.com
+
Created: February 2011
- Updated: June 2011
+ Updated: July 2011
*/
var PSIParser = (function() {
@@ -61,119 +22,9 @@ var PSIParser = (function() {
@private
*/
function PSIParser(config) {
-
- // Declare tags
- var bgnDocument = "<PsDocument>",
- endDocument = "</PsDocument>",
- bgnComposite = "<PsComposite>",
- endComposite = "</PsComposite>",
- bgnSubject = "<PsSubject",
- endSubject = "</PsSubject>",
- bgnInstance = "<PsInstance>",
- endInstance = "</PsInstance>",
- bgnEnv = "<PsEnvironment>",
- endEnv = "</PsEnvironment>",
- bgnName = "<Name>",
- endName = "</Name>",
- intMatIdStr = "<Identity>", // Internal Matrix Identity
- extMatIdStr = "<ExtIdentity>"; // External Matrix Identity
-
- var bgnCompList = "<PsList>",
- endCompList = "</PsList>",
- compositeModelStr = "CompositeModel:",
- instanceModelStr = "InstanceModel:",
- nurbSurfaceStr = "NurbSurface",
- nurbCurveStr = "NurbCurve",
- lineModelStr = "LineModel",
- quadMeshStr = "QuadMeshModel",
- triMeshStr = "TriMeshModel",
- cloudModelStr = "CloudModel",
- imgCloudModelStr = "ImageCloudModel",
- txtObjStr = "TextObject",
- imgObjStr = "ImageObject",
- soundObjStr = "SoundObject",
- octTreeModelStr = "OctTreeModel",
- openBracket = "<",
- closeBracket = ">",
- slashEndMark = "/",
- psPrefix = "Ps";
- // Shuffle Config Variable Strings
- var bgnShuffle = "<Shuffle>",
- endShuffle = "<\\Shuffle>",
- bgnTempLight = "<PsTempLight>",
- endTempLight = "<\\PsTempLight>",
- bgnTempLightVec = "<PsTempLightVec>",
- endTempLightVec = "<\\PsTempLightVec>",
- bgnGlblAmbLight = "<PsGlobalAmbientLight>",
- endGlblAmbLight = "<\\PsGlobalAmbientLight>",
- bgnLightType = "<PsLightType>",
- endLightType = "<\\PsLightType>";
-
- // Environment Variable Strings - <PsEnvironment>
- var posDataStr = "XYZData=",
- colDataStr = "RGBData=",
- normDataStr = "IJKData=",
- fileTypeStr = "FileType=",
- compStr = "Compression=",
- encStr = "Encryption=",
- wtrMrkStr = "WaterMark=",
- lenUnitStr = "LengthUnit=";
-
- // View Variable Strings
- var bgnViewDef = "<PointStream_View_Definition>",
- endViewDef = "</PointStream_View_Definition>",
- quartStr = "<Q", // Quarternion Matrix
- pivotStr = "<P", // Pivot
- transStr = "<T", // Translation
- angStr = "<A", // FOV Angle
- scrnSizeStr = "<S", // Screen Size
- bgColStr = "<B", // Background Color
- cv1Str = "<Cv1";
-
- // Material Variable Strings
- var bgnTemp1Mat = "<PsTemp1Material>",
- endTemp1Mat = "<\\PsTemp1Material>";
-
- // Parent Variable Strings
- var bgnParentTag = "<PsParent= '";
- endParentTag = "'>";
-
- // PsSubject Variables Strings
- var selStr = "Sel=",
- visStr = "Vis=",
- lockStr = "Lok=",
- actStr = "Act=";
-
- // Token Model Variable Strings
- var bgnLineModel = "<PsLineModel>",
- bgnCloudModel = "<PsCloudModel>",
- bgnImgCldModel = "<PsImageCloudModel>",
- bgnTriMeshModel = "<PsTriMeshModel>",
- bgnNurbCurve = "<PsNurbCurve>",
- bgnNurbSurface = "<PsNurbSurface>",
- bgnTextObject = "<PsTextObject>",
- bgnImageObject = "<PsImageObject>",
- bgnSoundObject = "<PsSoundObject>",
- bgnOctTreeModel = "<PsOctTreeModel>";
-
- // Level of Detail Variable Strings
- var numLvlStr = "<NumLevels=",
- scnMatStr = "<ScanMatrix:",
- bgnLvlStr = "<Level=",
- endLvlStr = "</Level=",
- binCloudStr = "<BinaryCloud>",
- ascCloudStr = "<AsciiCloud>",
- fmtStr = "<Format=",
- formatTag = "<Format=";
-
- var numPtStr = "<NumPoints=",
- sptSzStr = "<SpotSize=",
- posMinStr = "<Min=",
- posMaxStr = "<Max=",
- endXMLStr = ">";
-
- var undef;
+ //
+ var subParser;
var __empty_func = function(){};
@@ -186,20 +37,24 @@ var PSIParser = (function() {
const UNKNOWN = -1;
const XHR_DONE = 4;
const STARTED = 1;
+
+ const PSI2 = "psi2";
+ const HPS0 = "hps0";
var pathToFile = null;
var fileSize = 0;
- //
+ // Filetype can be hps0 or psi2.
+ var fileType;
+
+ // The number of point we parsed so far.
var numParsedPoints = 0;
+
+ // The total number of points in the point cloud.
var numTotalPoints = 0;
- var progress = 0;
- //
- var numValuesPerLine = -1;
- var normalsPresent = false;
- var colorsPresent = true;
- var layoutCode = UNKNOWN;
+ // Progress ranges from 0 to 1.
+ var progress = 0;
// Length of the arrays we'll be sending the library.
var BUFFER_SIZE = 30000;
@@ -213,73 +68,36 @@ var PSIParser = (function() {
var tempBufferN;
var tempBufferOffsetN = 0;
-
- //
- var parsedVerts = [];
- var parsedCols = [];
- var parsedNorms = [];
-
- var firstRun = true;
-
- // If the PSI file has normals, this will be true.
- var hasNormals = false;
-
- // If the PSI file has normals, we'll have 9 bytes for XYZ
- // and 3 for RGB.
- // If the PSI does NOT have normals, we'll have 6 bytes for XYZ
- // and 2 for RGB.
- // So when we're streaming in the bytes, we'll need to know what
- // parts are the vertices and which parts are the colors.
- //
- // Therefore this will either be 12 or 8.
- var byteIncrement;
-
- // values to be used in decompression of PSI
- var diffX, diffY, diffZ;
- var scaleX, scaleY, scaleZ;
- const SFACTOR = Math.pow(2, 24);
- const NFACTOR = -0.5 + Math.pow(2, 10);
-
- //
- var bgnTag = "";
- var endTag = "";
- var tagExists;
- var endTagExists;
- // keep track if onprogress event handler was called to
- // handle Chrome/WebKit vs. Firefox differences.
- //
- // Firefox will call onprogress zero or many times
- // Chrome/WebKit will call onprogress one or many times
- var onProgressCalled = false;
var AJAX = null;
-
- /**
- */
- var getByte = function(str, iOffset){
- return str.charCodeAt(iOffset) & 0xFF;
- }
-
/**
@private
+ Get the type of PSI this file is. Can be HPS0, PSI2, etc.
*/
- var getBytes2 = function(str, iOffset){
- return ((getByte(str, iOffset + 1) << 8) + getByte(str, iOffset)) << 8;
- };
+ var getFileType = function(textData){
+
+ // If we don't have enough bytes to find out what type of file it is,
+ // we'll have to wait until the next xhr request.
+ if(textData.length < 6){
+ return;
+ }
+
+ var type = textData.substring(0, 4);
+ if(type === "psi2"){
+ fileType = PSI2;
+ subParser = new PSI2Parser();
+ }
+ else{
+ var type = textData.substring(0, 6);
+ if(type === "<hps0>"){
+ fileType = HPS0;
+ subParser = new HPS0Parser();
+ }
+ }
+ }
+
- /**
- @private
-
- @param {String} str
- @param {Number} iOffset - Must be an int.
-
- @returns
- */
- var getBytes3 = function(str, iOffset){
- return (((getByte(str, iOffset + 2) << 8) + getByte(str, iOffset + 1)) << 8) + getByte(str, iOffset);
- };
-
/**
@private
@@ -393,7 +211,9 @@ var PSIParser = (function() {
@returns {Number}
*/
this.__defineGetter__("numTotalPoints", function(){
- return numTotalPoints;
+ if(subParser){
+ subParser.numTotalPoints;
+ }
});
/**
@@ -425,13 +245,13 @@ var PSIParser = (function() {
/**
@param {String} pathToFile
+
+ The library is responsible for calling this method.
*/
this.load = function(path){
pathToFile = path;
AJAX = new XMLHttpRequest();
- AJAX.startOfNextChunk = 0;
- AJAX.last12Index = 0;
// put a reference to the parser in the AJAX object
// so we can give the library a reference to the
@@ -446,94 +266,7 @@ var PSIParser = (function() {
AJAX.onloadstart = function(evt){
start(AJAX.parser);
};
-
- /*
-
- */
- AJAX.parseVertsCols = function(chunk, numBytes, byteIdx, verts, cols){
- var byte1;
- var byte2;
- var short;
-
- for(var point = 0; point < numBytes/byteIncrement; byteIdx += byteIncrement, point++){
- // If the PSI file has normals, there are 3 bytes for each component.
- if(hasNormals){
- verts[point*3 + 0] = (diffX * getBytes3(chunk, byteIdx )) / scaleX;
- verts[point*3 + 1] = (diffY * getBytes3(chunk, byteIdx + 3)) / scaleY;
- verts[point*3 + 2] = (diffZ * getBytes3(chunk, byteIdx + 6)) / scaleZ;
-
- cols[point*3 + 0] = getByte(chunk, byteIdx + 9) / 255;
- cols[point*3 + 1] = getByte(chunk, byteIdx + 10) / 255;
- cols[point*3 + 2] = getByte(chunk, byteIdx + 11) / 255;
- }
- else{
- verts[point*3 + 0] = (diffX * getBytes2(chunk, byteIdx )) / scaleX;
- verts[point*3 + 1] = (diffY * getBytes2(chunk, byteIdx + 2)) / scaleY;
- verts[point*3 + 2] = (diffZ * getBytes2(chunk, byteIdx + 4)) / scaleZ;
-
- byte1 = getByte(chunk, byteIdx + 6);
- byte2 = getByte(chunk, byteIdx + 7);
-
- short = (byte2 << 8) + byte1;
-
- cols[point*3 +0] = (((short>>10) & 0x1F) << 3)/255;
- cols[point*3 +1] = (((short>>5) & 0x1F) << 3)/255;
- cols[point*3 +2] = ((short & 0x1F) << 3)/255;
- }
- }
- };
-
- /*
- @param {String} chunk
- @param {Number} numBytes
- @param {Number} byteIdx
- @param {ArrayBuffer} norms
- */
- AJAX.parseNorms = function(chunk, numBytes, byteIdx, norms){
- var nzsign, nx11bits, ny11bits, ivalue;
- var nvec = new Float32Array(3);
-
- // Start reading the normals where we left off reading the
- // vertex positions and colors.
- // Each normal is 3 bytes.
- for(var point = 0; byteIdx < numBytes; byteIdx += 3, point += 3){
-
- ivalue = getBytes3(chunk, byteIdx);
- nzsign = (ivalue >> 22) & 0x0001;
- nx11bits = (ivalue) & 0x07ff;
- ny11bits = (ivalue >> 11) & 0x07ff;
- if(nx11bits >= 0 && nx11bits < 2048 && ny11bits >= 0 && ny11bits < 2048){
-
- nvec[0] = (nx11bits/NFACTOR) - 1.0;
- nvec[1] = (ny11bits/NFACTOR) - 1.0;
-
- var nxnymag = nvec[0]*nvec[0] + nvec[1]*nvec[1];
-
- // Clamp values.
- nxnymag = Math.min(nxnymag, 1);
- nxnymag = Math.max(nxnymag,-1);
- nxnymag = 1 - nxnymag;
-
- nxnymag = Math.min(nxnymag, 1);
- nxnymag = Math.max(nxnymag,-1);
-
- nvec[2] = Math.sqrt(nxnymag);
-
- if (nzsign){
- nvec[2] = -nvec[2];
- }
- var dNorm = nvec[0]*nvec[0] + nvec[1]*nvec[1] + nvec[2]*nvec[2];
-
- dNorm = (dNorm > 0) ? Math.sqrt(dNorm) : 1;
-
- norms[point] = nvec[0]/dNorm;
- norms[point+1] = nvec[1]/dNorm;
- norms[point+2] = nvec[2]/dNorm;
- }
- }
- };
-
/*
Occurs exactly once, when the file is done being downloaded.
@@ -543,108 +276,47 @@ var PSIParser = (function() {
@param {} evt
*/
AJAX.onload = function(evt){
-
var textData = AJAX.responseText;
- var chunkLength = textData.length;
// If we downloaded the file in one request.
- if(firstRun){
+ if(!fileType){
- // First, read in the important bounding box data.
- AJAX.firstLoad(textData);
-
- // first get the number of points in the cloud
- // <NumPoints= 11158 2 0 11158 0 0 >\r\n<Spot
- // <NumPoints= 11158 0 >
-
- // Find the start and end tags for NumPoints
- var numPointsTagIndex = textData.indexOf(numPtStr);
- var numPointsTagEndIndex = textData.indexOf(">", numPointsTagIndex+1);
-
- var numPointsContents = textData.substring(numPointsTagIndex, numPointsTagEndIndex+1);
-
- // Set the parser's attribute
- // Multiply by 1 to convert to a Number type
- // ["<NumPoints=", "11158", "0", ">"]
- numParsedPoints = numTotalPoints = numPointsContents.split(" ")[1] * 1;
-
- hasNormals = numPointsContents.split(" ")[2] * 1 == 0 ? false : true;
- // Read the position and color data
+ // Depending on the filetype, we'll have to instantiate different sub parsers.
+ getFileType(textData);
- // <Level=0>
- // <BinaryCloud>
- // <Format=1>
- // <NumPoints= 11158 0 >
- // <SpotSize= 0.134696 >
- // <Min= -24.1075 -28.9434 -16.8786 >
- // <Max= -12.4364 -14.8525 -18.72375 >
- // ...\
- // ... }- binary data (vertices & colors)
- // .../
- // ...\
- // ... }- possibly more binary data (normals)
- // .../
- // </Level=0>
- // </PsCloudModel>
-
- // Checks if begin or end tags can be found using regex.
- var tagExists = textData.indexOf(bgnTag);
- var infoEnd = textData.indexOf(endLvlStr);
- var infoStart;
+ var attributes = subParser.onload(textData);
- // If the bgnTag exists then set the startOfNextChunk
- // to the end of the bgnTag + 2 for offset values.
- if(tagExists !== -1){
- // +2 bytes for offset values 0D0A
- tagLen = bgnTag.length + 2;
- infoStart = tagExists + tagLen;
- }
-
- // This contains our raw binary data.
- var binData = textData.substring(infoStart, infoEnd);
-
- var numBytes = binData.length;
-
- var verts = new Float32Array(numTotalPoints * 3);
- var cols = new Float32Array(numTotalPoints * 3);
- AJAX.parseVertsCols(binData, numBytes, 0, verts, cols);
-
- // Parse the normals if we have them.
- var norms;
- if(hasNormals){
- norms = new Float32Array(numTotalPoints * 3);
- AJAX.parseNorms(binData, numBytes, numTotalPoints * byteIncrement, norms);
- }
+ numParsedPoints = numTotalPoints = subParser.numTotalPoints;
+ parse(AJAX.parser, attributes);
- var attributes = {};
- if(verts){attributes["ps_Vertex"] = verts;}
- if(cols){ attributes["ps_Color"] = cols;}
- if(norms){attributes["ps_Normal"] = norms;}
-
// Indicate parsing is done. Ranges from 0 to 1.
progress = 1;
- parse(AJAX.parser, attributes);
end(AJAX.parser);
-
return;
}
-
+
// If we didn't get the entire file in one request, continue on...
- var infoEnd = textData.indexOf(endLvlStr);
+ var attr = subParser.onload(textData);
+
+ numParsedPoints = numTotalPoints = subParser.numTotalPoints;
- var chunk = textData.substring(AJAX.startOfNextChunk, infoEnd);
+ if(attr.ps_Vertex){
+ var o = partitionArray(attr.ps_Vertex, tempBufferV, tempBufferOffsetV, 1);
+ tempBufferV = o.buffer;
+ tempBufferOffsetV = o.offset;
+ }
- // If the file has normals as indicated at the start of the file.
- if(hasNormals){
- normalsPresent = true;
- colorsPresent = false;
-
+ if(attr.ps_Color){
+ var o = partitionArray(attr.ps_Color, tempBufferC, tempBufferOffsetC, 2);
+ tempBufferC = o.buffer;
+ tempBufferOffsetC = o.offset;
}
- // else{
- // chunk = textData.substring(AJAX.startOfNextChunk-1, infoEnd-1);
- // }
- AJAX.parseChunk(chunk);
+ if(attr.ps_Normal){
+ var o = partitionArray(attr.ps_Normal, tempBufferN, tempBufferOffsetN, 3);
+ tempBufferN = o.buffer;
+ tempBufferOffsetN = o.offset;
+ }
// Get the last remaining bits from the temp buffers
// and parse those too.
@@ -668,178 +340,22 @@ var PSIParser = (function() {
}
progress = 1;
-
end(AJAX.parser);
}
/**
@private
- */
- AJAX.parseChunk = function(chunk){
-
- // !! Fix this.
- // This occurs over network connections, but not locally.
- if(chunk){
-
- var numVerts = chunk.length/byteIncrement;
- var numBytes = chunk.length;
-
- //
- var verts, cols, norms;
-
- // !!! COMMENT
- if(onProgressCalled === true){
-
- // !!! this needs to be changed.
- // if colors are present, we know we're still
- // dealing with vertices.
- if(numVerts > 0 && colorsPresent){
- // !!! only for debugging, remove on prduction
- if(numVerts !== Math.floor(numVerts)){
- console.log("invalid numVerts: " + numVerts);
- numVerts = Math.floor(numVerts);
- }
- verts = new Float32Array(numVerts * 3);
- cols = new Float32Array(numVerts * 3);
- }
-
- // parsing normal values, not sure the logic behind it (as it was never provided)
- // we take 3 bytes and apply some bit shifting operations on it
- // we then take the results and multiply it to some set values
- // the normals are the resulting values
- if(numBytes > 0 && normalsPresent){
-
- if(numBytes !== Math.floor(numBytes)){
- console.log('invalid num bytes');
- }
- norms = new Float32Array(numBytes);
- AJAX.parseNorms(chunk, numBytes, 0, norms);
- }
- // parsing xyz and rgb values, not sure behind the logic either
- // 3 bytes are used for each x, y, z values
- // each of the last 3 bytes of the 12 correspond to an rgb value
- else{
- var byteIdx = 0;
- AJAX.parseVertsCols(chunk, numBytes, byteIdx, verts, cols);
- }
- }
-
- if(verts){
- var o = partitionArray(verts, tempBufferV, tempBufferOffsetV, 1);
- tempBufferV = o.buffer;
- tempBufferOffsetV = o.offset;
- }
- if(cols){
- var o = partitionArray(cols, tempBufferC, tempBufferOffsetC, 2);
- tempBufferC = o.buffer;
- tempBufferOffsetC = o.offset;
- }
- if(norms){
- var o = partitionArray(norms, tempBufferN, tempBufferOffsetN, 3);
- tempBufferN = o.buffer;
- tempBufferOffsetN = o.offset;
- }
- }
- };
-
- /**
- @private
- */
- AJAX.firstLoad = function(textData){
- var temp;
-
- var xMax, xMin, yMax;
- var yMin, zMax, zMin;
-
- // numPtStr - number of points in the file
- tagExists = textData.indexOf(numPtStr);
- if(tagExists !== -1){
- endTagExists = textData.indexOf(endXMLStr, tagExists);
- temp = textData.substring((tagExists + numPtStr.length), endTagExists);
- var numPtArr = temp.split(" ");
-
- // Multiply by 1 to convert to a Number type.
- numTotalPoints = numPtArr[1] * 1;
-
- // We can find out if there are normals by inspecting <NumPoints>
- // <NumPoints= 6 1 >
- // <NumPoints= 6 2 >
- // If the second value is 0, the file does not contain normals.
- if((numPtArr[2] * 1) !== 0){
- hasNormals = true;
- }
- }
-
- // posMinStr - lowest value in the file (used for decompression)
- tagExists = textData.indexOf(posMinStr);
-
- if(tagExists !== -1){
- endTagExists = textData.indexOf(endXMLStr, tagExists);
- temp = textData.substring((tagExists + posMinStr.length), endTagExists);
- var posMinArr = temp.split(" ");
-
- // Multiply by 1 to convert to a Number type.
- xMin = posMinArr[1] * 1;
- yMin = posMinArr[2] * 1;
- zMin = posMinArr[3] * 1;
- }
-
- // posMaxStr - highest value in the file (used for decompression)
- tagExists = textData.indexOf(posMaxStr);
-
- if(tagExists !== -1){
- endTagExists = textData.indexOf(endXMLStr, tagExists);
- temp = textData.substring((tagExists + posMaxStr.length), endTagExists);
- var posMaxArr = temp.split(" ");
-
- // Multiply by 1 to convert to a Number type.
- xMax = posMaxArr[1] * 1;
- yMax = posMaxArr[2] * 1;
- zMax = posMaxArr[3] * 1;
-
- bgnTag = textData.substring(tagExists, (endTagExists + 1));
-
- diffX = xMax - xMin;
- diffY = yMax - yMin;
- diffZ = zMax - zMin;
-
- scaleX = SFACTOR + xMin;
- scaleY = SFACTOR + yMin;
- scaleZ = SFACTOR + zMin;
-
- // If normals:
- // 9 for XYZ
- // 3 for RGB
-
- // else:
- // 6 for XYZ
- // 2 for RGB
- byteIncrement = hasNormals ? 12 : 8;
-
- // If we got this far, we can start parsing values and we don't
- // have to try running this function again.
- firstRun = false;
- }
-
- // There is a chance that in the first XHR request we didn't get
- // as far as the <Max> tag. Since <Min> and <Max> are necessary to
- // read before parsing the file, force the PSI parser to wait for
- // the next request and try again.
- else{
- firstRun = true;
- }
- }
-
- /**
- @private
On Firefox/Minefield, this will occur zero or many times
On Chrome/WebKit this will occur one or many times
*/
AJAX.onprogress = function(evt){
-
- //
- onProgressCalled = true;
+ var textData = AJAX.responseText;
+
+ // If there is nothing to parse, wait until there is.
+ if(!textData || textData.length < 6){
+ return;
+ }
// Update the file's progress.
if(evt.lengthComputable){
@@ -847,87 +363,31 @@ var PSIParser = (function() {
progress = evt.loaded/evt.total;
}
- // If we have something to actually parse.
- if(AJAX.responseText){
- var textData = AJAX.responseText;
- var chunkLength = textData.length;
-
- // If this is the first call to onprogress.
- if(firstRun){
- AJAX.firstLoad(textData);
- // firstLoad() will have attempted to read at least the <Min> and <Max>
- // tags, if we aren't that far in the file, we'll need to try again
- if(firstRun){
- return;
- }
- }
-
- // Try to find the <Level> and </Level=0> tags which means we would
- // have all the data.
- endTag = endLvlStr;
- tagExists = textData.indexOf(bgnTag);
- var infoEnd = textData.indexOf(endTag);
- var infoStart;
-
- // If the bgnTag exists then set the startOfNextChunk
- // to the end of the bgnTag + 2 for offset values.
- if(tagExists !== -1){
- // +2 for offset values
- tagLen = bgnTag.length + 2;
- infoStart = tagExists + tagLen;
- if(AJAX.startOfNextChunk === 0){
- AJAX.startOfNextChunk = infoStart;
- }
- }
-
- // Find the last multiple of 12 in the chunk
- // this is because of the format shown at the top of this parser.
- var last12 = Math.floor((chunkLength - infoStart) / byteIncrement);
- AJAX.last12Index = (last12 * byteIncrement) + infoStart;
-
- // If the end tag was found.
- if(infoEnd !== -1){
- AJAX.last12Index = infoEnd;
- }
-
- var totalPointsInBytes = (numTotalPoints * byteIncrement) + infoStart;
-
- // Handles parsing up to the end of position and colors.
- // Sets the next chunk at the start of normals.
- if((totalPointsInBytes > AJAX.startOfNextChunk) && (totalPointsInBytes < AJAX.last12Index)){
- var chunk = textData.substring(AJAX.startOfNextChunk, totalPointsInBytes);
-
- if(chunk.length > 0){
- AJAX.startOfNextChunk = totalPointsInBytes;
- AJAX.parseChunk(chunk);
- }
- }
-
- // Parse the normals.
- else if((AJAX.last12Index > totalPointsInBytes) && (AJAX.startOfNextChunk >= totalPointsInBytes)){
- var chunk = textData.substring(AJAX.startOfNextChunk, AJAX.last12Index);
- normalsPresent = true;
- colorsPresent = false;
-
- if(chunk.length > 0){
- AJAX.startOfNextChunk = AJAX.last12Index;
- AJAX.parseChunk(chunk);
- }
- }
-
- // Parse position and colors.
- else{
- var chunk = textData.substring(AJAX.startOfNextChunk, AJAX.last12Index);
-
- // !! debug this
- if(chunk.length > 0){
- AJAX.startOfNextChunk = AJAX.last12Index;
- AJAX.parseChunk(chunk);
- }
-
- }
- }// AJAX.responseText
- };// onprogress
+ // If this is the first call to onprogress, get the file type.
+ if(!fileType){
+ getFileType(textData);
+ }
+
+ var attr = subParser.onprogress(textData);
+
+ if(attr.ps_Vertex){
+ var o = partitionArray(attr.ps_Vertex, tempBufferV, tempBufferOffsetV, 1);
+ tempBufferV = o.buffer;
+ tempBufferOffsetV = o.offset;
+ }
+
+ if(attr.ps_Color){
+ var o = partitionArray(attr.ps_Color, tempBufferC, tempBufferOffsetC, 2);
+ tempBufferC = o.buffer;
+ tempBufferOffsetC = o.offset;
+ }
+
+ if(attr.ps_Normal){
+ var o = partitionArray(attr.ps_Normal, tempBufferN, tempBufferOffsetN, 3);
+ tempBufferN = o.buffer;
+ tempBufferOffsetN = o.offset;
+ }
+ };
// This line is required since we are parsing binary data.
if(AJAX.overrideMimeType){
View
289 parsers/psi2.js
@@ -0,0 +1,289 @@
+/*
+ Copyright (c) 2010 Seneca College
+ MIT LICENSE
+*/
+/**
+ @class This is a sub parser used to parse PSI2 files.
+
+ @author: Andor Salga
+ asalga.wordpress.com
+*/
+var PSI2Parser = (function(){
+
+ /**
+ @private
+ */
+ function PSI2Parser(config){
+ var startOfNextChunk;
+ var last8Index;
+
+ // The number of bytes in the header, also where the binary data begins.
+ var headerLength = 0;
+
+ // The index where the vertices and colors end.
+ var endOfVertsCols;
+
+ // If the PSI file has normals, this will be true.
+ var hasNormals = false;
+
+ var numTotalPoints = 0;
+ var numParsedPoints = 0;
+ var parsingNormals = false;
+
+ // values to be used in decompression of PSI
+ var diffX, diffY, diffZ;
+ var xMin, yMin, zMin;
+ var xMax, yMax, zMax;
+ var gotHeader = false;
+
+ this.__defineGetter__("numTotalPoints", function(){
+ return numTotalPoints;
+ });
+
+ /**
+ */
+ var parseVertsCols = function(chunk, numBytes, byteIdx, verts, cols){
+ var byte1, byte2, short;
+
+ var numPoints = numBytes/8;
+
+ for(var point = 0; point < numPoints; byteIdx += 8, point++){
+ verts[point*3 + 0] = (diffX * getBytes2(chunk, byteIdx ) / (32767*1000)) + xMin;
+ verts[point*3 + 1] = (diffY * getBytes2(chunk, byteIdx + 2) / (32767*1000)) + yMin;
+ verts[point*3 + 2] = (diffZ * getBytes2(chunk, byteIdx + 4) / (32767*1000)) + zMin;
+
+ byte1 = getByte(chunk, byteIdx + 6);
+ byte2 = getByte(chunk, byteIdx + 7);
+
+ short = (byte2 << 8) + byte1;
+
+ cols[point*3] = (((short>>10) & 0x1F) << 3)/255;
+ cols[point*3 + 1] = (((short>>5) & 0x1F) << 3)/255;
+ cols[point*3 + 2] = ((short & 0x1F) << 3)/255;
+ }
+ };
+
+ /**
+ */
+ var parseNorms = function(chunk, numBytes, byteIdx, norms){
+ var i,j,k,mag;
+ for(var point = 0; byteIdx < numBytes; byteIdx += 3, point += 3){
+ i = getByte(chunk, byteIdx )/127 -1;
+ j = getByte(chunk, byteIdx+1)/127 -1;
+ k = getByte(chunk, byteIdx+2)/127 -1;
+
+ mag = Math.sqrt(i*i + j*j + k*k);
+
+ norms[point] = i/mag;
+ norms[point+1] = j/mag;
+ norms[point+2] = k/mag
+ }
+ };
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset
+ */
+ var getByte = function(str, iOffset){
+ return str.charCodeAt(iOffset) & 0xFF;
+ }
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset
+ */
+ var getBytes2 = function(str, iOffset){
+ return ((getByte(str, iOffset + 1) << 8) + getByte(str, iOffset)) << 8;
+ };
+
+ /**
+ @private
+
+ @param {String} str
+ @param {Number} iOffset - Must be an int.
+
+ @returns
+ */
+ var getBytes3 = function(str, iOffset){
+ return (((getByte(str, iOffset + 2) << 8) + getByte(str, iOffset + 1)) << 8) + getByte(str, iOffset);
+ };
+
+ /**
+ */
+ var readHeader = function(textData){
+
+ // If we couldn't find the specular tag, we know we aren't
+ // at the numPoints tag yet
+ if(textData.indexOf("Specular") === -1){
+ return;
+ }
+
+ // If we found a NoNormals string, turn off normals.
+ var noNormalsIdx = textData.indexOf("NoNormals");
+ hasNormals = noNormalsIdx === -1 ? true : false;
+
+ // !!
+ var headerStr = textData.substring(0, 250);
+ var headerArr = headerStr.split("\r\n");
+
+ // When split, the header will look something like this:
+ // [0] = "psi2:FrozenNormals|SingleCloud"
+ // [1] = "-73.947 -106.653 -56.3939"
+ // [2] = "88.7955 106.598 37.6935"
+ // [3] = "69298 Points: SpotSize= 1.43808 : Specular= 18 1"
+ // [4] = "F788,5>\0\b\uF7D0\\^,\uF7CD<!\f" <-- our binary data
+
+ for(var i = 0; i < 4; i++){
+ // +2 for \r\n
+ headerLength += (headerArr[i].length + 2);
+ }
+
+ var min = headerArr[1].split(" ");
+ var max = headerArr[2].split(" ");
+
+ numTotalPoints = headerArr[3].split(" ")[0] * 1;
+
+ // Since this is so frequently used, calculate it once here.
+ endOfVertsCols = numTotalPoints * 8 + headerLength;
+
+ // Multiply by 1 to convert to a Number type.
+ xMin = min[0] * 1;
+ yMin = min[1] * 1;
+ zMin = min[2] * 1;
+
+ xMax = max[0] * 1;
+ yMax = max[1] * 1;
+ zMax = max[2] * 1;
+
+ diffX = xMax - xMin;
+ diffY = yMax - yMin;
+ diffZ = zMax - zMin;
+
+ gotHeader = true;
+ }
+
+ /**
+ This will be called when the file is done being downloaded.
+ */
+ this.onload = function(textData){
+ // We got the entire file in one request.
+ if(!gotHeader){
+ readHeader(textData);
+
+ numParsedPoints = numTotalPoints;
+
+ // This contains our raw binary data.
+ var binData = textData.substring(headerLength, endOfVertsCols);
+
+ var attributes = {};
+ var verts = new Float32Array(numTotalPoints * 3);
+ var cols = new Float32Array(numTotalPoints * 3);
+
+ parseVertsCols(binData, binData.length, 0, verts, cols);
+
+ attributes["ps_Vertex"] = verts;
+ attributes["ps_Color"] = cols;
+
+ // Parse the normals if we have them.
+ if(hasNormals){
+ // 5 for "end\r\n"
+ var rawNormalData = textData.substring(endOfVertsCols, textData.length-5);
+
+ var norms = new Float32Array(numTotalPoints * 3);
+ parseNorms(rawNormalData, rawNormalData.length, 0, norms);
+ attributes["ps_Normal"] = norms;
+ }
+ return attributes;
+ }
+
+ return this.onprogress(textData);
+ }
+
+ /**
+ */
+ this.onprogress = function(textData){
+
+ chunkLength = textData.length;
+
+ // If this is the first time this is getting called,
+ // read the header data.
+ if(!gotHeader){
+ readHeader(textData);
+
+ if(!gotHeader){
+ return;
+ }
+
+ startOfNextChunk = headerLength;
+ }
+
+ var attr;
+ var verts, cols, norms;
+
+ if(!parsingNormals){
+ var last8 = Math.floor((chunkLength - headerLength) / 8);
+ last8Index = (last8 * 8) + headerLength;
+ }
+ else{
+ // Consider changing this since this could be present in the binary data
+ // somewhere.
+ if(textData.indexOf("End\r\n") !== -1){
+ last8Index = chunkLength - 5;
+ }
+ else{
+ var last8 = Math.floor((chunkLength - endOfVertsCols) /3);
+ last8Index = (last8*3) + endOfVertsCols;
+ }
+ }
+
+ // If we are still only reading vertices and colors.
+ if(chunkLength < endOfVertsCols){
+ var chunk = textData.substring(startOfNextChunk, last8Index);
+
+ // We'll start parsing in the next call where we left off.
+ startOfNextChunk = last8Index;
+
+ var numVerts = chunk.length/8;
+ var verts = new Float32Array(numVerts * 3);
+ var cols = new Float32Array(numVerts * 3);
+
+ parseVertsCols(chunk, chunk.length, 0, verts, cols);
+ }
+
+ // If we're in the middle of parsing vertices, colors and normals
+ else if(chunkLength > endOfVertsCols && startOfNextChunk < endOfVertsCols){
+ var chunk = textData.substring(startOfNextChunk, endOfVertsCols);
+
+ parsingNormals = true;
+ // We'll start parsing in the next call where we left off.
+ startOfNextChunk = endOfVertsCols;
+
+ var numVerts = chunk.length/8;
+ verts = new Float32Array(numVerts * 3);
+ cols = new Float32Array(numVerts * 3);
+
+ parseVertsCols(chunk, chunk.length, 0, verts, cols);
+ }
+
+ if(startOfNextChunk >= endOfVertsCols){
+ var chunk = textData.substring(startOfNextChunk, last8Index);
+ startOfNextChunk = last8Index;
+
+ norms = new Float32Array(chunk.length);
+ parseNorms(chunk, chunk.length, 0, norms);
+ }
+
+ var attributes = {};
+ if(verts){attributes["ps_Vertex"] = verts;}
+ if(cols){attributes["ps_Color"] = cols;}
+ if(norms){attributes["ps_Normal"] = norms;}
+ return attributes;
+ }
+ }// ctor
+
+ return PSI2Parser;
+}());
View
20 tests/psi2_parser/index.html
@@ -0,0 +1,20 @@
+<html>
+
+ <head>
+ <link rel="stylesheet" type="text/css" href="../style.css" />
+ <script src="../../xbps.js"></script>
+ <script src="test.js"></script>
+ </head>
+
+ <body onLoad="start();">
+
+ <h1><a href="http://zenit.senecac.on.ca/wiki/index.php/XB_PointStream">XB PointStream</a> PSI Parser</h1>
+
+ <p>
+ Test the PSI Parser<br />
+ <br />
+ <canvas id="canvas" width="500" height="500"></canvas><br />
+ </p>
+
+ </body>
+</html>
View
53 tests/psi2_parser/test.js
@@ -0,0 +1,53 @@
+var ps, pointCloud;
+var yRot = 0.0;
+var zoomed = -100;
+var prog;
+
+function zoom(amt){
+ zoomed += amt * 2 * 1;
+}
+
+function pointLight(light){
+ var lightName = "lights" + light.id;
+ ps.uniformi( lightName + ".isOn", true);
+ ps.uniformf( lightName + ".position", light.position);
+ ps.uniformi( lightName + ".type", 2);
+ ps.uniformf( lightName + ".attenuation", light.attenuation);
+
+ if(light.ambient){ps.uniformf( lightName + ".ambient", light.ambient);}
+ if(light.diffuse){ps.uniformf( lightName + ".diffuse", light.diffuse);}
+ if(light.specular){ps.uniformf( lightName + ".specular", light.specular);}
+}
+
+
+function render() {
+ ps.translate(0, 0, zoomed);
+ ps.rotateY(yRot += 0.001);
+
+ var c = pointCloud.getCenter();
+ ps.translate(-c[0], -c[1], -c[2]);
+
+ ps.clear();
+ ps.render(pointCloud);
+}
+
+function start(){
+ ps = new PointStream();
+ ps.setup(document.getElementById('canvas'));
+ ps.background([1, 1, 0.7, 1]);
+
+ var vertShader = ps.getShaderStr("../../shaders/fixed_function.vs");
+ var fragShader = ps.getShaderStr("../../shaders/fixed_function.fs");
+
+ var prog = ps.createProgram(vertShader, fragShader);
+ ps.useProgram(prog);
+
+ pointLight({id:0, ambient:[0,0,0], diffuse:[1,1,1], attenuation:[1,0,0], position: [0,0,50]});
+
+ ps.onRender = render;
+ ps.onMouseScroll = zoom;
+
+ ps.pointSize(3);
+
+ pointCloud = ps.load("../../clouds/ariusman_842K_n.psi");
+}
View
BIN  tools/sundae/resources/ariusman_842K_n/ariusman_842K_n.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
19 tools/sundae/resources/ariusman_842K_n/psi.js
@@ -0,0 +1,19 @@
+function start(cvs, cb){
+ var ps = new PointStream();
+ ps.setup(cvs);
+ ps.pointSize(1);
+ ps.onRender = function(){
+ if(pointCloud.status === 3){
+ ps.background([0.9, 0.5, 0.4, 1]);
+ ps.clear();
+
+ var c = pointCloud.getCenter();
+
+ ps.translate(-c[0], -c[1]+10, -c[2]-85);
+ ps.render(pointCloud);
+ cb();
+ ps.onRender = function(){};
+ }
+ };
+ var pointCloud = ps.load('../../clouds/ariusman_842K_n.psi');
+}
View
BIN  tools/sundae/resources/mask_1558K_n/1558K_n.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
19 tools/sundae/resources/mask_1558K_n/psi.js
@@ -0,0 +1,19 @@
+function start(cvs, cb){
+ var ps = new PointStream();
+ ps.setup(cvs);
+ ps.pointSize(1);
+ ps.onRender = function(){
+ if(pointCloud.status === 3){
+ ps.background([0.9, 0.5, 0.4, 1]);
+ ps.clear();
+
+ var c = pointCloud.getCenter();
+
+ ps.translate(-c[0], -c[1], -c[2]-55);
+ ps.render(pointCloud);
+ cb();
+ ps.onRender = function(){};
+ }
+ };
+ var pointCloud = ps.load('../../clouds/mask_1558K_n.psi');
+}
View
BIN  tools/sundae/resources/mask_70K_n/70K_n.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
19 tools/sundae/resources/mask_70K_n/psi.js
@@ -0,0 +1,19 @@
+function start(cvs, cb){
+ var ps = new PointStream();
+ ps.setup(cvs);
+ ps.pointSize(1);
+ ps.onRender = function(){
+ if(pointCloud.status === 3){
+ ps.background([0.9, 0.5, 0.4, 1]);
+ ps.clear();
+
+ var c = pointCloud.getCenter();
+
+ ps.translate(-c[0], -c[1], -c[2]-55);
+ ps.render(pointCloud);
+ cb();
+ ps.onRender = function(){};
+ }
+ };
+ var pointCloud = ps.load('../../clouds/mask_70K_n.psi');
+}
View
48 tools/sundae/tests.json
@@ -8,6 +8,8 @@
"../../parsers/psi.js",
"../../parsers/pts.js",
"../../parsers/ply.js",
+ "../../parsers/hps0.js",
+ "../../parsers/psi2.js",
],
"test": [
{
@@ -190,8 +192,54 @@
"src" : { "url" : "resources/mickey_754K_n/psi.js", "type" : "script" },
"run": "start"
}
+ },
+
+
+ {
+ "name": "mask_70K_n.psi",
+ "tag": "PSI2",
+ "note": "69,298 points (XYZ RGB IJK)",
+ "firstCanvas" : {
+ "src" : { "url" : "resources/mask_70K_n/70K_n.png", "type" : "image" }
+ },
+ "secondCanvas" : {
+ "src" : { "url" : "resources/mask_70K_n/psi.js", "type" : "script" },
+ "run": "start"
+ }
+ },
+
+ {
+ "name": "mask_1558K_n.psi",
+ "tag": "PSI2",
+ "note": "1,558,432 points (XYZ RGB IJK)",
+ "firstCanvas" : {
+ "src" : { "url" : "resources/mask_1558K_n/1558K_n.png", "type" : "image" }
+ },
+ "secondCanvas" : {
+ "src" : { "url" : "resources/mask_1558K_n/psi.js", "type" : "script" },
+ "run": "start"
+ }
+ },
+
+ {
+ "name": "ariusman_842K_n.psi",
+ "tag": "PSI2",
+ "note": "842,666 points (XYZ RGB IJK)",
+ "firstCanvas" : {
+ "src" : { "url" : "resources/ariusman_842K_n/ariusman_842K_n.png", "type" : "image" }
+ },
+ "secondCanvas" : {
+ "src" : { "url" : "resources/ariusman_842K_n/psi.js", "type" : "script" },
+ "run": "start"
+ }
}
+
+
+
+
+
+
/*{
"name": "User shader",
"note": "user shader",
View
2  xbps.js
@@ -17,3 +17,5 @@ ps_include('./parsers/asc.js');
ps_include('./parsers/psi.js');
ps_include('./parsers/pts.js');
ps_include('./parsers/ply.js');
+ps_include('./parsers/hps0.js');
+ps_include('./parsers/psi2.js');
Please sign in to comment.
Something went wrong with that request. Please try again.