https://github.com/ialhashim/topo-blend
Revision 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC, committed by ennetws on 13 March 2015, 18:17:18 UTC
1 parent c702819
Raw File
Tip revision: 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC
Create README.md
Tip revision: 39b1361
Synthesizer.cpp
#include <omp.h>

#include <QFile>
#include <QTextStream>

#include "NanoKdTree.h"
#include "Octree.h"
#include "SpherePackSampling.h"
#include "IsotropicRemesher.h"
#include "SimilarSampling.h"
#include "PCA.h"

#include "Synthesizer.h"
#include "weld.h"
Q_DECLARE_METATYPE(RMF)
Q_DECLARE_METATYPE(RMF::Frame)
Q_DECLARE_METATYPE(std::vector<RMF::Frame>)
Q_DECLARE_METATYPE(NanoKdTree)
Q_DECLARE_METATYPE( Structure::Sheet* )

typedef std::pair<ParameterCoord,int> ParameterCoordInt;
bool comparatorParameterCoordInt ( const ParameterCoordInt& l, const ParameterCoordInt& r)
{ return l.first.u < r.first.u; }

// Parameters
#define CURVE_FRAME_RESOLUTION 0.01
#define SHEET_FRAME_RESOLUTION 0.01
#define CURVE_FRAME_COUNT 101	// to match the resolution 0.01

#define OCTREE_NODE_SIZE 40

// Sampling
#define RANDOM_COUNT 1e3
#define UNIFORM_RESOLUTION 0.01f
#define EDGE_RESOLUTION 0.05
#define UNIFORM_TRI_SAMPLES 1e4

// Remeshing
#define REMESH_EDGE_LENGTH 0.005

int global_countt = 0;

int randomCount = RANDOM_COUNT;
int uniformTriCount = UNIFORM_TRI_SAMPLES;

// Helper functions
RMF Synthesizer::consistentFrame( Structure::Curve * curve, Array1D_Vector4d & coords )
{
	// Generate consistent frames along curve
	std::vector<Scalar> times;
	curve->curve.SubdivideByLengthTime(CURVE_FRAME_COUNT, times);
	foreach(Scalar t, times) coords.push_back(Vector4d(t,0,0,0));
	std::vector<Vector3d> samplePoints;
	foreach(Vector4d c, coords) samplePoints.push_back( curve->position(c) );

    RMF rmf = RMF( samplePoints );
	rmf.compute();
	//rmf.generate();

	// Save RMF frames
	curve->property["rmf_frames"].setValue(rmf.U);

	return rmf;
}


/// SAMPLING
// Ray parameters from points
QVector<ParameterCoord> Synthesizer::genPointCoordsCurve( Structure::Curve * curve, const std::vector<Vector3f> & points, const std::vector<Vector3f> & normals )
{
	QVector<ParameterCoord> samples(points.size());

	// Generate consistent frames along curve
	Array1D_Vector4d coords;
	RMF rmf = consistentFrame(curve,coords);

	// Add all curve points to kd-tree
	NanoKdTree kdtree;
	foreach(Vector3d p, rmf.point) kdtree.addPoint( p );
	kdtree.build();

	// Project
	int N = points.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		NURBS::NURBSCurved mycurve = curve->curve;

		float theta, psi;

		Vector3f point = points[i];

		KDResults match;
		kdtree.k_closest(point.cast<double>(), 1, match);
		int closest_idx = match.front().first;
		Vector4d c = coords[closest_idx];

		Vector3f X = rmf.U[closest_idx].r.normalized().cast<float>();
		Vector3f Y = rmf.U[closest_idx].s.normalized().cast<float>();
		Vector3f Z = rmf.U[closest_idx].t.normalized().cast<float>();

		Vector3f curvePoint = mycurve.GetPosition(c[0]).cast<float>();
		Vector3f delta = point - curvePoint;
		Vector3f raydirection = delta.normalized();

		globalToLocalSpherical(X, Y, Z, theta, psi, raydirection);

		samples[i] = ParameterCoord(theta, psi, c[0], 0, delta.norm(), curve);
		samples[i].origNormal = normals[i];
	}

	return samples;
}

QVector<ParameterCoord> Synthesizer::genPointCoordsSheet( Structure::Sheet * sheet, const std::vector<Vector3f> & points, const std::vector<Vector3f> & normals )
{
	QVector<ParameterCoord> samples(points.size());

	float resolution = sheet->bbox().diagonal().norm() * SHEET_FRAME_RESOLUTION;

	Array2D_Vector4d sheetCoords = sheet->discretizedPoints(resolution);
	Array1D_Vector4d allCoords;

	qDebug() << "Sheet resolution count = " << sheetCoords.size();

	NanoKdTree kdtree;
	foreach(Array1D_Vector4d row, sheetCoords){
		foreach(Vector4d c, row) {
			kdtree.addPoint( sheet->position(c) );
			allCoords.push_back(c);
		}
	}
	kdtree.build();

	int N = points.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		NURBS::NURBSRectangled r = sheet->surface;

		Vector3f point = points[i];

		// Project
		Vector3d X, Y, Z;
		Vector3d vDirection, sheetPoint;

		double theta, psi;

		KDResults match;
		kdtree.k_closest(point.cast<double>(), 1, match);
		int closest_idx = match.front().first;
		Vector4d c = allCoords[closest_idx];

		r.GetFrame( c[0], c[1], sheetPoint, X, vDirection, Z );
		Y = cross(Z, X);
		
		Vector3d delta = point.cast<double>() - sheetPoint;
		Vector3d raydirection = delta.normalized();

		globalToLocalSpherical(X, Y, Z, theta, psi, raydirection);

		samples[i] = ParameterCoord(theta, psi, c[0], c[1], delta.norm(), sheet);
		samples[i].origNormal = normals[i];
		samples[i].origPoint = point;
	}

    return samples;
}

// Different sampling methods to generate rays
QVector<ParameterCoord> Synthesizer::genFeatureCoords( Structure::Node * node )
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	// Collect original mesh points
	Vector3VertexProperty points = model->vertex_property<Vector3>(VPOINT);
	std::vector<Vector3f> meshPoints;
	foreach(Vertex v, model->vertices()) meshPoints.push_back(points[v].cast<float>());

	// Normals
	model->update_face_normals();
	model->update_vertex_normals();
	Vector3VertexProperty normals = model->vertex_property<Vector3>(VNORMAL);
	std::vector<Vector3f> meshNormals;
	foreach(Vertex v, model->vertices()) meshNormals.push_back(normals[v].cast<float>());

	if(node->type() == Structure::CURVE)
		return genPointCoordsCurve((Structure::Curve*)node, meshPoints, meshNormals);
	else
		return genPointCoordsSheet((Structure::Sheet*)node, meshPoints, meshNormals);
}

QVector<ParameterCoord> Synthesizer::genEdgeCoords( Structure::Node * node)
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	// Sample mesh surface
	QVector<Vector3d> sample_normals;
	QVector<Vector3d> sample_points = SimilarSampler::EdgeUniform( model, uniformTriCount, sample_normals );
	std::vector<Vector3d> samplePoints = sample_points.toStdVector();
	std::vector<Vector3d> sampleNormals = sample_normals.toStdVector();

	// Double to float...
	std::vector<Vector3f> samplePointsF,sampleNormalsF;
	foreach(Vector3d p, sample_points) samplePointsF.push_back(p.cast<float>());
	foreach(Vector3d n, sampleNormals) sampleNormalsF.push_back(n.cast<float>());

	if(node->type() == Structure::CURVE)
		return genPointCoordsCurve((Structure::Curve*)node, samplePointsF, sampleNormalsF);
	else
		return genPointCoordsSheet((Structure::Sheet*)node, samplePointsF, sampleNormalsF);
}

QVector<ParameterCoord> Synthesizer::genRandomCoords(Node *node, int samples_count)
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	// Sample mesh surface
	std::vector<Vector3d> samplePoints, sampleNormals;
	samplePoints = SpherePackSampling::getRandomSamples(model, samples_count, sampleNormals);

	// Double to float...
	std::vector<Vector3f> samplePointsF,sampleNormalsF;
	foreach(Vector3d p, samplePoints) samplePointsF.push_back(p.cast<float>());
	foreach(Vector3d n, sampleNormals) sampleNormalsF.push_back(n.cast<float>());

	if(node->type() == Structure::CURVE)
		return genPointCoordsCurve((Structure::Curve*)node, samplePointsF, sampleNormalsF);
	else
		return genPointCoordsSheet((Structure::Sheet*)node, samplePointsF, sampleNormalsF);
}

QVector<ParameterCoord> Synthesizer::genUniformCoords(Node *node, float sampling_resolution)
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

    // Auto resolution
    if(sampling_resolution < 0) sampling_resolution = UNIFORM_RESOLUTION;

    // Sample mesh surface
    std::vector<Vector3d> samplePoints, sampleNormals, gp;
    samplePoints = SpherePackSampling::sample(model, 1e4, sampling_resolution, gp, sampleNormals, 1);

	// Double to float...
	std::vector<Vector3f> samplePointsF,sampleNormalsF;
	foreach(Vector3d p, samplePoints) samplePointsF.push_back(p.cast<float>());
	foreach(Vector3d n, sampleNormals) sampleNormalsF.push_back(n.cast<float>());

    if(node->type() == Structure::CURVE)
        return genPointCoordsCurve((Structure::Curve*)node, samplePointsF, sampleNormalsF);
    else
        return genPointCoordsSheet((Structure::Sheet*)node, samplePointsF, sampleNormalsF);
}

QVector<ParameterCoord> Synthesizer::genRemeshCoords( Structure::Node * node )
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();
	SurfaceMesh::Model copyModel;

	std::vector<Vector3f> samplePoints, sampleNormals;

	Vector3VertexProperty points = model->vertex_property<Vector3d>(VPOINT);

	foreach(Vertex v, model->vertices()) copyModel.add_vertex( points[v] );
	foreach(Face f, model->faces()){
		std::vector<Vertex> verts;
		Surface_mesh::Vertex_around_face_circulator vit = model->vertices(f),vend=vit;
		do{ verts.push_back(vit); } while(++vit != vend);
		copyModel.add_face(verts);
	}

	IsotropicRemesher remesher(&copyModel);
	remesher.doRemesh( REMESH_EDGE_LENGTH );
	
	// Position
	Vector3VertexProperty new_points = copyModel.vertex_property<Vector3d>(VPOINT);
	foreach(Vertex v, copyModel.vertices()) samplePoints.push_back( new_points[v].cast<float>() );
	
	// Normal
	copyModel.update_face_normals();
	copyModel.update_vertex_normals();
	Vector3VertexProperty new_normals = copyModel.vertex_property<Vector3d>(VNORMAL);
	foreach(Vertex v, copyModel.vertices()) sampleNormals.push_back( new_normals[v].cast<float>() );

	// Maybe add face centers + edge centers ?
	//foreach(Face f, copyModel.faces()){
	//	Vector3f sum(0); int c = 0;
	//	Surface_mesh::Vertex_around_face_circulator vit = copyModel.vertices(f),vend=vit;
	//	do{ sum += new_points[vit]; c++; } while(++vit != vend);
	//	samplePoints.push_back( sum / c );
	//}

	//foreach(Edge e, copyModel.edges()) 
	//	samplePoints.push_back( (new_points[copyModel.vertex(e,0)]+new_points[copyModel.vertex(e,1)]) / 2.0 );

	if(node->type() == Structure::CURVE)
		return genPointCoordsCurve((Structure::Curve*)node, samplePoints, sampleNormals);
	else
		return genPointCoordsSheet((Structure::Sheet*)node, samplePoints, sampleNormals);
}

QVector<ParameterCoord> Synthesizer::genUniformTrisCoords( Structure::Node * node )
{
	SurfaceMesh::Model * model = node->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	// Sample mesh surface
	QVector<Vector3d> sample_normals;
	QVector<Vector3d> sample_points = SimilarSampler::All( model, uniformTriCount, sample_normals );
	
	// Double to float...
	std::vector<Vector3f> samplePointsF,sampleNormalsF;
	foreach(Vector3d p, sample_points) 
	{
		samplePointsF.push_back(p.cast<float>());
	}
	foreach(Vector3d n, sample_normals) 
	{
		sampleNormalsF.push_back(n.cast<float>());
	}

	if(node->type() == Structure::CURVE)
		return genPointCoordsCurve((Structure::Curve*)node, samplePointsF, sampleNormalsF);
	else
		return genPointCoordsSheet((Structure::Sheet*)node, samplePointsF, sampleNormalsF);
}

// Generate rays depending on configuration of sampling types \s
QVector<ParameterCoord> Synthesizer::genSampleCoordsCurve( Structure::Curve * curve, int s )
{
	QVector<ParameterCoord> samples;

//	if (!curve->property.contains("original_sheet"))
	if (true)
	{
		if (s & Synthesizer::Features)	samples += genFeatureCoords(curve);
		if (s & Synthesizer::Edges)		samples += genEdgeCoords(curve);
		if (s & Synthesizer::Random)	samples += genRandomCoords(curve, randomCount);
		if (s & Synthesizer::Uniform)	samples += genUniformCoords(curve);
		if (s & Synthesizer::Remeshing)	samples += genRemeshCoords(curve);
		if (s & Synthesizer::TriUniform)samples += genUniformTrisCoords(curve);
	}
	else
	{
		// get samples using the original sheet
		Structure::Sheet* originalSheet  = curve->property["original_sheet"].value<Structure::Sheet*>();
		samples = genSampleCoordsSheet(originalSheet, s);

		// adjust the coordinates
		Array1D_Vector4d coords;
		RMF rmf = consistentFrame(curve,coords);
		QString curveDirection = originalSheet->property["curveDirection"].toString();
		for (int i = 0; i < (int)samples.size(); i++)
		{
			ParameterCoord &sample = samples[i];

			// adjust u, which is also t for curve
			//if (curveDirection == "positiveU");
			if (curveDirection == "negativeU")	sample.u = 1 - sample.u;
			if (curveDirection == "positiveV")	sample.u = sample.v;
			if (curveDirection == "positiveV")	sample.u = 1 - sample.v;

			// the corresponding frame on curve
			int idx = sample.u * (rmf.count() - 1);
			Vector3f X = rmf.U[idx].r.cast<float>();
			Vector3f Y = rmf.U[idx].s.cast<float>();
			Vector3f Z = rmf.U[idx].t.cast<float>();

			// encode the sample point in this frame
			float theta, psi;
			Vector3f delta = Vector3f(sample.origPoint - rmf.U[idx].center.cast<float>());
			Vector3f raydirection = delta.normalized();
			globalToLocalSpherical(X, Y, Z, theta, psi, raydirection);

			// update
			sample.theta = theta;
			sample.psi = psi;
			sample.origOffset = delta.norm();
			sample.origNode = curve;
		}
	}

	return samples;
}

QVector<ParameterCoord> Synthesizer::genSampleCoordsSheet( Structure::Sheet * sheet, int s )
{
	QVector<ParameterCoord> samples;

	if (s & Synthesizer::Features)	samples += genFeatureCoords(sheet);
	if (s & Synthesizer::Edges)		samples += genEdgeCoords(sheet);
	if (s & Synthesizer::Random)	samples += genRandomCoords(sheet, randomCount);
	if (s & Synthesizer::Uniform)	samples += genUniformCoords(sheet);
	if (s & Synthesizer::Remeshing)	samples += genRemeshCoords(sheet);
	if (s & Synthesizer::TriUniform)samples += genUniformTrisCoords(sheet);
	
	return samples;
}

// Compute offset and normal for each ray
void Synthesizer::sampleGeometryCurve( QVector<ParameterCoord> samples, Structure::Curve * curve, QVector<float> &offsets, QVector<Vec2f> &normals )
{
	SurfaceMesh::Model * model = curve->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	model->update_face_normals();
	Vector3FaceProperty fnormals = model->face_property<Vector3d>("f:normal");
    Octree octree(model, OCTREE_NODE_SIZE);

	offsets.clear();
	offsets.resize(samples.size());

	normals.clear();
	normals.resize(samples.size());

	const ParameterCoord * samplesArray = samples.data();

	// Generate consistent frames along curve
	Array1D_Vector4d coords;
	RMF rmf = consistentFrame(curve,coords);
	qDebug() << "Curve RMF count = " << rmf.U.size() << ", Samples = " << samples.size();

	const std::vector<Vector3d> curvePnts = curve->curve.mCtrlPoint;
	int N = samples.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		NURBS::NURBSCurved mycurve = NURBS::NURBSCurved::createCurveFromPoints(curvePnts);

		ParameterCoord sample = samplesArray[i];
		
		int idx = sample.u * (rmf.count() - 1);
		Vector3f X = rmf.U[idx].r.normalized().cast<float>();
		Vector3f Y = rmf.U[idx].s.normalized().cast<float>();
		Vector3f Z = rmf.U[idx].t.normalized().cast<float>();

		Vector4d coordinate(sample.u,0,0,0);
		Vector3f rayPos = mycurve.GetPosition( coordinate[0] ).cast<float>();
		Vector3f rayDir = rotatedVec(Z, sample.theta, Y);
		rayDir = rotatedVec(rayDir, sample.psi, Z);

		Vector3d rayPosition(rayPos.x(), rayPos.y(), rayPos.z());
		Vector3d rayDirection(rayDir.x(), rayDir.y(), rayDir.z());

		Ray ray( rayPosition, rayDirection );
		int faceIndex = 0;

		Vector3f vn(1,1,1);

		if(curve == sample.origNode)
		{
			offsets[ i ] = sample.origOffset;
			vn = sample.origNormal;
		}
		else
		{
			Vector3 isect = octree.closestIntersectionPoint(ray, &faceIndex);

			// Store the offset
			offsets[ i ] = (Vector3(isect - rayPos.cast<double>())).norm();

			vn = fnormals[ SurfaceMesh::Model::Face(faceIndex) ].cast<float>();
		}

		// Code the normal relative to local frame
		Vec2f normalCoord(0,0);
		globalToLocalSpherical(X, Y, Z, normalCoord[0], normalCoord[1], vn);

		normals[ i ] = normalCoord;
	}
}

void Synthesizer::sampleGeometrySheet( QVector<ParameterCoord> samples, Structure::Sheet * sheet, QVector<float> &offsets, QVector<Vec2f> &normals )
{
	SurfaceMesh::Model * model = sheet->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();

	model->update_face_normals();
	Vector3FaceProperty fnormals = model->face_property<Vector3d>("f:normal");
    Octree octree(model, OCTREE_NODE_SIZE);

	offsets.clear();
	offsets.resize( samples.size() );

	normals.clear();
	normals.resize( samples.size() );

	qDebug() << "Sheet samples = " << samples.size();

	const ParameterCoord * samplesArray = samples.data();

	const Array2D_Vector3 sheetPnts = sheet->surface.mCtrlPoint;
	int N = samples.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		NURBS::NURBSRectangled r = NURBS::NURBSRectangled::createSheetFromPoints(sheetPnts);

		ParameterCoord sample = samplesArray[i];

		Vector3d X(0,0,0), Y(0,0,0), Z(0,0,0);
		Vector3d vDirection(0,0,0), rayPos(0,0,0);

		r.GetFrame( sample.u, sample.v, rayPos, X, vDirection, Z );
		Y = cross(Z, X);

		Vector3d rayDir = rotatedVec(Z, sample.theta, Y);
		rayDir = rotatedVec(rayDir, sample.psi, Z);

		Ray ray( rayPos, rayDir );
		int faceIndex = 0;

		Vector3d vn(1,1,1);

		if(sheet == sample.origNode)
		{
			offsets[i] = sample.origOffset;
			vn = sample.origNormal.cast<double>();
		}
		else
		{
			// Store the offset
			Vector3 isect = octree.closestIntersectionPoint(ray, &faceIndex);
			offsets[i] = (isect - rayPos).norm();

			// Code the normal relative to local frame
			vn = fnormals[SurfaceMesh::Model::Face(faceIndex)];
		}

		Vec2f normalCoord;
		globalToLocalSpherical(X, Y, Z, normalCoord[0], normalCoord[1], vn);
		normals[i] = normalCoord;
	}

	qDebug() << QString("Sheet [%1] Done.").arg(sheet->id);
}

// Co-sampling
void Synthesizer::prepareSynthesizeCurve( Structure::Curve * curve1, Structure::Curve * curve2, int s, SynthData & output )
{
	if(!curve1 || !curve2 || !curve1->property.contains("mesh") || !curve2->property.contains("mesh")) return;

	QElapsedTimer timer; timer.start();
	qDebug() << "Generating samples..";

	QVector<ParameterCoord> samples;
	QVector<float> offsets1, offsets2;
	QVector<Vec2f> normals1, normals2;

	// Sample two curves to get rays
	{
		if (s & Synthesizer::All)	s = Synthesizer::Features | Synthesizer::Edges | Synthesizer::Random | Synthesizer::Uniform;
		if (s & Synthesizer::AllNonUniform) s = Synthesizer::Features | Synthesizer::Edges | Synthesizer::Random;

		int old_uniformTriCount = uniformTriCount;
		if (curve1->property.contains("original_sheet") || curve2->property.contains("original_sheet"))
		{
			// Super sample converted parts
			uniformTriCount *= 10;
		}

		samples  = genSampleCoordsCurve(curve1, s);
		samples += genSampleCoordsCurve(curve2, s);

		if (curve1->property.contains("original_sheet") || curve2->property.contains("original_sheet"))
		{
			// Reset the sampling rate
			uniformTriCount = old_uniformTriCount;
		}

		// Why need sorting? -HH
		// Sort samples by 'u'
		sort(samples.begin(), samples.end());
	}

	qDebug() << QString("Samples Time [ %1 ms ]").arg(timer.elapsed());timer.restart();

	qDebug() << "Re-sampling mesh..";

	// Compute offset and normal for each ray
	{
		sampleGeometryCurve(samples, curve1, offsets1, normals1);
		output["node1"]["samples"].setValue(samples);
		output["node1"]["offsets"].setValue(offsets1);
		output["node1"]["normals"].setValue(normals1);

		sampleGeometryCurve(samples, curve2, offsets2, normals2);
		output["node2"]["samples"].setValue(samples);
		output["node2"]["offsets"].setValue(offsets2);
		output["node2"]["normals"].setValue(normals2);

		// Save counts
		output["node1"]["samplesCount"] = samples.size();
		output["node2"]["samplesCount"] = samples.size();
	}

	qDebug() << QString("Resampling Time [ %1 ms ]\n==\n").arg(timer.elapsed());
}

void Synthesizer::prepareSynthesizeSheet( Structure::Sheet * sheet1, Structure::Sheet * sheet2, int s, SynthData & output  )
{
	if(!sheet1 || !sheet2 || !sheet1->property.contains("mesh") || !sheet2->property.contains("mesh")) return;

	QElapsedTimer timer; timer.start();
	qDebug() << "Generating samples..";

	QVector<ParameterCoord> samples;
	QVector<float> offsets1, offsets2;
	QVector<Vec2f> normals1, normals2;
	//bool c1_isSampled = false, c2_isSampled = false;

	// Sample two sheets
	{
		if (s & Synthesizer::All)	s = Synthesizer::Features | Synthesizer::Edges | Synthesizer::Random | Synthesizer::Uniform;
		if (s & Synthesizer::AllNonUniform) s = Synthesizer::Features | Synthesizer::Edges | Synthesizer::Random;

		samples  = genSampleCoordsSheet(sheet1, s);
		if(sheet1 != sheet2) samples += genSampleCoordsSheet(sheet2, s);
	}

	qDebug() << QString("Samples Time [ %1 ms ]").arg(timer.elapsed());timer.restart();

	qDebug() << "Re-sampling mesh..";

	// Re-sample the meshes
	{	
		sampleGeometrySheet(samples, sheet1, offsets1, normals1);
		output["node1"]["samples"].setValue(samples);
		output["node1"]["offsets"].setValue(offsets1);
		output["node1"]["normals"].setValue(normals1);

		if(sheet1 != sheet2) 
		{
			sampleGeometrySheet(samples, sheet2, offsets2, normals2);
			output["node2"]["samples"].setValue(samples);
			output["node2"]["offsets"].setValue(offsets2);
			output["node2"]["normals"].setValue(normals2);
		}

		// Save counts
		output["node1"]["samplesCount"] = samples.size();
		output["node2"]["samplesCount"] = samples.size();
	}

	qDebug() << QString("Resampling Time [ %1 ms ]\n==\n").arg(timer.elapsed());
}

/// RECONSTRUCTION
void Synthesizer::reconstructGeometryCurve( Structure::Curve * base_curve, const QVector<ParameterCoord> & in_samples, const QVector<float> &in_offsets,
	const QVector<Vec2f> &in_normals, QVector<Vector3f> &out_points, QVector<Vector3f> &out_normals, bool isApprox )
{
	// Clear
	out_points.clear();
	out_points.resize(in_samples.size());
	out_normals.clear();
	out_normals.resize(in_samples.size());

	// Generate consistent frames along curve
	Array1D_Vector4d coords;
	RMF rmf = consistentFrame(base_curve,coords);
	int rmfCount = rmf.count();

	// Approximation for faster reconstruction
	std::vector<Vector3f> proxy;
	if(isApprox){
		int steps = 100;
		double curveLength = base_curve->curve.GetTotalLength();
		curveLength = qMax(curveLength, 1e-4);

		foreach(Vector4d p, base_curve->discretizedPoints( curveLength / steps).front())
			proxy.push_back( base_curve->position(p).cast<float>() );

		if(!proxy.size()) isApprox = false;
	}

	const std::vector<Vector3d> curvePnts = base_curve->curve.mCtrlPoint;

	int N = in_samples.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		ParameterCoord sample = in_samples[i];

		Vector3f rayPos, rayDir;

		int idx = sample.u * (rmfCount - 1);

		Vector3f X (rmf.U[idx].r[0],rmf.U[idx].r[1],rmf.U[idx].r[2]);
		Vector3f Y (rmf.U[idx].s[0],rmf.U[idx].s[1],rmf.U[idx].s[2]);
		Vector3f Z (rmf.U[idx].t[0],rmf.U[idx].t[1],rmf.U[idx].t[2]);

		if(isApprox)
			rayPos = proxy[ sample.u * (proxy.size()-1) ];
		else
		{
			NURBS::NURBSCurved c = NURBS::NURBSCurved::createCurveFromPoints(curvePnts);
			rayPos = c.GetPosition(sample.u).cast<float>();
		}

		localSphericalToGlobal(X, Y, Z, sample.theta, sample.psi, rayDir);

		// Reconstructed point
		out_points[ i ] = rayPos + (rayDir * in_offsets[ i ]);

		// Reconstructed normal
		Vec2f normalCoord = in_normals[ i ];
		Vector3f normal(0,0,0);
		localSphericalToGlobal(X, Y, Z, normalCoord[0], normalCoord[1], normal);

		// Normal correction
		{
			float theta = acos(qRanged(-1.0f, (float)dot(normal, rayDir), 1.0f));
			if(theta > M_PI / 2.0) normal = rayDir;
		}

		out_normals[ i ] = normal;
	}
}

void Synthesizer::reconstructGeometrySheet( Structure::Sheet * base_sheet, const QVector<ParameterCoord> &in_samples, const QVector<float> &in_offsets,
	const QVector<Vec2f> &in_normals, QVector<Vector3f> &out_points, QVector<Vector3f> &out_normals, bool isApprox )
{
	out_points.clear();
	out_points.resize(in_samples.size());

	out_normals.clear();
	out_normals.resize(in_samples.size());

	// Approximation for faster reconstruction
	std::vector< std::vector< std::vector<Vector3> > > proxy;
	if(isApprox){
		int steps = 100;
		double res = base_sheet->bbox().diagonal().norm() / steps;
		Array2D_Vector4d pnts = base_sheet->discretizedPoints( res );

		if(pnts.size())
		{
			proxy.resize(pnts.size(), std::vector< std::vector<Vector3> >(pnts[0].size(), std::vector<Vector3>(4,Vector3(0,0,0))));

			// Fill proxy
			for(int u = 0; u < (int)pnts.size(); u++){
				for(int v = 0; v < (int)pnts[0].size(); v++){
					base_sheet->surface.GetFrame(pnts[u][v][0],pnts[u][v][1],
						proxy[u][v][0],proxy[u][v][1],proxy[u][v][2],proxy[u][v][3]);
				}
			}
		}
		else
			isApprox = false;
	}

	const Array2D_Vector3 sheetPnts = base_sheet->surface.mCtrlPoint;

	int N = in_samples.size();

	#pragma omp parallel for
	for(int i = 0; i < N; i++)
	{
		Vector3d _X, _Y, _Z;
		Vector3d vDirection, sheetPoint;
		Vector3f rayPos, rayDir;

		ParameterCoord sample = in_samples[i];

		if( isApprox )
		{
			int u = sample.u * (proxy.size()-1);
			int v = sample.v * (proxy.front().size()-1);
			sheetPoint = proxy[u][v][0];
			_X = proxy[u][v][1];
			_Z = proxy[u][v][3];
		}
		else
		{
			NURBS::NURBSRectangled r = NURBS::NURBSRectangled::createSheetFromPoints(sheetPnts);
			r.GetFrame( sample.u, sample.v, sheetPoint, _X, vDirection, _Z );
		}

		rayPos = Vector3f(sheetPoint[0],sheetPoint[1],sheetPoint[2]);

		_Y = cross(_Z, _X);

		// double float
		Vector3f X(_X[0],_X[1],_X[2]),
				 Y(_Y[0],_Y[1],_Y[2]),
				 Z(_Z[0],_Z[1],_Z[2]);

		localSphericalToGlobal(X, Y, Z, sample.theta, sample.psi, rayDir);

		// Reconstructed point
		Vector3f isect = rayPos + (rayDir * in_offsets[i]);
		out_points[i] = isect;

		// Reconstructed normal
		Vec2f normalCoord = in_normals[i];
		Vector3f normal;
		localSphericalToGlobal(X, Y, Z, normalCoord[0], normalCoord[1], normal);

		// Normal correction
		{
			float theta = acos(qRanged(-1.0f,(float)dot(normal,rayDir),1.0f));
			if(theta > M_PI / 2.0) normal = rayDir;
		}

		out_normals[i] = normal;
	}
}

/// BLENDING
void Synthesizer::blendCurveBases( Structure::Curve * curve1, Structure::Curve * curve2, float alpha )
{
	std::vector<Vector3d> &ctrlPnts1 = curve1->curve.mCtrlPoint;

	for (int i = 0; i < (int) ctrlPnts1.size(); i++)
	{
		Vector3d cp1 = ctrlPnts1[i];
		float time = float(i) / (ctrlPnts1.size() - 1);
		Vector3d cp2 = curve2->curve.GetPosition(time);

		ctrlPnts1[i] = (1 - alpha) * cp1 + alpha * cp2;
	}
}

void Synthesizer::blendSheetBases( Structure::Sheet * sheet1, Structure::Sheet * sheet2, float alpha )
{
    sheet1 = sheet1;
    sheet2 = sheet2;
    alpha = alpha;
}

void Synthesizer::blendGeometryCurves( Structure::Curve * curve, float alpha, const SynthData & data, QVector<Vector3f> &points, QVector<Vector3f> &normals, bool isApprox )
{
	alpha = qMax(0.0f, qMin(alpha, 1.0f));
	QVector<ParameterCoord> samples = data["node1"]["samples"].value< QVector<ParameterCoord> >();
	QVector<float> offsets1 = data["node1"]["offsets"].value< QVector<float> >();
	QVector<float> offsets2 = data["node2"]["offsets"].value< QVector<float> >();
	QVector<Vec2f> normals1 = data["node1"]["normals"].value< QVector<Vec2f> >();
	QVector<Vec2f> normals2 = data["node2"]["normals"].value< QVector<Vec2f> >();

	// Blend Geometries 
	int N = samples.size();
	QVector<float> blended_offsets(N);
	QVector<Vec2f> blended_normals(N);
	for( int i = 0; i < N; i++)
	{
		float off = ((1 - alpha) * offsets1[i]) + (alpha * offsets2[i]);
		blended_offsets[i] = off;

		Vec2f n = ((1 - alpha) * normals1[i]) + (alpha * normals2[i]);
		blended_normals[i] = n;
	}

	// Reconstruct geometry on the new base
	reconstructGeometryCurve(curve, samples, blended_offsets, blended_normals, points, normals, isApprox);
}

void Synthesizer::blendGeometrySheets( Structure::Sheet * sheet, float alpha, const SynthData & data, QVector<Vector3f> &points, QVector<Vector3f> &normals, bool isApprox )
{
	alpha = qMax(0.0f, qMin(alpha, 1.0f));
	QVector<ParameterCoord> samples = data["node1"]["samples"].value< QVector<ParameterCoord> >();
	QVector<float> offsets1 = data["node1"]["offsets"].value< QVector<float> >();
	QVector<float> offsets2 = data["node2"]["offsets"].value< QVector<float> >();
	QVector<Vec2f> normals1 = data["node1"]["normals"].value< QVector<Vec2f> >();
	QVector<Vec2f> normals2 = data["node2"]["normals"].value< QVector<Vec2f> >();

	// Blend Geometries 
	int N = samples.size();
	QVector<float> blended_offsets(N);
	QVector<Vec2f> blended_normals(N);
	for( int i = 0; i < N; i++)
	{
		float off = ((1 - alpha) * offsets1[i]) + (alpha * offsets2[i]);
		blended_offsets[i] = off;

		Vec2f n = ((1 - alpha) * normals1[i]) + (alpha * normals2[i]);
		blended_normals[i] = n;
	}

	// Reconstruct geometry on the new base
	reconstructGeometrySheet(sheet, samples, blended_offsets, blended_normals, points, normals, isApprox);
}

/// I/O
void Synthesizer::writeXYZ( QString filename, std::vector<Vector3f> points, std::vector<Vector3f> normals )
{
	QFile file(filename);
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
	QTextStream out(&file);

	for(int i = 0; i < (int) points.size(); i++)
	{
		Vector3f p = points[i];
		Vector3f n = normals[i];
		out << QString("%1 %2 %3 %4 %5 %6\n").arg(p[0]).arg(p[1]).arg(p[2]).arg(n[0]).arg(n[1]).arg(n[2]);
	}

	file.close();
}

void Synthesizer::saveSynthesisData( Structure::Node *node, QString prefix, SynthData & input )
{
	if(!node) return;

	QString key = node->id;

	QVector<ParameterCoord> samples = input[key]["samples"].value< QVector<ParameterCoord> >();
	QVector<float> offsets = input[key]["offsets"].value< QVector<float> >();
	QVector<Vec2f> normals = input[key]["normals"].value< QVector<Vec2f> >();

	if(samples.empty())
	{
		qDebug() << QString("WARNING: Node [%1]: No synthesis data").arg(node->id);
		return;
	}

	QString synthFilename = prefix + node->id + ".txt";
	QFile file( synthFilename );
	QFileInfo fileinfo( file );

	QString filepath = fileinfo.absoluteFilePath();
	qDebug() << "Saving " << filepath;

	if (!file.open(QIODevice::WriteOnly)) return;
	QDataStream out(&file);

	out << samples.size();

	for(int i = 0; i < (int) samples.size(); i++)
	{
		out << samples[i].u << samples[i].v << samples[i].theta << samples[i].psi << 
			offsets[i] << normals[i][0] << normals[i][1];
	}

	file.close();
}

int Synthesizer::loadSynthesisData( Structure::Node *node, QString prefix, SynthData & output )
{
	if(!node) return 0;

	QFile file(prefix + node->id + ".txt");
	if (!file.open(QIODevice::ReadOnly)) 
	{
		qDebug() << QString("WARNING: Node [%1]: No synthesis data found").arg(node->id);
		return 0;
	}

	QDataStream inF(&file);

	int num;
	inF >> num;

	QVector<ParameterCoord> samples(num);
	QVector<float> offsets(num);
	QVector<Vec2f> normals(num);

	for(int i = 0; i < num; i++)
	{
		inF >> samples[i].u >> samples[i].v >> samples[i].theta >> samples[i].psi >> 
			offsets[i] >> normals[i][0] >> normals[i][1];
	}

	QString key = node->id;

	output[key]["samples"].setValue(samples);
	output[key]["offsets"].setValue(offsets);
	output[key]["normals"].setValue(normals);
	output[key]["samplesCount"].setValue(samples.size());

	return num;
}
back to top