diff --git a/CgMath/CgPointMath.h b/CgMath/CgPointMath.h
index b550ebb5f7c9f4e5260ba04c30bd5045ed404d33..49d1ea0ce5cd940abbb0b64be13d55f4ed4a6ef9 100644
--- a/CgMath/CgPointMath.h
+++ b/CgMath/CgPointMath.h
@@ -14,6 +14,15 @@ using namespace Eigen;
 
 #include "CgAABB.h"
 
+struct PCA {
+    glm::vec3 evec0;
+    glm::vec3 evec1;
+    glm::vec3 evec2;
+    float eval0;
+    float eval1;
+    float eval2;
+};
+
 /*
 * simple geometric calculations used in multiple places
 */
@@ -24,7 +33,7 @@ class CgPointMath {
         static glm::vec3 calculateCentroid(const std::vector<glm::vec3> &points);
         static CgAABB calculateAABB(const std::vector<glm::vec3>& vertices);
         static glm::mat3 getCovarianceMatrix(const std::vector<glm::vec3> &points);
-        static glm::vec3 estimateNormalFromPCA(const std::vector<glm::vec3> &points);
+        static PCA calculatePCA(const std::vector<glm::vec3> &points);
         static glm::vec3 getPerpendicularVector(glm::vec3 arg);
     private:
         CgPointMath(){}
@@ -141,7 +150,7 @@ inline glm::mat3 CgPointMath::getCovarianceMatrix(const std::vector<glm::vec3> &
     return ret;
 }
 
-inline glm::vec3 CgPointMath::estimateNormalFromPCA(const std::vector<glm::vec3> &points) {
+inline PCA CgPointMath::calculatePCA(const std::vector<glm::vec3> &points) {
     // calculate covariance matrix
     unsigned int size = points.size();
     Eigen::MatrixXd mat(size, 3);
@@ -160,8 +169,17 @@ inline glm::vec3 CgPointMath::estimateNormalFromPCA(const std::vector<glm::vec3>
 
     // The eigenvalues of a selfadjoint matrix are always real.
     // the eigenvalues are sorted in ascending order
-    Eigen::Vector3d normal = es.eigenvectors().col(0);
-    return glm::vec3(normal(0), normal(1), normal(2));
+    Eigen::Vector3d evec0 = es.eigenvectors().col(0);
+    Eigen::Vector3d evec1 = es.eigenvectors().col(1);
+    Eigen::Vector3d evec2 = es.eigenvectors().col(2);
+    return {
+        glm::vec3(evec0(0), evec0(1), evec0(2)),
+        glm::vec3(evec1(0), evec1(1), evec1(2)),
+        glm::vec3(evec2(0), evec2(1), evec2(2)),
+        es.eigenvalues()(0),
+        es.eigenvalues()(1),
+        es.eigenvalues()(2)
+    };
 }
 
 // calculates an arbitrary verctor perpendicular to the given one
diff --git a/CgMath/CgPointWrangler.h b/CgMath/CgPointWrangler.h
index d06f6fa79116ea5f9855dca232ad016bf07475e3..3bea1a2104e21733e200a8ad65f404d489ce73e0 100644
--- a/CgMath/CgPointWrangler.h
+++ b/CgMath/CgPointWrangler.h
@@ -223,6 +223,7 @@ inline std::vector<glm::vec3> CgPointWrangler::estimateNormals(unsigned int neig
     std::vector<std::thread> threads;
     threads.reserve(thread_count);
 
+    // worker for the threads that calculate all k-neighbourhoods
     auto worker = [&](
         const unsigned int start_index, 
         const unsigned int count
@@ -231,13 +232,12 @@ inline std::vector<glm::vec3> CgPointWrangler::estimateNormals(unsigned int neig
         std::vector<glm::vec3> nh(neighbourhood_size);
         for(unsigned int i = start_index; i < start_index + count; i++) {
             neighbourhoods[i] = getNearestNeighborsFast(i, neighbourhood_size, vertices, aabb);
-            for(int j = 0; j < neighbourhood_size; j++) {
-                nh[j] = vertices[neighbourhoods[i][j]];
-            }
-            normals[i] = CgPointMath::estimateNormalFromPCA(nh);
+            for(int j = 0; j < neighbourhood_size; j++) nh[j] = vertices[neighbourhoods[i][j]];
+            normals[i] = CgPointMath::calculatePCA(nh).evec0;
         }
     };
 
+    // start some workers
     unsigned int slice_size = vertices.size() / thread_count;
     unsigned int slice_rest = vertices.size() % thread_count;
     unsigned int t = 0;
diff --git a/CgSceneGraph/CgPointCloud.cpp b/CgSceneGraph/CgPointCloud.cpp
index a7cec1c16e21e43c2de0bf4ceaba3b78c8c48da0..332f93626d2e97b5eab1751c27e2b5930c559742 100644
--- a/CgSceneGraph/CgPointCloud.cpp
+++ b/CgSceneGraph/CgPointCloud.cpp
@@ -35,31 +35,6 @@ CgPointCloud::~CgPointCloud() {
     m_splat_indices.clear();
 }
 
-void CgPointCloud::calculateSplatOrientations() {
-  // calculate local coordinate system for splats (arbitrary orientation of ellipse in plane)
-  // replace this if you have the real coordinate system, use up vector = y-Axis of your local coordinate system instead of getPerpendicularVector(...)
-
-  m_splat_orientations.clear();
-  m_splat_scaling.clear();
-  m_splat_indices.clear();
-
-  for(unsigned int i = 0; i < m_vertices.size(); i++) {
-    // eye, center, up
-    // look from vertex position to the point the normal points at
-    glm::mat4 lookAt_matrix(glm::lookAt(
-      glm::vec3(m_vertices[i]),
-      glm::vec3(m_vertices[i] - m_vertex_normals[i]),
-      CgPointMath::getPerpendicularVector(m_vertex_normals[i])
-    ));
-    m_splat_orientations.push_back(lookAt_matrix);
-    m_splat_scaling.push_back(glm::vec2(0.02,0.02));
-
-    // use all points for splatting by default
-    m_splat_indices.push_back(i);
-  }
-
-}
-
 void CgPointCloud::setColors(std::vector<unsigned int> indices, glm::vec3 color) {
   for(unsigned int i = 0; i < indices.size(); i++) m_vertex_colors[indices[i]] = color;
 }
diff --git a/CgSceneGraph/CgPointCloud.h b/CgSceneGraph/CgPointCloud.h
index d7d9decf1e83c26d63e22ac637c2ae3099b72e74..2d7cb9f92a24345ae9fcc4e3c304e2ed85a25fdb 100644
--- a/CgSceneGraph/CgPointCloud.h
+++ b/CgSceneGraph/CgPointCloud.h
@@ -56,19 +56,12 @@ private:
 
     CgPointCloud(int id): m_type(Cg::PointCloud), m_id(id) {};
 
-    //for demonstration: find local coordinate system (normal plus arbitrary tangent spanning directions)
-    void calculateSplatOrientations();
-
     std::vector<glm::vec3> m_vertices;
     std::vector<glm::vec3> m_vertex_normals;
     std::vector<glm::vec3> m_vertex_colors;
     glm::vec3 m_centroid;
     CgAABB m_aabb;
-
-    // indices of vertices for which a splat will be rendered
     std::vector<unsigned int> m_splat_indices;
-
-
     std::vector<glm::mat4> m_splat_orientations;
     std::vector<glm::vec2> m_splat_scaling;
 
diff --git a/CgSceneGraph/CgSceneControl.cpp b/CgSceneGraph/CgSceneControl.cpp
index c92078abdb6203b1ceb243aa714852b227145805..5becc4bfc7723e7615497220ec27659631b3c9e7 100644
--- a/CgSceneGraph/CgSceneControl.cpp
+++ b/CgSceneGraph/CgSceneControl.cpp
@@ -372,6 +372,9 @@ void CgSceneControl::handleEvent(CgBaseEvent* e) {
           std::vector<glm::vec3> positions;
           std::vector<glm::vec3> normals;
           std::vector<glm::vec3> colors;
+          std::vector<glm::mat4> splat_orientations;
+          std::vector<glm::vec2> splat_scalings;
+          std::vector<unsigned int> splat_indices;
           loader->getPositionData(positions);
 
           std::cout << "loaded " << positions.size() << " points" << std::endl;
@@ -379,17 +382,27 @@ void CgSceneControl::handleEvent(CgBaseEvent* e) {
           run_timed("build kd", 1, [&](){CgPointWrangler::buildKdTree(positions.data(), positions.data() + positions.size(), 0);});
           run_timed("est. normals", 1, [&](){normals = CgPointWrangler::estimateNormals(15, positions);});
           colors.resize(positions.size());
+          splat_orientations.resize(positions.size());
+          splat_scalings.resize(positions.size());
+          splat_indices.resize(positions.size());
           for(unsigned int i = 0; i < positions.size(); i++) {
             colors[i] = (normals[i] + glm::vec3(1.0)) * .5;
+            splat_orientations[i] = glm::lookAt(
+              positions[i],
+              positions[i] - normals[i],
+              CgPointMath::getPerpendicularVector(normals[i])
+            );
+            splat_scalings[i] = glm::vec2(0.01, 0.01);
+            splat_indices[i] = i;
           }
 
           m_pointcloud = new CgPointCloud(
             positions,
             normals,
             colors,
-            std::vector<glm::mat4>(positions.size()),
-            std::vector<glm::vec2>(positions.size()),
-            std::vector<unsigned int>(positions.size())
+            splat_orientations,
+            splat_scalings,
+            splat_indices
           );
           
           // save away a copy