Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potree v2 remote #47

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ void Start() {

private void LoadHierarchy() {
try {
if (!cloudPath.EndsWith("/")) {
cloudPath = cloudPath + "/";
}

PointCloudMetaData metaData = CloudLoader.LoadMetaData(cloudPath, false);

setController.UpdateBoundingBox(this, metaData.boundingBox_transformed, metaData.tightBoundingBox_transformed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,59 @@
using System.Text;
using System.Net;
using System;
using System.Diagnostics;
using UnityEngine;
using System.Linq;
using Debug = UnityEngine.Debug;

namespace BAPointCloudRenderer.Loading {
/// <summary>
/// Provides methods for loading point clouds from the file system
/// </summary>
class CloudLoader {
public static bool isCloudOnline;

public enum FileTypeV2 {
HIERARCHY, OCTREE
}
/* Loads the metadata from the json-file in the given cloudpath
*/
/// <summary>
/// Loads the meta data from the json-file in the given cloudpath. Attributes "cloudPath", and "cloudName" are set as well.
/// </summary>
/// <param name="cloudPath">Folderpath of the cloud or URL to download the cloud from. In the latter case, it will be downloaded to a /temp folder</param>
/// <param name="moveToOrigin">True, if the center of the cloud should be moved to the origin</param>
public static PointCloudMetaData LoadMetaData(string cloudPath, bool moveToOrigin = false) {
public static PointCloudMetaData LoadMetaData(string fullPath, bool moveToOrigin = false) {
string jsonfile = "";
//Debug.Log(cloudPath);
bool isCloudOnline = Uri.IsWellFormedUriString(cloudPath, UriKind.Absolute);
if (isCloudOnline){
isCloudOnline = Uri.IsWellFormedUriString(fullPath, UriKind.Absolute);
if (isCloudOnline) {
WebClient client = new WebClient();
Stream stream = client.OpenRead(cloudPath + "cloud.js");
Stream stream = client.OpenRead(fullPath);
StreamReader reader = new StreamReader(stream);
jsonfile = reader.ReadToEnd();
reader.Close();
}else{
string filePath;
if (File.Exists(cloudPath + "cloud.js"))
{
filePath = cloudPath + "cloud.js";
}
else if (File.Exists(cloudPath + "metadata.json"))
{
filePath = cloudPath + "metadata.json";
}
else
{
Debug.LogError("Unable to find neither cloud.js nor metadata.json from " + cloudPath);
throw new Exception("Unable to find neither cloud.js nor metadata.json from " + cloudPath);
if (!File.Exists (fullPath)) {
Debug.LogError("Unable to find file from " + fullPath);
throw new Exception("Unable to find file from " + fullPath);
}
if (filePath.Length > 0)
{
using (StreamReader reader = new StreamReader(filePath, Encoding.Default))
{
jsonfile = reader.ReadToEnd();
reader.Close();
}
if (fullPath.Length > 0) {
using StreamReader reader = new StreamReader(fullPath, Encoding.Default);
jsonfile = reader.ReadToEnd();
reader.Close();
}
}

int lastSlashIndex = fullPath.LastIndexOf('/');
string cloudPath = fullPath.Substring (0, lastSlashIndex + 1);
if (cloudPath == null) {
Debug.LogError("Unable to find directory from " + fullPath);
throw new Exception("Unable to find file directory " + fullPath);
}

PointCloudMetaData metaData = PointCloudMetaDataReader.ReadFromJson(jsonfile, moveToOrigin);

metaData.cloudName = cloudPath.Substring(0, cloudPath.Length-1).Substring(cloudPath.Substring(0, cloudPath.Length - 1).LastIndexOf("/") + 1);
//Debug.Log(metaData.cloudName);

if (isCloudOnline){
metaData.cloudUrl = cloudPath;
Expand Down Expand Up @@ -116,12 +115,13 @@ public static void LoadPointsForNode(Node node) {
/// </summary>
/// <param name="metaData"></param>
/// <param name="node"></param>
private static void LoadHierarchy(PointCloudMetaData metaData, ref Node node)
/// <param name="isUrl"></param>
private static void LoadHierarchy (PointCloudMetaData metaData, ref Node node)
{
// sanitycheck.
if (node.hierarchyByteSize > 0)
{
byte[] data = ReadFromFile(metaData.cloudPath + "hierarchy.bin", (long)node.hierarchyByteOffset, node.hierarchyByteSize);
byte[] data = ReadFromFile(isCloudOnline ? metaData.cloudUrl : metaData.cloudPath, (long)node.hierarchyByteOffset, node.hierarchyByteSize, FileTypeV2.HIERARCHY, isCloudOnline);
if (data.Length == (int)node.hierarchyByteSize)
{
ParseHierarchy(ref node, data);
Expand Down Expand Up @@ -306,18 +306,21 @@ private static BoundingBox CalculateBoundingBox(BoundingBox parent, int index) {
/// <param name="dataRPath"></param>
/// <param name="metaData"></param>
/// <param name="node"></param>
private static void LoadPoints(string dataRPath, PointCloudMetaData metaData, Node node) {
/// <param name="hierarchy"></param>
private static void LoadPoints (string dataRPath, PointCloudMetaData metaData, Node node) {
// in potree v2 type 2 nodes are proxies and their hierarchy
// yearns to be loaded just-in-time.
if (metaData.version == "2.0" && node.type == 2)
{
LoadHierarchy(metaData, ref node);
}
byte[] data = metaData.version switch
{
"2.0" => ReadFromFile(metaData.cloudPath + "octree.bin", (long)node.byteOffset, node.byteSize),
_ => FindAndLoadFile(dataRPath, metaData, node.Name, ".bin"),
};
byte[] data = null;

if (metaData.version.Equals ("2.0")) {
data = ReadFromFile (isCloudOnline ? metaData.cloudUrl : metaData.cloudPath, (long) node.byteOffset, node.byteSize, FileTypeV2.OCTREE, isCloudOnline);
} else {
data = FindAndLoadFile (dataRPath, metaData, node.Name, ".bin");
}
int pointByteSize = metaData.pointByteSize;
int numPoints = data.Length / pointByteSize;
int offset = 0, toSetOff = 0;
Expand Down Expand Up @@ -429,27 +432,63 @@ private static byte[] FindAndLoadFile(string dataRPath, PointCloudMetaData metaD
}
return null;
}

/// <summary>
/// used only for Potree v2. for now.
/// </summary>
/// <param name="fileNameWithPath"></param>
/// <param name="offset"></param>
/// <param name="size"></param>
/// <param name="fileType"></param>
/// <param name="isURL"></param>
/// <returns></returns>
private static byte[] ReadFromFile(string fileNameWithPath, long offset, UInt64 size)
private static byte[] ReadFromFile (string fileNameWithPath, long offset, ulong size, FileTypeV2 fileType, bool isURL = false)
{
switch (fileType) {
case FileTypeV2.HIERARCHY :
fileNameWithPath = fileNameWithPath + "hierarchy.bin";
break;
case FileTypeV2.OCTREE :
fileNameWithPath = fileNameWithPath + "octree.bin";
break;
}

if (size == 0)
{
return new byte[] { };
}
byte[] returnable = new byte[size];

if (File.Exists(fileNameWithPath))
{
using FileStream stream = File.OpenRead(fileNameWithPath);
stream.Seek(offset, SeekOrigin.Begin);
stream.Read(returnable, 0, (int)size);
stream.Close();

if (!isURL) {
if (File.Exists(fileNameWithPath))
{
using FileStream stream = File.OpenRead(fileNameWithPath);
stream.Seek(offset, SeekOrigin.Begin);
stream.Read(returnable, 0, (int)size);
stream.Close();
}
} else {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fileNameWithPath);
request.AddRange(offset, offset + (long)size - 1);

try {
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using Stream stream = response.GetResponseStream();
if (stream == null)
return null;

int bytesRead = 0, totalBytesRead = 0;

while ((bytesRead = stream.Read(returnable, totalBytesRead, (int)size - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;
}
}
catch (WebException ex)
{
Debug.Log($"Error downloading data: {ex.Message}");
return null;
}
}

return returnable;
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,38 @@ PointCloud-BachelorThesis

Project files for my bachelor thesis on rendering large point clouds in Unity.


## Range request

We have to do range request to get the part of the remote file we want, so we're using range request as Potree is doing.

## Ressources
Please refer to the code documentation for details about the classes and scripts (Folder "/doc").
For details about the algorithms please refer to the bachelor thesis (https://www.cg.tuwien.ac.at/research/publications/2017/FRAISS-2017-PCU/).
Below you will find a Getting-Started-Guide

Download the current version: https://github.com/SFraissTU/BA_PointCloud/releases/

### Version PullRequest PotreeRemoveV2 (13/08/2024)

#### Remote URL

We would like to use the PotreeV2 format for remote url, mainly because we might have to handle several Go of data.

#### Remote/local URL handling with v1/v2 version

For this we had to transform the URL given to the Cloud loader :

Potree1 :
Clouds/Lion -> Clouds/Lion/cloud.js
https://remoteurl.com/Lion -> https://remoteurl.com/Lion/cloud.js

Potree2 :
Clouds/Lion2 -> Clouds/Lion2/metadata.json
https://remoteurl.com/Lion2 -> https://remoteurl.com/Lion2/metadata.json

We did this so codebase can handle in a easier way the remote/local and v1/v2 cases.

### Version 1.6 (09.07.2023)
Changes:
* Support for Potree Format V2 (Provided by [cognitivedata](https://github.com/cognitivedata))
Expand Down