TriObject *createTriangleMesh(const std::vector<Point3> &points,
const std::vector<Point3> &normals,
const std::vector<Point2> &uvs,
const std::vector<int> &triangleVertIndices)
{
TriObject *triobj = CreateNewTriObject();
if (triobj == NULL)
return NULL;
assert(points.size() == normals.size() && normals.size() == uvs.size());
assert(triangleVertIndices.size() % 3 == 0);
int numVertices = (int) points.size();
int numTriangles = (int) triangleVertIndices.size() / 3;
Mesh &mesh = triobj->GetMesh();
// set vertex positions
mesh.setNumVerts(numVertices);
for (int i = 0; i < numVertices; i++)
mesh.setVert(i, points[i]);
// set vertex normals
mesh.SpecifyNormals();
MeshNormalSpec *normalSpec = mesh.GetSpecifiedNormals();
normalSpec->ClearNormals();
normalSpec->SetNumNormals(numVertices);
for (int i = 0; i < numVertices; i++)
{
normalSpec->Normal(i) = normals[i].Normalize();
normalSpec->SetNormalExplicit(i, true);
}
// set UVs
// TODO: multiple map channels?
// channel 0 is reserved for vertex color, channel 1 is the default texture mapping
mesh.setNumMaps(2);
mesh.setMapSupport(1, TRUE); // enable map channel
MeshMap &map = mesh.Map(1);
map.setNumVerts(numVertices);
for (int i = 0; i < numVertices; i++)
{
UVVert &texVert = map.tv[i];
texVert.x = uvs[i].x;
texVert.y = uvs[i].y;
texVert.z = 0.0f;
}
// set triangles
mesh.setNumFaces(numTriangles);
normalSpec->SetNumFaces(numTriangles);
map.setNumFaces(numTriangles);
for (int i = 0, j = 0; i < numTriangles; i++, j += 3)
{
// three vertex indices of a triangle
int v0 = triangleVertIndices[j];
int v1 = triangleVertIndices[j+1];
int v2 = triangleVertIndices[j+2];
// vertex positions
Face &face = mesh.faces[i];
face.setMatID(1);
face.setEdgeVisFlags(1, 1, 1);
face.setVerts(v0, v1, v2);
// vertex normals
MeshNormalFace &normalFace = normalSpec->Face(i);
normalFace.SpecifyAll();
normalFace.SetNormalID(0, v0);
normalFace.SetNormalID(1, v1);
normalFace.SetNormalID(2, v2);
// vertex UVs
TVFace &texFace = map.tf[i];
texFace.setTVerts(v0, v1, v2);
}
mesh.InvalidateGeomCache();
mesh.InvalidateTopologyCache();
return triobj;
}
Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts
October 28, 2011
Creating a Triangle Mesh with 3ds Max SDK
I have been working on developing 3ds Max plug-ins lately. Comparing to Maya API, 3ds Max SDK is a hell of a mess. Inconsistent naming styles, lots of exposed pointers, and poor documentations. It took me a while to achieve this common task: Creating a triangle mesh. The following code is what I came up with eventually. Most of it came from OpenCOLLADA. Enjoy!
July 22, 2011
A Gotcha of C++ map and set
C++ std::map and std::set allow you to define your own less-than comparator for the key type. But one thing to bear in mind: The comparator must follow the rules of strick weak ordering! That is:
To see what it means, let me show you a mistake I made.
I was thinking of keeping a bunch of non-repeated (unique) floats, but I wish to compare the floats with some tolerance. For example, 1.0 and 1.01 should be same. So naturally, I used float as the map key, and the mapped value can be any type I need. Let's say here I use
In order to compare floats with tolerance, I wrote a float comparator like this:
The answer: I broke the rule of "transitivity of equivalence" (
So how to fix this? Well, as long as "transitivity of equivalence" is followed, there would be no problems. Finally I came up with this new comparator:
x < x is always false(irreflexivity)If x < y, then y < x is false(asymmetric)If x < y and y < z, then x < z(transitivity)If x = y and y = z, then x = z(transitivity of equivalence)
To see what it means, let me show you a mistake I made.
I was thinking of keeping a bunch of non-repeated (unique) floats, but I wish to compare the floats with some tolerance. For example, 1.0 and 1.01 should be same. So naturally, I used float as the map key, and the mapped value can be any type I need. Let's say here I use
int.In order to compare floats with tolerance, I wrote a float comparator like this:
struct FloatCmp
{
bool operator()(float a, float b)
{
const float epsilon = 0.01f;
if (fabsf(a - b) < epsilon)
return false; // a == b
return a < b;
}
};
You can see line 6-7 checks if two floats are equal first before using the less-than comparison (line 8). Now, I can initialize my map container happily:std::map<float, int, FloatCmp> table; table[1.0f] = 1; table[1.001f] = 2; // same as table[1.0f] = 2 table[1.002f] = 3; // same as table[1.0f] = 3It all seemed to work perfectly. But I found sometimes it didn't work. It produced some duplicated keys. What did I do wrong?
The answer: I broke the rule of "transitivity of equivalence" (
if x = y and y = z then x = z). Both FloatCmp(1.0f, 1.006f) and FloatCmp(1.006f, 1.012f) are true, but FloatCmp(1.0f, 1.012f) is not true! This makes the result depend on how you insert your elements. Consider the following code:#include <iostream>
#include <map>
int main()
{
std::map<float, int, FloatCmp> map1;
std::map<float, int, FloatCmp> map2;
float a = 1.000f;
float b = 1.006f;
float c = 1.012f;
map1[a] = 1;
map1[b] = 1;
map1[c] = 1;
map2[b] = 1;
map2[a] = 1;
map2[c] = 1;
std::cout << map1.size() << std::endl; // print 2
std::cout << map2.size() << std::endl; // print 1
return 0;
}
After the insertions, map1 contains a and c, and map2 contains b. This is astonishing: Two maps with the same set of elements inserted can have different sizes because you insert them in different orders. It's not hard to figure out why by tracing the code.So how to fix this? Well, as long as "transitivity of equivalence" is followed, there would be no problems. Finally I came up with this new comparator:
inline float discretize(float a)
{
return floorf(a * 100.0f) / 100.0f;
}
struct FloatCmp
{
bool operator()(float a, float b)
{
float aa = discretize(a);
float bb = discretize(b);
if (aa == bb)
return false;
return aa < bb;
}
};
At line 9-10, discretize() function truncates (round off) floating-point numbers to 2 decimal digits. For example, discretize(1.1234f) returns 1.12f. And check if they're equal with == operator directly (line 11). In this way, I make sure "transitivity of equivalence" is strictly followed. Now if you relaunch the previous main program with this new comparator, both map1 and map2 will contain two elements as expected.July 4, 2011
Python shelve vs. sqlite3
I have a huge Python dict that I can't store in the memory. Python provides a shelve module for this purpose. It acts like a regular dict but it can be saved into a file. I wonder its performance comparing to sqlite3, so I run this little test:
import shelve
import sqlite3
def test_sqlite3():
conn = sqlite3.connect("debug.s3db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS [mydict] ("
"[key] VARCHAR(255) PRIMARY KEY NOT NULL, "
"[value] VARCHAR(255) NOT NULL)")
for i in xrange(0, 1000000):
cur.execute("INSERT INTO [mydict] (key, value) VALUES (?, ?)",
(str(i), str(i*2)))
conn.commit()
cur.close()
conn.close()
def test_shelve():
d = shelve.open("debug.shelf")
for i in xrange(0, 1000000):
d[str(i)] = str(i*2)
d.close()
On my computer, test_sqlite3() cost me about 25 seconds, and about 51 seconds for test_shelve(). I also performed some fetch queries on these two, sqlite3 still runs faster than shelve. In conclusion, sqlite3 wins!June 24, 2011
How to check whether an OptiX variable is initialized or not?
In OptiX/CUDA programs (*.cu), simple type variables can always be initialized by putting an assignment operator immediately after rtDeclareVariable:
// in CUDA program
rtDeclareVariable(int, an_integer, , ) = 0;
rtDeclareVariable(float, a_float, , ) = 0.0f;
rtDeclareVariable(float3, a_vector, , ) = { 1.0f, 0.0f, 0.0f };
But for complex type variables, such as TextureSampler and Buffer, the only way to initialize them is to set the variables in host (CPU) programs. To prevent OptiX from recompiling, make sure you initialize your OptiX variables only at the beginning and only ONCE. So to do this, you can use Variable::getType() to check if an OptiX variable is initialized:// in CUDA program rtBuffer<float, 1> a_buffer; rtTextureSampler<float4, 2> a_texture;
// in host program
Variable buf_var = context["a_buffer"];
if (buf_var->getType() == RT_OBJECTTYPE_UNKNOWN)
{
// initialize buf_var...
Buffer buf = context->createBuffer(...);
buf_var->setBuffer(buf);
}
Variable tex_var = context["a_texture"];
if (tex_var->getType() == RT_OBJECTTYPE_UNKNOWN)
{
// initialize tex_var...
TextureSampler sampler = context->createTextureSampler(...);
tex_var->setTextureSampler(sampler);
}
// now you can dereference the variables safely
Buffer buf = context["a_buffer"]->getBuffer();
TextureSampler tex = context["a_tex"]->getTextureSampler();
May 31, 2011
Getting NVIDIA GPU Usage in C++
Strangely enough, NVAPI has no functions to get GPU usage/load. It turns out that there are some secret functions in nvapi.dll. You can use QueryInterface function to retrieve them by specifying the memory address of the function. Here's the code for Windows.
//
// Getting Nvidia GPU Usage
//
// Reference: Open Hardware Monitor (http://code.google.com/p/open-hardware-monitor)
//
#include <windows.h>
#include <iostream>
// magic numbers, do not change them
#define NVAPI_MAX_PHYSICAL_GPUS 64
#define NVAPI_MAX_USAGES_PER_GPU 34
// function pointer types
typedef int *(*NvAPI_QueryInterface_t)(unsigned int offset);
typedef int (*NvAPI_Initialize_t)();
typedef int (*NvAPI_EnumPhysicalGPUs_t)(int **handles, int *count);
typedef int (*NvAPI_GPU_GetUsages_t)(int *handle, unsigned int *usages);
int main()
{
HMODULE hmod = LoadLibraryA("nvapi.dll");
if (hmod == NULL)
{
std::cerr << "Couldn't find nvapi.dll" << std::endl;
return 1;
}
// nvapi.dll internal function pointers
NvAPI_QueryInterface_t NvAPI_QueryInterface = NULL;
NvAPI_Initialize_t NvAPI_Initialize = NULL;
NvAPI_EnumPhysicalGPUs_t NvAPI_EnumPhysicalGPUs = NULL;
NvAPI_GPU_GetUsages_t NvAPI_GPU_GetUsages = NULL;
// nvapi_QueryInterface is a function used to retrieve other internal functions in nvapi.dll
NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress(hmod, "nvapi_QueryInterface");
// some useful internal functions that aren't exported by nvapi.dll
NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828);
NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F);
NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF);
if (NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL ||
NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL)
{
std::cerr << "Couldn't get functions in nvapi.dll" << std::endl;
return 2;
}
// initialize NvAPI library, call it once before calling any other NvAPI functions
(*NvAPI_Initialize)();
int gpuCount = 0;
int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 };
// gpuUsages[0] must be this value, otherwise NvAPI_GPU_GetUsages won't work
gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000;
(*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount);
// print GPU usage every second
for (int i = 0; i < 100; i++)
{
(*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages);
int usage = gpuUsages[3];
std::cout << "GPU Usage: " << usage << std::endl;
Sleep(1000);
}
return 0;
}
Subscribe to:
Posts (Atom)