[Bf-blender-cvs] [bb3b7a7] cycles-ptex-08: WIP Ptex texture packing

Nicholas Bishop noreply at git.blender.org
Tue Jan 20 13:57:10 CET 2015


Commit: bb3b7a7f31a070633d83aa274d596587c7e488db
Author: Nicholas Bishop
Date:   Fri Jan 16 12:27:41 2015 +0100
Branches: cycles-ptex-08
https://developer.blender.org/rBbb3b7a7f31a070633d83aa274d596587c7e488db

WIP Ptex texture packing

===================================================================

M	extern/ptex/CMakeLists.txt
A	extern/ptex/bl_ptex.h
A	extern/ptex/pack.cpp

===================================================================

diff --git a/extern/ptex/CMakeLists.txt b/extern/ptex/CMakeLists.txt
index 7e7bcd0..0eda505 100644
--- a/extern/ptex/CMakeLists.txt
+++ b/extern/ptex/CMakeLists.txt
@@ -24,12 +24,16 @@
 # ***** END GPL LICENSE BLOCK *****
 
 set(INC
+	.
 )
 
 set(INC_SYS
 )
 
 set(SRC
+	bl_ptex.h
+	pack.cpp
+
 	src/ptex/PtexCache.h
 	src/ptex/PtexDict.h
 	src/ptex/PtexHalf.h
diff --git a/extern/ptex/bl_ptex.h b/extern/ptex/bl_ptex.h
new file mode 100644
index 0000000..d022cd3
--- /dev/null
+++ b/extern/ptex/bl_ptex.h
@@ -0,0 +1,53 @@
+#ifndef __BL_PTEX_H__
+#define __BL_PTEX_H__
+
+#ifdef __cplusplus
+/* Silence warning */
+#	define NEW_API 0
+
+#	include "src/ptex/Ptexture.h"
+
+class PtexCache;
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PtexPackedTexture;
+struct PtexCacheHandle;
+
+struct PtexTableElement {
+	uint co[2];
+	uint res[2];
+};
+
+struct PtexCacheHandle *ptex_cache_new(void);
+void ptex_cache_delete(struct PtexCacheHandle *ptex_cache_handle);
+
+struct PtexPackedTexture *ptex_packed_texture_new(void);
+
+void ptex_packed_texture_delete(struct PtexPackedTexture *ppt);
+
+bool ptex_packed_texture_fill(struct PtexPackedTexture *output,
+							  const char *path,
+							  const int width,
+							  struct PtexCacheHandle *ptex_cache_handle);
+
+int ptex_packed_texture_width(const struct PtexPackedTexture *ppt);
+int ptex_packed_texture_height(const struct PtexPackedTexture *ppt);
+int ptex_packed_texture_bytes_per_texel(const struct PtexPackedTexture *ppt);
+int ptex_packed_texture_num_channels(const struct PtexPackedTexture *ppt);
+const void *ptex_packed_texture_texels(const struct PtexPackedTexture *ppt);
+int ptex_packed_texture_table_len(const struct PtexPackedTexture *ppt);
+
+const struct PtexTableElement *
+ptex_packed_texture_table_elem(const struct PtexPackedTexture *ppt,
+							   const int index);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif
diff --git a/extern/ptex/pack.cpp b/extern/ptex/pack.cpp
new file mode 100644
index 0000000..4c977f8
--- /dev/null
+++ b/extern/ptex/pack.cpp
@@ -0,0 +1,560 @@
+#include <OpenImageIO/imageio.h>
+OIIO_NAMESPACE_USING
+
+#include <cassert>
+#include <vector>
+
+#include "bl_ptex.h"
+
+// TODO, silly to copy utility classes...
+struct float2 {
+	float x, y;
+
+	void operator+=(const float2 &other)
+	{
+		x += other.x;
+		y += other.y;
+	}
+
+	float2 operator*(const float s) const
+	{
+		float2 result = {x * s, y * s};
+		return result;
+	}
+};
+
+static float2 make_float2(float x, float y)
+{
+	float2 f = {x, y};
+	return f;
+}
+
+// TODO, obviously not correct
+typedef unsigned int uint;
+typedef unsigned char uint8_t;
+
+using std::vector;
+
+struct PtexPackedTexture {
+	PtexPackedTexture() : width(0), height(0) {}
+
+	class Cursor {
+	public:
+		Cursor(PtexPackedTexture &output, const int start_x,
+			   const int start_y)
+		: output(output),
+		  offset(output.calc_offset(start_x, start_y))
+		{
+		}
+
+		void set_increment(const int new_incr)
+		{
+			incr = new_incr;
+		}
+
+		void step()
+		{
+			offset += incr;
+		}
+
+		uint8_t *data() {
+			return output.data(offset);
+		}
+
+	private:
+		PtexPackedTexture &output;
+		int incr;
+		int offset;
+	};
+
+	int calc_offset(const int x, const int y) const
+	{
+		return (y * width + x) * bytes_per_texel;
+	}
+
+	uint8_t *data(const int pixel_index)
+	{
+		assert(pixel_index >= 0);
+		assert(pixel_index < texels.size() - num_channels);
+
+		return texels.data() + pixel_index;
+	}
+
+	// TODO: more privacy here
+
+	vector<uint8_t> texels;
+
+	vector<PtexTableElement> table;
+
+	uint width;
+	uint height;
+
+	int num_channels;
+
+	int bytes_per_texel;
+	int dst_stride;
+
+	void test_write() {
+		// Quick test to visually examine the packed output
+		const char *filename = "/tmp/packed.png";
+		ImageOutput *out = ImageOutput::create(filename);
+		if (out) {
+			ImageSpec spec(width, height, num_channels, TypeDesc::UINT8);
+			out->open(filename, spec);
+			out->write_image(TypeDesc::UINT8, texels.data());
+			std::cout << "ptex test written: " << filename << std::endl;
+			out->close();
+		}
+	}
+};
+
+static int ptex_data_type_size_in_bytes(const Ptex::DataType dt)
+{
+	switch (dt) {
+		case Ptex::dt_uint8:
+			return 1;
+		case Ptex::dt_uint16:
+			return 2;
+		case Ptex::dt_half:
+			return 2;
+		case Ptex::dt_float:
+			return 4;
+	}
+
+	// Error
+	return 0;
+}
+
+struct PtexEdgeIter {
+	/* Set up a texel iterator for one edge of a face
+	 *
+	 * face and edge_id: specify the edge being iterated over
+	 *
+	 * from_res: the resolution of a neighboring face, controls the
+	 *           distance to step each time next() is called; next()
+	 *           can be called up to `from_res` times.
+	 *
+	 * reverse: iterate over the edge backwards. In general this is
+	 *          `true` because adjacent face edges have (implicit)
+	 *          opposite directions.
+	 */
+	PtexEdgeIter(PtexFaceData &face,
+				 const Ptex::EdgeId edge_id,
+				 const int from_res,
+				 const bool reverse)
+		: face(face)
+	{
+		const Ptex::Res &res = face.res();
+
+		float u_res = res.u() - 1.0f;
+		float v_res = res.v() - 1.0f;
+		const float u_incr = (u_res + 1.0f) / (float)from_res;
+		const float v_incr = (v_res + 1.0f) / (float)from_res;
+
+		switch (edge_id) {
+			case Ptex::e_bottom:
+				if (reverse) {
+					pos = make_float2(u_res, 0);
+					incr = make_float2(-u_incr, 0);
+				}
+				else {
+					pos = make_float2(0, 0);
+					incr = make_float2(u_incr, 0);
+				}
+				break;
+
+			case Ptex::e_right:
+				if (reverse) {
+					pos = make_float2(u_res, v_res);
+					incr = make_float2(0, -v_incr);
+				}
+				else {
+					pos = make_float2(u_res, 0);
+					incr = make_float2(0, v_incr);
+				}
+				break;
+
+			case Ptex::e_top:
+				if (reverse) {
+					pos = make_float2(0, v_res);
+					incr = make_float2(u_incr, 0);
+				}
+				else {
+					pos = make_float2(u_res, v_res);
+					incr = make_float2(-u_incr, 0);
+				}
+				break;
+
+			case Ptex::e_left:
+				if (reverse) {
+					pos = make_float2(0, 0);
+					incr = make_float2(0, v_incr);
+				}
+				else {
+					pos = make_float2(0, v_res);
+					incr = make_float2(0, -v_incr);
+				}
+		}
+	}
+
+	void step() {
+		pos += incr;
+	}
+
+	void step(int n) {
+		pos += incr * n;
+	}
+
+	void copy_num_texels(PtexPackedTexture::Cursor &cursor,
+						 const int num)
+	{
+		for (int i = 0; i < num; i++) {
+			copy_texel(cursor.data());
+			cursor.step();
+			step();
+		}
+	}
+
+	/* TODO(nicholasbishop): not doing any special filtering for
+	 * adjacent faces of differing resolutions, not sure how much that
+	 * will matter in practice */
+	void copy_texel(void *texel)
+	{
+		const int x = (int)(pos.x);
+		const int y = (int)(pos.y);
+		assert(x < face.res().u());
+		assert(y < face.res().v());
+		face.getPixel(x, y, texel);
+	}
+
+	PtexFaceData &face;
+	float2 pos;
+	float2 incr;
+};
+
+static void ptex_average_corner(PtexPtr<PtexTexture> &r,
+								const int start_face_id,
+								Ptex::EdgeId edge_id,
+								void *output)
+{
+	const Ptex::DataType data_type = r->dataType();
+	const int num_channels = r->numChannels();
+	assert(num_channels <= 4);
+
+	/* TODO: handle more than four channels */
+	float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+	/* TODO: probably special cases to handle at mesh boundaries?
+	 * Might need to traverse edges in both directions */
+
+	/* TODO: subface corners */
+
+	int face_id = start_face_id;
+	int num_adj = 0;
+	do {
+		float ftexel[4];
+		char texel[sizeof(float) * 4];
+
+		PtexFaceData &data = *r->getData(face_id);
+		const Ptex::FaceInfo &info = r->getFaceInfo(face_id);
+		const Ptex::Res res = data.res();
+		const int u_res = res.u() - 1;
+		const int v_res = res.v() - 1;
+		switch (edge_id) {
+			case Ptex::e_bottom:
+				data.getPixel(0, 0, texel);
+				break;
+			case Ptex::e_right:
+				data.getPixel(u_res, 0, texel);
+				break;
+			case Ptex::e_top:
+				data.getPixel(u_res, v_res, texel);
+				break;
+			case Ptex::e_left:
+				data.getPixel(0, v_res, texel);
+				break;
+		}
+
+		Ptex::ConvertToFloat(ftexel, texel, data_type, num_channels);
+		for (int i = 0; i < 4; i++) {
+			accum[i] += ftexel[i];
+		}
+		num_adj++;
+
+		if (num_adj > 10) {
+			// TODO, code is not yet correct so prevent infinite loops
+			break;
+		}
+
+		/* Move to next face and edge */
+		face_id = info.adjface(edge_id);
+		edge_id = (Ptex::EdgeId)(((int)info.adjedge(edge_id) + 1) % 4);
+		
+	} while (face_id != start_face_id && face_id != -1);
+
+	/* Average */
+	const float fac = 1.0f / (float)num_adj;
+	for (int i = 0; i < 4; i++) {
+		accum[i] *= fac;
+	}
+
+	/* Output */
+	Ptex::ConvertFromFloat(output, accum, data_type, num_channels);
+
+}
+
+static void ptex_copy_face_border(PtexPackedTexture::Cursor &cursor,
+								  PtexPtr<PtexTexture> &r,
+								  const int face_id,
+								  const Ptex::FaceInfo &info,
+								  const Ptex::EdgeId edge_id,
+								  const int side_res)
+{
+	ptex_average_corner(r, face_id, edge_id, cursor.data());
+	cursor.step();
+
+	/* In general there is only one adjacent face, but when a quad is
+	 * adjacent to a non-quad there are two adjacent subfaces */
+	int adj_face_id[2] = {info.adjface(edge_id), -1};
+	Ptex::EdgeId adj_edge[2] = {info.adjedge(edge_id)};
+
+	if (adj_face_id[0] == -1) {
+		/* If there's no adjacent face, pretend the face is adjacent
+		 * to itself (i.e. copy its own borders) */
+		PtexEdgeIter iter(*r->getData(face_id), edge_id, side_res, false);
+		iter.copy_num_texels(cursor, side_res);
+	}
+	else {
+		const Ptex::FaceInfo &adj_info = r->getFaceInfo(adj_face_id[0]);
+		const bool is_subface = info.isSubface();
+		if (!is_subface && adj_info.isSubface()) {
+			/* Face is adjacent to two subfaces */
+			int next_edge_id = (adj_edge[0] + 3) % 4;
+			adj_face_id[1] = adj_info.adjface(next_edge_id);
+			adj_edge[1] = adj_info.adjedge(next_edge_id);
+			adj_edge[1] = (Ptex::EdgeId)((adj_edge[1] + 3) % 4);
+		}
+
+		PtexFaceData *face_data[2] = {
+			r->getData(adj_face_id[0]),
+			adj_face_id[1] == -1 ? NULL : r->getData(adj_face_id[1])
+		};
+
+		if (is_subface && !adj_info.isSubface()) {
+			PtexEdgeIter iter(*face_data[0], adj_edge[0], side_res * 2, true);
+			const bool is_primary = adj_info.adjface(ad

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list