static void _ase_submit_triangles_unshared ( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, int meshHasNormals ) { aseFacesIter_t i = faces, end = faces + numFaces; aseUniqueIndices_t indices; aseUniqueIndices_t remap; aseUniqueIndices_reserve(&indices, numFaces * 3); aseUniqueIndices_reserve(&remap, numFaces * 3); indices.faces = faces; for(; i != end; ++i) { /* look up the shader for the material/submaterial pair */ aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); if( subMtl == NULL ) { return; } { picoSurface_t* surface = PicoModelFindOrAddSurface(model, subMtl->shader); int j; /* we pull the data from the vertex, color and texcoord arrays using the face index data */ for ( j = 0 ; j < 3 ; j ++ ) { picoIndex_t index = (picoIndex_t)(((i - faces) * 3) + j); picoIndex_t size = (picoIndex_t)aseUniqueIndices_size(&indices); picoIndex_t unique = aseUniqueIndices_insertUniqueVertex(&indices, index); picoIndex_t numVertexes = PicoGetSurfaceNumVertexes(surface); picoIndex_t numIndexes = PicoGetSurfaceNumIndexes(surface); aseUniqueIndices_pushBack(&remap, numIndexes); PicoSetSurfaceIndex(surface, numIndexes, remap.data[unique]); if(unique == size) { PicoSetSurfaceXYZ(surface, numVertexes, vertices[(*i).indices[j]].xyz); PicoSetSurfaceNormal(surface, numVertexes, vertices[(*i).indices[j]].normal); PicoSetSurfaceST(surface, 0, numVertexes, texcoords[(*i).indices[j + 3]].texcoord); if ( (*i).indices[j + 6] >= 0 ) { PicoSetSurfaceColor(surface, 0, numVertexes, colors[(*i).indices[j + 6]].color); } else { PicoSetSurfaceColor(surface, 0, numVertexes, white); } PicoSetSurfaceSmoothingGroup(surface, numVertexes, (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup); } } } } aseUniqueIndices_clear(&indices); aseUniqueIndices_clear(&remap); }
void CPicoSurface::AccumulateBBox() { int i; picoVec_t *p; aabb_clear(&m_BBox); for (i=0; i<PicoGetSurfaceNumVertexes(m_pSurface); i++) { p=PicoGetSurfaceXYZ(m_pSurface,i); aabb_extend_by_point(&m_BBox, p); } aabb_update_radius(&m_BBox); }
int PicoGetModelTotalVertexes (picoModel_t *model) { int i, count; if (model == NULL) return 0; if (model->surface == NULL) return 0; count = 0; for (i = 0; i < model->numSurfaces; i++) count += PicoGetSurfaceNumVertexes(model->surface[i]); return count; }
// Constructor. Copy the provided picoSurface_t structure into this object RenderablePicoSurface::RenderablePicoSurface (picoSurface_t* surf) : _originalShaderName(""), _mappedShaderName("") { // Get the shader from the picomodel struct. picoShader_t* shader = PicoGetSurfaceShader(surf); if (shader != 0) { _originalShaderName = PicoGetShaderName(shader); } // Capture the shader _shader = GlobalShaderCache().capture(_originalShaderName); if (_shader) _mappedShaderName = _originalShaderName; // no skin at this time // Get the number of vertices and indices, and reserve capacity in our vectors in advance // by populating them with empty structs. const int nVerts = PicoGetSurfaceNumVertexes(surf); _nIndices = PicoGetSurfaceNumIndexes(surf); _vertices.resize(nVerts); _indices.resize(_nIndices); // Stream in the vertex data from the raw struct, expanding the local AABB to include // each vertex. for (int vNum = 0; vNum < nVerts; ++vNum) { Vertex3f vertex(PicoGetSurfaceXYZ(surf, vNum)); _localAABB.includePoint(vertex); _vertices[vNum].vertex = vertex; _vertices[vNum].normal = Normal3f(PicoGetSurfaceNormal(surf, vNum)); _vertices[vNum].texcoord = TexCoord2f(PicoGetSurfaceST(surf, 0, vNum)); } // Stream in the index data picoIndex_t* ind = PicoGetSurfaceIndexes(surf, 0); for (unsigned int i = 0; i < _nIndices; i++) _indices[i] = ind[i]; }
// Constructor. Copy the provided picoSurface_t structure into this object RenderablePicoSurface::RenderablePicoSurface(picoSurface_t* surf, const std::string& fExt) : _shaderName(""), _dlRegular(0), _dlProgramVcol(0), _dlProgramNoVCol(0) { // Get the shader from the picomodel struct. If this is a LWO model, use // the material name to select the shader, while for an ASE model the // bitmap path should be used. picoShader_t* shader = PicoGetSurfaceShader(surf); std::string rawName = ""; if (shader != 0) { if (fExt == "lwo") { _shaderName = PicoGetShaderName(shader); } else if (fExt == "ase") { rawName = PicoGetShaderName(shader); std::string rawMapName = PicoGetShaderMapName(shader); _shaderName = cleanupShaderName(rawMapName); } } // If shader not found, fallback to alternative if available // _shaderName is empty if the ase material has no BITMAP // materialIsValid is false if _shaderName is not an existing shader if ((_shaderName.empty() || !GlobalMaterialManager().materialExists(_shaderName)) && !rawName.empty()) { _shaderName = cleanupShaderName(rawName); } // Capturing the shader happens later on when we have a RenderSystem reference // Get the number of vertices and indices, and reserve capacity in our // vectors in advance by populating them with empty structs. int nVerts = PicoGetSurfaceNumVertexes(surf); _nIndices = PicoGetSurfaceNumIndexes(surf); _vertices.resize(nVerts); _indices.resize(_nIndices); // Stream in the vertex data from the raw struct, expanding the local AABB // to include each vertex. for (int vNum = 0; vNum < nVerts; ++vNum) { // Get the vertex position and colour Vertex3f vertex(PicoGetSurfaceXYZ(surf, vNum)); // Expand the AABB to include this new vertex _localAABB.includePoint(vertex); _vertices[vNum].vertex = vertex; _vertices[vNum].normal = Normal3f(PicoGetSurfaceNormal(surf, vNum)); _vertices[vNum].texcoord = TexCoord2f(PicoGetSurfaceST(surf, 0, vNum)); _vertices[vNum].colour = getColourVector(PicoGetSurfaceColor(surf, 0, vNum)); } // Stream in the index data picoIndex_t* ind = PicoGetSurfaceIndexes(surf, 0); for (unsigned int i = 0; i < _nIndices; i++) _indices[i] = ind[i]; // Calculate the tangent and bitangent vectors calculateTangents(); // Construct the DLs createDisplayLists(); }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }
picoModel_t *LoadModel(char *name, int frame) { int i; picoModel_t *model, **pm; /* init */ if(numPicoModels <= 0) memset(picoModels, 0, sizeof(picoModels)); /* dummy check */ if(name == NULL || name[0] == '\0') return NULL; /* try to find existing picoModel */ model = FindModel(name, frame); if(model != NULL) return model; /* none found, so find first non-null picoModel */ pm = NULL; for(i = 0; i < MAX_MODELS; i++) { if(picoModels[i] == NULL) { pm = &picoModels[i]; break; } } /* too many picoModels? */ if(pm == NULL) Error("MAX_MODELS (%d) exceeded, there are too many model files referenced by the map.", MAX_MODELS); /* attempt to parse model */ *pm = PicoLoadModel((char *)name, frame); /* if loading failed, make a bogus model to silence the rest of the warnings */ if(*pm == NULL) { /* allocate a new model */ *pm = PicoNewModel(); if(*pm == NULL) return NULL; /* set data */ PicoSetModelName(*pm, name); PicoSetModelFrameNum(*pm, frame); } /* debug code */ #if 0 { int numSurfaces, numVertexes; picoSurface_t *ps; Sys_Printf("Model %s\n", name); numSurfaces = PicoGetModelNumSurfaces(*pm); for(i = 0; i < numSurfaces; i++) { ps = PicoGetModelSurface(*pm, i); numVertexes = PicoGetSurfaceNumVertexes(ps); Sys_Printf("Surface %d has %d vertexes\n", i, numVertexes); } } #endif /* set count */ if(*pm != NULL) numPicoModels++; /* return the picoModel */ return *pm; }
/** * @brief A nice way to add individual triangles to the model. * Chooses an appropriate surface based on the shader, or adds a new surface if necessary */ void PicoAddTriangleToModel (picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader, picoIndex_t* smoothingGroup) { int i, j; int vertDataIndex; picoSurface_t* workSurface = NULL; /* see if a surface already has the shader */ for (i = 0; i < model->numSurfaces; i++) { workSurface = model->surface[i]; if (workSurface->shader == shader) { break; } } /* no surface uses this shader yet, so create a new surface */ if (!workSurface || i >= model->numSurfaces) { /* create a new surface in the model for the unique shader */ workSurface = PicoNewSurface(model); if (!workSurface) { _pico_printf(PICO_ERROR, "Could not allocate a new surface!\n"); return; } /* do surface setup */ PicoSetSurfaceType(workSurface, PICO_TRIANGLES); PicoSetSurfaceName(workSurface, shader->name); PicoSetSurfaceShader(workSurface, shader); } /* add the triangle data to the surface */ for (i = 0; i < 3; i++) { /* get the next free spot in the index array */ int newVertIndex = PicoGetSurfaceNumIndexes(workSurface); /* get the index of the vertex that we're going to store at newVertIndex */ vertDataIndex = PicoFindSurfaceVertexNum(workSurface, *xyz[i], *normals[i], numSTs, st[i], numColors, colors[i], smoothingGroup[i]); /* the vertex wasn't found, so create a new vertex in the pool from the data we have */ if (vertDataIndex == -1) { /* find the next spot for a new vertex */ vertDataIndex = PicoGetSurfaceNumVertexes(workSurface); /* assign the data to it */ PicoSetSurfaceXYZ(workSurface, vertDataIndex, *xyz[i]); PicoSetSurfaceNormal(workSurface, vertDataIndex, *normals[i]); /* make sure to copy over all available ST's and colors for the vertex */ for (j = 0; j < numColors; j++) { PicoSetSurfaceColor(workSurface, j, vertDataIndex, colors[i][j]); } for (j = 0; j < numSTs; j++) { PicoSetSurfaceST(workSurface, j, vertDataIndex, st[i][j]); } PicoSetSurfaceSmoothingGroup(workSurface, vertDataIndex, smoothingGroup[i]); } /* add this vertex to the triangle */ PicoSetSurfaceIndex(workSurface, newVertIndex, vertDataIndex); } }