예제 #1
0
/**
 * Reads the contents of a text file and returns the lines in a linked list.
 */
LinkNode *BLI_file_read_as_lines(const char *name)
{
	FILE *fp = BLI_fopen(name, "r");
	LinkNodePair lines = {NULL, NULL};
	char *buf;
	size_t size;

	if (!fp) return NULL;
		
	fseek(fp, 0, SEEK_END);
	size = (size_t)ftell(fp);
	fseek(fp, 0, SEEK_SET);

	if (UNLIKELY(size == (size_t)-1)) {
		fclose(fp);
		return NULL;
	}

	buf = MEM_mallocN(size, "file_as_lines");
	if (buf) {
		size_t i, last = 0;
		
		/*
		 * size = because on win32 reading
		 * all the bytes in the file will return
		 * less bytes because of crnl changes.
		 */
		size = fread(buf, 1, size, fp);
		for (i = 0; i <= size; i++) {
			if (i == size || buf[i] == '\n') {
				char *line = BLI_strdupn(&buf[last], i - last);

				BLI_linklist_append(&lines, line);
				/* faster to build singly-linked list in reverse order */
				/* alternatively, could process buffer in reverse order so
				 * list ends up right way round to start with */
				last = i + 1;
			}
		}
		
		MEM_freeN(buf);
	}
	
	fclose(fp);

	return lines.list;
}
예제 #2
0
static int create_navmesh_exec(bContext *C, wmOperator *op)
{
	Scene* scene= CTX_data_scene(C);
	LinkNode* obs= NULL;
	Base* navmeshBase= NULL;

	CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
		if (base->object->type == OB_MESH) {
			if (base->object->body_type==OB_BODY_TYPE_NAVMESH) {
				if (!navmeshBase || base == scene->basact) {
					navmeshBase= base;
				}
			}
			else {
				BLI_linklist_append(&obs, (void*)base->object);
			}
		}
	}
	CTX_DATA_END;

	if (obs) {
		struct recast_polyMesh *pmesh= NULL;
		struct recast_polyMeshDetail *dmesh= NULL;

		int nverts= 0, ntris= 0;
		int *tris= 0;
		float *verts= NULL;

		createVertsTrisData(C, obs, &nverts, &verts, &ntris, &tris);
		BLI_linklist_free(obs, NULL);
		buildNavMesh(&scene->gm.recastData, nverts, verts, ntris, tris, &pmesh, &dmesh);
		createRepresentation(C, pmesh, dmesh, navmeshBase);

		MEM_freeN(verts);
		MEM_freeN(tris);

		return OPERATOR_FINISHED;
	}
	else {
		BKE_report(op->reports, RPT_ERROR, "No mesh objects found");

		return OPERATOR_CANCELLED;
	}
}
예제 #3
0
/**
 * Reads the contents of a text file and returns the lines in a linked list.
 */
LinkNode *BLI_file_read_as_lines(const char *name)
{
	FILE *fp = BLI_fopen(name, "r");
	LinkNodePair lines = {NULL, NULL};
	char *buf;
	size_t size;

	if (!fp) return NULL;

	fseek(fp, 0, SEEK_END);
	size = (size_t)ftell(fp);
	fseek(fp, 0, SEEK_SET);

	if (UNLIKELY(size == (size_t)-1)) {
		fclose(fp);
		return NULL;
	}

	buf = MEM_mallocN(size, "file_as_lines");
	if (buf) {
		size_t i, last = 0;

		/*
		 * size = because on win32 reading
		 * all the bytes in the file will return
		 * less bytes because of `CRNL` changes.
		 */
		size = fread(buf, 1, size, fp);
		for (i = 0; i <= size; i++) {
			if (i == size || buf[i] == '\n') {
				char *line = BLI_strdupn(&buf[last], i - last);
				BLI_linklist_append(&lines, line);
				last = i + 1;
			}
		}

		MEM_freeN(buf);
	}

	fclose(fp);

	return lines.list;
}
예제 #4
0
static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm )
{
	Cloth *cloth = clmd->clothObject;
	ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL;
	unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0;
	unsigned int i = 0;
	unsigned int numverts = (unsigned int)dm->getNumVerts ( dm );
	unsigned int numedges = (unsigned int)dm->getNumEdges ( dm );
	unsigned int numfaces = (unsigned int)dm->getNumFaces ( dm );
	MEdge *medge = dm->getEdgeArray ( dm );
	MFace *mface = dm->getFaceArray ( dm );
	int index2 = 0; // our second vertex index
	LinkNode **edgelist = NULL;
	EdgeHash *edgehash = NULL;
	LinkNode *search = NULL, *search2 = NULL;
	
	// error handling
	if ( numedges==0 )
		return 0;

	cloth->springs = NULL;

	edgelist = MEM_callocN ( sizeof ( LinkNode * ) * numverts, "cloth_edgelist_alloc" );
	
	if(!edgelist)
		return 0;
	
	for ( i = 0; i < numverts; i++ )
	{
		edgelist[i] = NULL;
	}

	if ( cloth->springs )
		MEM_freeN ( cloth->springs );

	// create spring network hash
	edgehash = BLI_edgehash_new();

	// structural springs
	for ( i = 0; i < numedges; i++ )
	{
		spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );

		if ( spring )
		{
			spring->ij = MIN2(medge[i].v1, medge[i].v2);
			spring->kl = MAX2(medge[i].v2, medge[i].v1);
			spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
			clmd->sim_parms->avg_spring_len += spring->restlen;
			cloth->verts[spring->ij].avg_spring_len += spring->restlen;
			cloth->verts[spring->kl].avg_spring_len += spring->restlen;
			cloth->verts[spring->ij].spring_count++;
			cloth->verts[spring->kl].spring_count++;
			spring->type = CLOTH_SPRING_TYPE_STRUCTURAL;
			spring->flags = 0;
			spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
			struct_springs++;
			
			BLI_linklist_prepend ( &cloth->springs, spring );
		}
		else
		{
			cloth_free_errorsprings(cloth, edgehash, edgelist);
			return 0;
		}
	}
	
	if(struct_springs > 0)
		clmd->sim_parms->avg_spring_len /= struct_springs;
	
	for(i = 0; i < numverts; i++)
	{
		cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count);
	}
	
	// shear springs
	for ( i = 0; i < numfaces; i++ )
	{
		// triangle faces already have shear springs due to structural geometry
		if ( !mface[i].v4 )
			continue; 
		
		spring = ( ClothSpring *) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
		
		if(!spring)
		{
			cloth_free_errorsprings(cloth, edgehash, edgelist);
			return 0;
		}

		spring->ij = MIN2(mface[i].v1, mface[i].v3);
		spring->kl = MAX2(mface[i].v3, mface[i].v1);
		spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
		spring->type = CLOTH_SPRING_TYPE_SHEAR;
		spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;

		BLI_linklist_append ( &edgelist[spring->ij], spring );
		BLI_linklist_append ( &edgelist[spring->kl], spring );
		shear_springs++;

		BLI_linklist_prepend ( &cloth->springs, spring );

		
		// if ( mface[i].v4 ) --> Quad face
		spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
		
		if(!spring)
		{
			cloth_free_errorsprings(cloth, edgehash, edgelist);
			return 0;
		}

		spring->ij = MIN2(mface[i].v2, mface[i].v4);
		spring->kl = MAX2(mface[i].v4, mface[i].v2);
		spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
		spring->type = CLOTH_SPRING_TYPE_SHEAR;
		spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0;

		BLI_linklist_append ( &edgelist[spring->ij], spring );
		BLI_linklist_append ( &edgelist[spring->kl], spring );
		shear_springs++;

		BLI_linklist_prepend ( &cloth->springs, spring );
	}
	
	if(numfaces) {
		// bending springs
		search2 = cloth->springs;
		for ( i = struct_springs; i < struct_springs+shear_springs; i++ )
		{
			if ( !search2 )
				break;

			tspring2 = search2->link;
			search = edgelist[tspring2->kl];
			while ( search )
			{
				tspring = search->link;
				index2 = ( ( tspring->ij==tspring2->kl ) ? ( tspring->kl ) : ( tspring->ij ) );
				
				// check for existing spring
				// check also if startpoint is equal to endpoint
				if ( !BLI_edgehash_haskey ( edgehash, MIN2(tspring2->ij, index2), MAX2(tspring2->ij, index2) )
				&& ( index2!=tspring2->ij ) )
				{
					spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
					
					if(!spring)
					{
						cloth_free_errorsprings(cloth, edgehash, edgelist);
						return 0;
					}

					spring->ij = MIN2(tspring2->ij, index2);
					spring->kl = MAX2(tspring2->ij, index2);
					spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
					spring->type = CLOTH_SPRING_TYPE_BENDING;
					spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
					BLI_edgehash_insert ( edgehash, spring->ij, spring->kl, NULL );
					bend_springs++;

					BLI_linklist_prepend ( &cloth->springs, spring );
				}
				search = search->next;
			}
			search2 = search2->next;
		}
	}
	else if(struct_springs > 2) {
		/* bending springs for hair strands */
		/* The current algorightm only goes through the edges in order of the mesh edges list	*/
		/* and makes springs between the outer vert of edges sharing a vertice. This works just */
		/* fine for hair, but not for user generated string meshes. This could/should be later	*/
		/* extended to work with non-ordered edges so that it can be used for general "rope		*/
		/* dynamics" without the need for the vertices or edges to be ordered through the length*/
		/* of the strands. -jahka */
		search = cloth->springs;
		search2 = search->next;
		while(search && search2)
		{
			tspring = search->link;
			tspring2 = search2->link;

			if(tspring->ij == tspring2->kl) {
				spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
				
				if(!spring)
				{
					cloth_free_errorsprings(cloth, edgehash, edgelist);
					return 0;
				}

				spring->ij = tspring2->ij;
				spring->kl = tspring->kl;
				spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
				spring->type = CLOTH_SPRING_TYPE_BENDING;
				spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
				bend_springs++;

				BLI_linklist_prepend ( &cloth->springs, spring );
			}
			
			search = search->next;
			search2 = search2->next;
		}
	}
	
	/* insert other near springs in edgehash AFTER bending springs are calculated (for selfcolls) */
	for ( i = 0; i < numedges; i++ ) // struct springs
		BLI_edgehash_insert ( edgehash, MIN2(medge[i].v1, medge[i].v2), MAX2(medge[i].v2, medge[i].v1), NULL );
	
	for ( i = 0; i < numfaces; i++ ) // edge springs
	{
		if(mface[i].v4)
		{
			BLI_edgehash_insert ( edgehash, MIN2(mface[i].v1, mface[i].v3), MAX2(mface[i].v3, mface[i].v1), NULL );
			
			BLI_edgehash_insert ( edgehash, MIN2(mface[i].v2, mface[i].v4), MAX2(mface[i].v2, mface[i].v4), NULL );
		}
	}
	
	
	cloth->numsprings = struct_springs + shear_springs + bend_springs;
	
	if ( edgelist )
	{
		for ( i = 0; i < numverts; i++ )
		{
			BLI_linklist_free ( edgelist[i],NULL );
		}
	
		MEM_freeN ( edgelist );
	}
	
	cloth->edgehash = edgehash;
	
	if(G.rt>0)
		printf("avg_len: %f\n",clmd->sim_parms->avg_spring_len);

	return 1;

} /* cloth_build_springs */
예제 #5
0
static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm )
{
	Cloth *cloth = clmd->clothObject;
	ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL;
	unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0;
	unsigned int i = 0;
	unsigned int numverts = (unsigned int)dm->getNumVerts (dm);
	unsigned int numedges = (unsigned int)dm->getNumEdges (dm);
	unsigned int numfaces = (unsigned int)dm->getNumTessFaces (dm);
	float shrink_factor;
	MEdge *medge = dm->getEdgeArray (dm);
	MFace *mface = dm->getTessFaceArray (dm);
	int index2 = 0; // our second vertex index
	LinkNode **edgelist = NULL;
	EdgeSet *edgeset = NULL;
	LinkNode *search = NULL, *search2 = NULL;
	
	// error handling
	if ( numedges==0 )
		return 0;

	/* NOTE: handling ownership of springs and edgeset is quite sloppy
	 * currently they are never initialized but assert just to be sure */
	BLI_assert(cloth->springs == NULL);
	BLI_assert(cloth->edgeset == NULL);

	cloth->springs = NULL;
	cloth->edgeset = NULL;

	edgelist = MEM_callocN ( sizeof (LinkNode *) * numverts, "cloth_edgelist_alloc" );
	
	if (!edgelist)
		return 0;

	// structural springs
	for ( i = 0; i < numedges; i++ ) {
		spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );

		if ( spring ) {
			spring_verts_ordered_set(spring, medge[i].v1, medge[i].v2);
			if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && medge[i].flag & ME_LOOSEEDGE) {
				// handle sewing (loose edges will be pulled together)
				spring->restlen = 0.0f;
				spring->stiffness = 1.0f;
				spring->type = CLOTH_SPRING_TYPE_SEWING;
			}
			else {
				if (clmd->sim_parms->vgroup_shrink > 0)
					shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f);
				else
					shrink_factor = 1.0f - clmd->sim_parms->shrink_min;
				spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
				spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
				spring->type = CLOTH_SPRING_TYPE_STRUCTURAL;
			}
			clmd->sim_parms->avg_spring_len += spring->restlen;
			cloth->verts[spring->ij].avg_spring_len += spring->restlen;
			cloth->verts[spring->kl].avg_spring_len += spring->restlen;
			cloth->verts[spring->ij].spring_count++;
			cloth->verts[spring->kl].spring_count++;
			spring->flags = 0;
			struct_springs++;
			
			BLI_linklist_prepend ( &cloth->springs, spring );
		}
		else {
			cloth_free_errorsprings(cloth, edgelist);
			return 0;
		}
	}

	if (struct_springs > 0)
		clmd->sim_parms->avg_spring_len /= struct_springs;
	
	for (i = 0; i < numverts; i++) {
		cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count);
	}

	// shear springs
	for ( i = 0; i < numfaces; i++ ) {
		// triangle faces already have shear springs due to structural geometry
		if ( !mface[i].v4 )
			continue;

		spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
		
		if (!spring) {
			cloth_free_errorsprings(cloth, edgelist);
			return 0;
		}

		spring_verts_ordered_set(spring, mface[i].v1, mface[i].v3);
		if (clmd->sim_parms->vgroup_shrink > 0)
			shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f);
		else
			shrink_factor = 1.0f - clmd->sim_parms->shrink_min;
		spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
		spring->type = CLOTH_SPRING_TYPE_SHEAR;
		spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;

		BLI_linklist_append ( &edgelist[spring->ij], spring );
		BLI_linklist_append ( &edgelist[spring->kl], spring );
		shear_springs++;

		BLI_linklist_prepend ( &cloth->springs, spring );

		
		// if ( mface[i].v4 ) --> Quad face
		spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
		
		if (!spring) {
			cloth_free_errorsprings(cloth, edgelist);
			return 0;
		}

		spring_verts_ordered_set(spring, mface[i].v2, mface[i].v4);
		if (clmd->sim_parms->vgroup_shrink > 0)
			shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f);
		else
			shrink_factor = 1.0f - clmd->sim_parms->shrink_min;
		spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
		spring->type = CLOTH_SPRING_TYPE_SHEAR;
		spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;

		BLI_linklist_append ( &edgelist[spring->ij], spring );
		BLI_linklist_append ( &edgelist[spring->kl], spring );
		shear_springs++;

		BLI_linklist_prepend ( &cloth->springs, spring );
	}

	edgeset = BLI_edgeset_new_ex(__func__, numedges);
	cloth->edgeset = edgeset;

	if (numfaces) {
		// bending springs
		search2 = cloth->springs;
		for ( i = struct_springs; i < struct_springs+shear_springs; i++ ) {
			if ( !search2 )
				break;

			tspring2 = search2->link;
			search = edgelist[tspring2->kl];
			while ( search ) {
				tspring = search->link;
				index2 = ( ( tspring->ij==tspring2->kl ) ? ( tspring->kl ) : ( tspring->ij ) );

				// check for existing spring
				// check also if startpoint is equal to endpoint
				if ((index2 != tspring2->ij) &&
				    !BLI_edgeset_haskey(edgeset, tspring2->ij, index2))
				{
					spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );

					if (!spring) {
						cloth_free_errorsprings(cloth, edgelist);
						return 0;
					}

					spring_verts_ordered_set(spring, tspring2->ij, index2);
					spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
					spring->type = CLOTH_SPRING_TYPE_BENDING;
					spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
					BLI_edgeset_insert(edgeset, spring->ij, spring->kl);
					bend_springs++;

					BLI_linklist_prepend ( &cloth->springs, spring );
				}
				search = search->next;
			}
			search2 = search2->next;
		}
	}
	else if (struct_springs > 2) {
		/* bending springs for hair strands */
		/* The current algorightm only goes through the edges in order of the mesh edges list	*/
		/* and makes springs between the outer vert of edges sharing a vertice. This works just */
		/* fine for hair, but not for user generated string meshes. This could/should be later	*/
		/* extended to work with non-ordered edges so that it can be used for general "rope		*/
		/* dynamics" without the need for the vertices or edges to be ordered through the length*/
		/* of the strands. -jahka */
		search = cloth->springs;
		search2 = search->next;
		while (search && search2) {
			tspring = search->link;
			tspring2 = search2->link;

			if (tspring->ij == tspring2->kl) {
				spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
				
				if (!spring) {
					cloth_free_errorsprings(cloth, edgelist);
					return 0;
				}

				spring->ij = tspring2->ij;
				spring->kl = tspring->kl;
				spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
				spring->type = CLOTH_SPRING_TYPE_BENDING;
				spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
				bend_springs++;

				BLI_linklist_prepend ( &cloth->springs, spring );
			}
			
			search = search->next;
			search2 = search2->next;
		}
	}
	
	/* note: the edges may already exist so run reinsert */

	/* insert other near springs in edgeset AFTER bending springs are calculated (for selfcolls) */
	for (i = 0; i < numedges; i++) { /* struct springs */
		BLI_edgeset_add(edgeset, medge[i].v1, medge[i].v2);
	}

	for (i = 0; i < numfaces; i++) { /* edge springs */
		if (mface[i].v4) {
			BLI_edgeset_add(edgeset, mface[i].v1, mface[i].v3);
			
			BLI_edgeset_add(edgeset, mface[i].v2, mface[i].v4);
		}
	}
	
	
	cloth->numsprings = struct_springs + shear_springs + bend_springs;
	
	cloth_free_edgelist(edgelist, numverts);

#if 0
	if (G.debug_value > 0)
		printf("avg_len: %f\n", clmd->sim_parms->avg_spring_len);
#endif

	return 1;

} /* cloth_build_springs */
예제 #6
0
static void createVertsTrisData(bContext *C, LinkNode* obs, int *nverts_r, float **verts_r, int *ntris_r, int **tris_r)
{
	MVert *mvert;
	int nfaces= 0, *tri, i, curnverts, basenverts, curnfaces;
	MFace *mface;
	float co[3], wco[3];
	Object *ob;
	LinkNode *oblink, *dmlink;
	DerivedMesh *dm;
	Scene* scene= CTX_data_scene(C);
	LinkNode* dms= NULL;

	int nverts, ntris, *tris;
	float *verts;

	nverts= 0;
	ntris= 0;

	/* calculate number of verts and tris */
	for(oblink= obs; oblink; oblink= oblink->next) {
		ob= (Object*) oblink->link;
		dm= mesh_create_derived_no_virtual(scene, ob, NULL, CD_MASK_MESH);
		BLI_linklist_append(&dms, (void*)dm);

		nverts+= dm->getNumVerts(dm);
		nfaces= dm->getNumFaces(dm);
		ntris+= nfaces;

		/* resolve quad faces */
		mface= dm->getFaceArray(dm);
		for(i= 0; i<nfaces; i++) {
			MFace* mf= &mface[i];
			if(mf->v4)
				ntris+=1;
		}
	}

	/* create data */
	verts= MEM_mallocN(sizeof(float)*3*nverts, "createVertsTrisData verts");
	tris= MEM_mallocN(sizeof(int)*3*ntris, "createVertsTrisData faces");

	basenverts= 0;
	tri= tris;
	for(oblink= obs, dmlink= dms; oblink && dmlink;
			oblink= oblink->next, dmlink= dmlink->next) {
		ob= (Object*) oblink->link;
		dm= (DerivedMesh*) dmlink->link;

		curnverts= dm->getNumVerts(dm);
		mvert= dm->getVertArray(dm);

		/* copy verts */
		for(i= 0; i<curnverts; i++) {
			MVert *v= &mvert[i];

			copy_v3_v3(co, v->co);
			mul_v3_m4v3(wco, ob->obmat, co);

			verts[3*(basenverts+i)+0]= wco[0];
			verts[3*(basenverts+i)+1]= wco[2];
			verts[3*(basenverts+i)+2]= wco[1];
		}

		/* create tris */
		curnfaces= dm->getNumFaces(dm);
		mface= dm->getFaceArray(dm);

		for(i= 0; i<curnfaces; i++) {
			MFace* mf= &mface[i];

			tri[0]= basenverts + mf->v1;
			tri[1]= basenverts + mf->v3;
			tri[2]= basenverts + mf->v2;
			tri += 3;

			if(mf->v4) {
				tri[0]= basenverts + mf->v1;
				tri[1]= basenverts + mf->v4;
				tri[2]= basenverts + mf->v3;
				tri += 3;
			}
		}

		basenverts+= curnverts;
	}

	/* release derived mesh */
	for(dmlink= dms; dmlink; dmlink= dmlink->next) {
		dm= (DerivedMesh*) dmlink->link;
		dm->release(dm);
	}

	BLI_linklist_free(dms, NULL);

	*nverts_r= nverts;
	*verts_r= verts;
	*ntris_r= ntris;
	*tris_r= tris;
}