/* * Add or update an entry in the Global MDVSN cache for a versioning event * found in the event list. Reconcile with current contents of the cache * if needed. * event: The event containing the versioning information for an update */ static void mdver_globalhandler_add_version(mdver_event *event) { Assert(NULL != event); Cache *glob_mdvsn = mdver_get_glob_mdvsn(); if (mdver_is_nuke_event(event)) { mdver_glob_mdvsn_nuke(); return; } mdver_entry mdver = { InvalidOid, INVALID_MD_VERSION, INVALID_MD_VERSION }; mdver.key = event->key; mdver.ddl_version = INVALID_MD_VERSION; mdver.dml_version = INVALID_MD_VERSION; /* FIXME gcaragea 04/14/2014: Trigger evictions if cache is full (MPP-22923) */ CacheEntry *acquired_entry = Cache_AcquireEntry(glob_mdvsn, &mdver); Assert(NULL != acquired_entry); /* * We're about to look-up and insert/update a shared cache entry. * Grab writer lock in exclusive mode, so that no other backend * tries to insert or update the same entry at the same time. */ LWLockAcquire(MDVerWriteLock, LW_EXCLUSIVE); CacheEntry *cached_entry = Cache_Lookup(glob_mdvsn, acquired_entry); if (NULL != cached_entry) { mdver_globalhandler_reconcile(event, cached_entry); /* Done with the looked-up entry. Release it */ Cache_Release(glob_mdvsn, cached_entry); } else { /* Entry not found, insert new entry */ mdver_entry *new_mdver_entry = CACHE_ENTRY_PAYLOAD(acquired_entry); #ifdef MD_VERSIONING_INSTRUMENTATION elog(gp_mdversioning_loglevel, "Inserting into GlobalMDVSN entry %d: (%d,%d)", event->key, (int) event->new_ddl_version, (int) event->new_dml_version); #endif new_mdver_entry->ddl_version = event->new_ddl_version; new_mdver_entry->dml_version = event->new_dml_version; Cache_Insert(glob_mdvsn, acquired_entry); } Cache_Release(glob_mdvsn, acquired_entry); LWLockRelease(MDVerWriteLock); }
/* * Sweeps through the cache and marks all entries as deleted * * Returns the number of elements it found and marked deleted. */ int32 Cache_Clear(Cache *cache) { Assert(NULL != cache); int32 startIdx = cdb_randint(cache->cacheHdr->nEntries - 1, 0); int32 entryIdx = startIdx; int32 numClearedEntries = 0; while (true) { entryIdx = (entryIdx + 1) % cache->cacheHdr->nEntries; if (entryIdx == startIdx) { /* Completed one loop through the list of all entries. We're done */ break; } CacheEntry *crtEntry = Cache_GetEntryByIndex(cache->cacheHdr, entryIdx); /* Lock entry so that nobody else changes its state until we're done with it */ Cache_LockEntry(cache, crtEntry); if (crtEntry->state != CACHE_ENTRY_CACHED) { /* Not interested in free/acquired/deleted entries. Go back and look at next entry */ Cache_UnlockEntry(cache, crtEntry); continue; } /* Found cached entry */ Cache_EntryAddRef(cache, crtEntry); if (crtEntry->state == CACHE_ENTRY_FREE || crtEntry->state == CACHE_ENTRY_ACQUIRED) { /* Someone freed up the entry before we had a chance to Add-Ref it. Skip it. */ Assert(false); Cache_EntryDecRef(cache, crtEntry); Cache_UnlockEntry(cache, crtEntry); continue; } Cache_RegisterCleanup(cache, crtEntry, true /* isCachedEntry */); Cache_Remove(cache, crtEntry); /* Done with changing the state. Unlock the entry */ Cache_UnlockEntry(cache, crtEntry); Cache_Release(cache, crtEntry); numClearedEntries++; } return numClearedEntries; }
/* * Look up an entry in the Global MDVSN component. * To avoid any concurrency issues, this returns a copy of the entry, * palloc'ed in the current memory context. The caller is responsible * for freeing this copy. * * Returns a copy of the entry if found, NULL otherwise. * */ mdver_entry * mdver_glob_mdvsn_find(Oid oid) { Assert(NULL != mdver_glob_mdvsn); mdver_entry mdver_info; mdver_info.key = oid; /* FIXME gcaragea 03/18/2014: Trigger evictions if cache is full (MPP-22923) */ CacheEntry *localEntry = Cache_AcquireEntry(mdver_glob_mdvsn, &mdver_info); Assert(NULL != localEntry); CacheEntry *cachedEntry = Cache_Lookup(mdver_glob_mdvsn, localEntry); /* Release local entry. We don't need it anymore */ Cache_Release(mdver_glob_mdvsn, localEntry); mdver_entry *mdver_copy = NULL; if (NULL != cachedEntry) { /* Found a match. Make a local copy */ mdver_entry *shared_mdver = (mdver_entry *) CACHE_ENTRY_PAYLOAD(cachedEntry); mdver_copy = (mdver_entry *) palloc0(sizeof(mdver_entry)); /* Lock entry to ensure atomicity of copy */ Cache_LockEntry(mdver_glob_mdvsn, cachedEntry); memcpy(mdver_copy, shared_mdver, sizeof(mdver_entry)); /* Got the copy, unlock entry */ Cache_UnlockEntry(mdver_glob_mdvsn, cachedEntry); /* * We're also done with the entry, release our pincount on it * * TODO gcaragea 05/02/2014: Are there cases where we need to hold the * entry past this point? (MPP-22923) */ Cache_Release(mdver_glob_mdvsn, cachedEntry); } return mdver_copy; }
void R_AliasDrawModel (alight_t *plighting) { int size; finalvert_t *finalverts; r_amodels_drawn++; if (!(paliashdr = currententity->model->aliashdr)) paliashdr = Cache_Get (¤tentity->model->cache); pmdl = (mdl_t *) ((byte *) paliashdr + paliashdr->model); size = (CACHE_SIZE - 1) + sizeof (finalvert_t) * (pmdl->numverts + 1) + sizeof (auxvert_t) * pmdl->numverts; finalverts = (finalvert_t *) Hunk_TempAlloc (size); if (!finalverts) Sys_Error ("R_AliasDrawModel: out of memory"); // cache align pfinalverts = (finalvert_t *) (((intptr_t) &finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); pauxverts = (auxvert_t *) &pfinalverts[pmdl->numverts + 1]; R_AliasSetupSkin (); R_AliasSetUpTransform (currententity->trivial_accept); R_AliasSetupLighting (plighting); R_AliasSetupFrame (); r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && r_recursiveaffinetriangles; if (!acolormap) acolormap = vid.colormap8; if (r_affinetridesc.drawtype) { D_PolysetUpdateTables (); // FIXME: precalc... } else { #ifdef USE_INTEL_ASM D_Aff8Patch (acolormap); #endif } if (currententity != vr_data.view_model) ziscale = (float) 0x8000 *(float) 0x10000; else ziscale = (float) 0x8000 *(float) 0x10000 *3.0; if (currententity->trivial_accept && pmdl->ident != HEADER_MDL16) R_AliasPrepareUnclippedPoints (); else R_AliasPreparePoints (); if (!currententity->model->aliashdr) Cache_Release (¤tentity->model->cache); }
void sw32_R_AliasDrawModel (alight_t *plighting) { int size; finalvert_t *finalverts; sw32_r_amodels_drawn++; if (!(paliashdr = currententity->model->aliashdr)) paliashdr = Cache_Get (¤tentity->model->cache); pmdl = (mdl_t *) ((byte *) paliashdr + paliashdr->model); size = (CACHE_SIZE - 1) + sizeof (finalvert_t) * (pmdl->numverts + 1) + sizeof (auxvert_t) * pmdl->numverts; finalverts = (finalvert_t *) Hunk_TempAlloc (size); if (!finalverts) Sys_Error ("R_AliasDrawModel: out of memory"); // cache align pfinalverts = (finalvert_t *) (((intptr_t) &finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); sw32_pauxverts = (auxvert_t *) &pfinalverts[pmdl->numverts + 1]; R_AliasSetupSkin (); sw32_R_AliasSetUpTransform (currententity->trivial_accept); R_AliasSetupLighting (plighting); R_AliasSetupFrame (); if (!sw32_acolormap) sw32_acolormap = vid.colormap8; if (sw32_acolormap == &vid.colormap8 && sw32_r_pixbytes != 1) { if (sw32_r_pixbytes == 2) sw32_acolormap = vid.colormap16; else if (sw32_r_pixbytes == 4) sw32_acolormap = vid.colormap32; else Sys_Error("R_AliasDrawmodel: unsupported r_pixbytes %i", sw32_r_pixbytes); } if (currententity != vr_data.view_model) sw32_ziscale = (float) 0x8000 *(float) 0x10000; else sw32_ziscale = (float) 0x8000 *(float) 0x10000 *3.0; if (currententity->trivial_accept) R_AliasPrepareUnclippedPoints (); else R_AliasPreparePoints (); if (!currententity->model->aliashdr) Cache_Release (¤tentity->model->cache); }
/* * Close a spill file set. If we're planning to re-use it, insert it in the * cache. If not, let the cleanup routine delete the files and free up memory. */ void workfile_mgr_close_set(workfile_set *work_set) { Assert(work_set!=NULL); elog(gp_workfile_caching_loglevel, "closing workfile set: location: %s, size=" INT64_FORMAT " in_progress_size=" INT64_FORMAT, work_set->path, work_set->size, work_set->in_progress_size); CacheEntry *cache_entry = CACHE_ENTRY_HEADER(work_set); Cache_Release(workfile_mgr_cache, cache_entry); }
static void PrintFrameName (model_t *m, int frame) { aliashdr_t *hdr; maliasframedesc_t *pframedesc; hdr = Cache_TryGet (&m->cache); if (!hdr) return; pframedesc = &hdr->frames[frame]; Sys_Printf ("frame %i: %s\n", frame, pframedesc->name); Cache_Release (&m->cache); }
/* * Close a spill file set. If we're planning to re-use it, insert it in the * cache. If not, let the cleanup routine delete the files and free up memory. */ void workfile_mgr_close_set(workfile_set *work_set) { Assert(work_set!=NULL); elog(gp_workfile_caching_loglevel, "closing workfile set: can_be_reused=%d complete=%d location: %s, size=" INT64_FORMAT " in_progress_size=" INT64_FORMAT, work_set->can_be_reused, work_set->complete, work_set->path, work_set->size, work_set->in_progress_size); CacheEntry *cache_entry = CACHE_ENTRY_HEADER(work_set); if (Cache_IsCached(cache_entry)) { /* Workset came from cache. Just release it, nothing to do */ Cache_Release(workfile_mgr_cache, cache_entry); return; } if (work_set->complete && work_set->can_be_reused) { cache_entry->size = work_set->size; /* We want to keep this one around. Insert into cache */ Cache_Insert(workfile_mgr_cache, cache_entry); Cache_Release(workfile_mgr_cache, cache_entry); return; } /* * Fall-through case: We need to delete this work_set, as it's not reusable. */ Assert(!work_set->complete || !work_set->can_be_reused); Cache_Release(workfile_mgr_cache, cache_entry); }
void SND_CacheRelease (sfx_t *sfx) { sfxblock_t *block = sfx->data.block; // due to the possibly asynchronous nature of the mixer, the cache // may have been flushed behind our backs if (block->cache.data) { if (!Cache_ReadLock (&block->cache)) { Sys_Printf ("WARNING: taniwha screwed up in the sound engine: %s\n", sfx->name); return; } Cache_Release (&block->cache); if (!Cache_ReadLock (&block->cache)) block->buffer = 0; } }
/* * Look up file set the cache given a certain PlanState. * Return NULL if not found. */ static workfile_set * workfile_mgr_lookup_set(PlanState *ps) { Assert(NULL != ps); Assert(NULL != workfile_mgr_cache); Assert(NULL != ps->plan); Assert(nodeTag(ps->plan) >= T_Plan && nodeTag(ps->plan) < T_PlanInvalItem); /* Create parameter info for the populate function */ workset_info set_info; set_info.dir_path = NULL; set_info.operator_work_mem = get_operator_work_mem(ps); set_info.on_disk = false; CacheEntry *localEntry = acquire_entry_retry(workfile_mgr_cache, &set_info); Assert(localEntry != NULL); workfile_set *local_work_set = (workfile_set *) CACHE_ENTRY_PAYLOAD(localEntry); /* Populate the rest of the entries needed for look-up * Allocate the serialized plan in the TopMemoryContext since this memory * context is still available when calling the transaction callback at the * time when the transaction aborts. */ MemoryContext oldcxt = MemoryContextSwitchTo(TopMemoryContext); workfile_set_plan *s_plan = workfile_mgr_serialize_plan(ps); MemoryContextSwitchTo(oldcxt); Assert(s_plan != NULL); local_work_set->set_plan = s_plan; local_work_set->key = workfile_mgr_hash_key(s_plan); CacheEntry *cachedEntry = Cache_Lookup(workfile_mgr_cache, localEntry); /* Release local entry and free up plan memory. We don't need it anymore */ Cache_Release(workfile_mgr_cache, localEntry); workfile_set *work_set = NULL; if (NULL != cachedEntry) { work_set = (workfile_set *) CACHE_ENTRY_PAYLOAD(cachedEntry); } return work_set; }
/* * Close a spill file set. If we're planning to re-use it, insert it in the * cache. If not, let the cleanup routine delete the files and free up memory. */ void workfile_mgr_close_set(workfile_set *work_set) { Assert(work_set!=NULL); /* Although work_set is in shared memory only this process has access to it */ if (work_set->prev) work_set->prev->next = work_set->next; else open_workfile_sets = work_set->next; if (work_set->next) work_set->next->prev = work_set->prev; elog(gp_workfile_caching_loglevel, "closing workfile set: location: %s, size=" INT64_FORMAT " in_progress_size=" INT64_FORMAT, work_set->path, work_set->size, work_set->in_progress_size); CacheEntry *cache_entry = CACHE_ENTRY_HEADER(work_set); Cache_Release(workfile_mgr_cache, cache_entry); }
qboolean sw32_R_AliasCheckBBox (void) { int i, flags, frame, numv; aliashdr_t *pahdr; float zi, basepts[8][3], v0, v1, frac; finalvert_t *pv0, *pv1, viewpts[16]; auxvert_t *pa0, *pa1, viewaux[16]; maliasframedesc_t *pframedesc; qboolean zclipped, zfullyclipped; unsigned int anyclip, allclip; int minz; // expand, rotate, and translate points into worldspace currententity->trivial_accept = 0; pmodel = currententity->model; if (!(pahdr = pmodel->aliashdr)) pahdr = Cache_Get (&pmodel->cache); pmdl = (mdl_t *) ((byte *) pahdr + pahdr->model); sw32_R_AliasSetUpTransform (0); // construct the base bounding box for this frame frame = currententity->frame; // TODO: don't repeat this check when drawing? if ((frame >= pmdl->numframes) || (frame < 0)) { Sys_MaskPrintf (SYS_DEV, "No such frame %d %s\n", frame, pmodel->name); frame = 0; } pframedesc = &pahdr->frames[frame]; // x worldspace coordinates basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = (float) pframedesc->bboxmin.v[0]; basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = (float) pframedesc->bboxmax.v[0]; // y worldspace coordinates basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = (float) pframedesc->bboxmin.v[1]; basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = (float) pframedesc->bboxmax.v[1]; // z worldspace coordinates basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = (float) pframedesc->bboxmin.v[2]; basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = (float) pframedesc->bboxmax.v[2]; zclipped = false; zfullyclipped = true; minz = 9999; for (i = 0; i < 8; i++) { sw32_R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) { // we must clip points that are closer than the near clip plane viewpts[i].flags = ALIAS_Z_CLIP; zclipped = true; } else { if (viewaux[i].fv[2] < minz) minz = viewaux[i].fv[2]; viewpts[i].flags = 0; zfullyclipped = false; } } if (zfullyclipped) { if (!pmodel->aliashdr) Cache_Release (&pmodel->cache); return false; // everything was near-z-clipped } numv = 8; if (zclipped) { // organize points by edges, use edges to get new points (possible // trivial reject) for (i = 0; i < 12; i++) { // edge endpoints pv0 = &viewpts[aedges[i].index0]; pv1 = &viewpts[aedges[i].index1]; pa0 = &viewaux[aedges[i].index0]; pa1 = &viewaux[aedges[i].index1]; // if one end is clipped and the other isn't, make a new point if (pv0->flags ^ pv1->flags) { frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / (pa1->fv[2] - pa0->fv[2]); viewaux[numv].fv[0] = pa0->fv[0] + (pa1->fv[0] - pa0->fv[0]) * frac; viewaux[numv].fv[1] = pa0->fv[1] + (pa1->fv[1] - pa0->fv[1]) * frac; viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; viewpts[numv].flags = 0; numv++; } } } // project the vertices that remain after clipping anyclip = 0; allclip = ALIAS_XY_CLIP_MASK; // TODO: probably should do this loop in ASM, especially if we use floats for (i = 0; i < numv; i++) { // we don't need to bother with vertices that were z-clipped if (viewpts[i].flags & ALIAS_Z_CLIP) continue; zi = 1.0 / viewaux[i].fv[2]; // FIXME: do with chop mode in ASM, or convert to float v0 = (viewaux[i].fv[0] * sw32_xscale * zi) + sw32_xcenter; v1 = (viewaux[i].fv[1] * sw32_yscale * zi) + sw32_ycenter; flags = 0; if (v0 < r_refdef.fvrectx) flags |= ALIAS_LEFT_CLIP; if (v1 < r_refdef.fvrecty) flags |= ALIAS_TOP_CLIP; if (v0 > r_refdef.fvrectright) flags |= ALIAS_RIGHT_CLIP; if (v1 > r_refdef.fvrectbottom) flags |= ALIAS_BOTTOM_CLIP; anyclip |= flags; allclip &= flags; } if (allclip) { if (!pmodel->aliashdr) Cache_Release (&pmodel->cache); return false; // trivial reject off one side } currententity->trivial_accept = !anyclip & !zclipped; if (currententity->trivial_accept) { if (minz > (sw32_r_aliastransition + (pmdl->size * sw32_r_resfudge))) { currententity->trivial_accept |= 2; } } if (!pmodel->aliashdr) Cache_Release (&pmodel->cache); return true; }
/* * When a backend is requesting the more recent version of an object, * if the Local MDVSN cache doesn't have the version, and if a NUKE event * hasn't been encountered in the current transaction, it is looked up * in the Global MDVSN shared cache. * * If the object is found in Global MDVSN, return the global version. * If the object is not found, generate a new version, record it in Global MDVSN * and then return it. * * key: The key of the looked-up object * ddl_version: used to return the ddl version for the object * dml_version: used to return the dml version for the object * */ static void mdver_request_from_global(Oid key, uint64 *ddl_version, uint64 *dml_version) { Assert(NULL != ddl_version); Assert(NULL != dml_version); Cache *mdver_glob_mdvsn = mdver_get_glob_mdvsn(); Assert(NULL != mdver_glob_mdvsn); mdver_entry entry = {key, INVALID_MD_VERSION, INVALID_MD_VERSION}; /* FIXME gcaragea 06/03/2014: Trigger evictions if cache is full (MPP-22923) */ CacheEntry *localEntry = Cache_AcquireEntry(mdver_glob_mdvsn, &entry); Assert(NULL != localEntry); /* * We're about to look-up and insert a shared cache entry. * Grab writer lock in exclusive mode, so that no other backend * can insert or update the same entry at the same time. */ LWLockAcquire(MDVerWriteLock, LW_EXCLUSIVE); CacheEntry *cachedEntry = Cache_Lookup(mdver_glob_mdvsn, localEntry); if (NULL != cachedEntry) { /* Not found in LVSN, not nuke happened, eventually found in GVSN */ mdver_entry *crt_entry = CACHE_ENTRY_PAYLOAD(cachedEntry); *ddl_version = crt_entry->ddl_version; *dml_version = crt_entry->dml_version; #ifdef MD_VERSIONING_INSTRUMENTATION elog(gp_mdversioning_loglevel, "Found version in Global MDVSN: (%d, " UINT64_FORMAT ", " UINT64_FORMAT "). Adding it to Local MDVSN", key, crt_entry->ddl_version, crt_entry->dml_version); #endif /* * We're also done with the entry, release our pincount on it * * TODO gcaragea 05/02/2014: Are there cases where we need to hold the * entry past this point? (MPP-22923) */ Cache_Release(mdver_glob_mdvsn, cachedEntry); } else { /* Not found in LVSN, not nuke happened, not found in GVSN either */ /* Generate new version */ *ddl_version = mdver_next_global_version(); *dml_version = mdver_next_global_version(); /* Add to GVSN */ mdver_entry *new_entry = CACHE_ENTRY_PAYLOAD(localEntry); new_entry->ddl_version = *ddl_version; new_entry->dml_version = *dml_version; #ifdef MD_VERSIONING_INSTRUMENTATION elog(gp_mdversioning_loglevel, "Inserting new version in Global MDVSN: (%d, " UINT64_FORMAT ", " UINT64_FORMAT "). Adding it to Local MDVSN", key, new_entry->ddl_version, new_entry->dml_version); #endif Cache_Insert(mdver_glob_mdvsn, localEntry); } LWLockRelease(MDVerWriteLock); /* Release local entry. We don't need it anymore */ Cache_Release(mdver_glob_mdvsn, localEntry); }
//#define TETRAHEDRON void glsl_R_DrawAlias (void) { #ifdef TETRAHEDRON static aliasvrt_t debug_verts[] = { {{ 0, 0}, {-18918,-18918,-18918}, { 0, 0, 0}}, {{ 0,300}, { 18918, 18918,-18918}, {255,255, 0}}, {{300,300}, {-18918, 18918, 18918}, { 0,255,255}}, {{300, 0}, { 18918,-18918, 18918}, {255, 0,255}}, }; static GLushort debug_indices[] = { 0, 1, 2, 0, 3, 1, 1, 3, 2, 0, 2, 3, }; #endif static quat_t color = { 1, 1, 1, 1}; static vec3_t lightvec; float ambient; float shadelight; float skin_size[2]; float blend; entity_t *ent = currententity; model_t *model = ent->model; aliashdr_t *hdr; vec_t norm_mat[9]; mat4_t mvp_mat; int skin_tex; int colormap; aliasvrt_t *pose1 = 0; // VBO's are null based aliasvrt_t *pose2 = 0; // VBO's are null based if (!(hdr = model->aliashdr)) hdr = Cache_Get (&model->cache); calc_lighting (ent, &ambient, &shadelight, lightvec); // we need only the rotation for normals. VectorCopy (ent->transform + 0, norm_mat + 0); VectorCopy (ent->transform + 4, norm_mat + 3); VectorCopy (ent->transform + 8, norm_mat + 6); // ent model scaling and offset Mat4Zero (mvp_mat); mvp_mat[0] = hdr->mdl.scale[0]; mvp_mat[5] = hdr->mdl.scale[1]; mvp_mat[10] = hdr->mdl.scale[2]; mvp_mat[15] = 1; VectorCopy (hdr->mdl.scale_origin, mvp_mat + 12); Mat4Mult (ent->transform, mvp_mat, mvp_mat); Mat4Mult (alias_vp, mvp_mat, mvp_mat); colormap = glsl_colormap; if (ent->skin && ent->skin->auxtex) colormap = ent->skin->auxtex; if (ent->skin && ent->skin->texnum) { skin_t *skin = ent->skin; skin_tex = skin->texnum; } else { maliasskindesc_t *skindesc; skindesc = R_AliasGetSkindesc (ent->skinnum, hdr); skin_tex = skindesc->texnum; } blend = R_AliasGetLerpedFrames (ent, hdr); pose1 += ent->pose1 * hdr->poseverts; pose2 += ent->pose2 * hdr->poseverts; skin_size[0] = hdr->mdl.skinwidth; skin_size[1] = hdr->mdl.skinheight; qfeglActiveTexture (GL_TEXTURE0 + 1); qfeglBindTexture (GL_TEXTURE_2D, colormap); qfeglActiveTexture (GL_TEXTURE0 + 0); qfeglBindTexture (GL_TEXTURE_2D, skin_tex); #ifndef TETRAHEDRON qfeglBindBuffer (GL_ARRAY_BUFFER, hdr->posedata); qfeglBindBuffer (GL_ELEMENT_ARRAY_BUFFER, hdr->commands); #endif qfeglVertexAttrib4fv (quake_mdl.colora.location, color); qfeglVertexAttrib4fv (quake_mdl.colorb.location, color); qfeglUniform1f (quake_mdl.blend.location, blend); qfeglUniform1f (quake_mdl.ambient.location, ambient); qfeglUniform1f (quake_mdl.shadelight.location, shadelight); qfeglUniform3fv (quake_mdl.lightvec.location, 1, lightvec); qfeglUniform2fv (quake_mdl.skin_size.location, 1, skin_size); qfeglUniformMatrix4fv (quake_mdl.mvp_matrix.location, 1, false, mvp_mat); qfeglUniformMatrix3fv (quake_mdl.norm_matrix.location, 1, false, norm_mat); #ifndef TETRAHEDRON set_arrays (&quake_mdl.vertexa, &quake_mdl.normala, &quake_mdl.sta, pose1); set_arrays (&quake_mdl.vertexb, &quake_mdl.normalb, &quake_mdl.stb, pose2); qfeglDrawElements (GL_TRIANGLES, 3 * hdr->mdl.numtris, GL_UNSIGNED_SHORT, 0); #else set_arrays (&quake_mdl.vertexa, &quake_mdl.normala, &quake_mdl.sta, debug_verts); set_arrays (&quake_mdl.vertexb, &quake_mdl.normalb, &quake_mdl.stb, debug_verts); qfeglDrawElements (GL_TRIANGLES, sizeof (debug_indices) / sizeof (debug_indices[0]), GL_UNSIGNED_SHORT, debug_indices); #endif if (!model->aliashdr) Cache_Release (&model->cache); }