/* ================== RB_ReadPixels Reads an image but takes care of alignment issues for reading RGB images. Prepends the specified number of (uninitialized) bytes to the buffer. The returned buffer must be freed with ri.Hunk_FreeTempMemory(). ================== */ static byte *RB_ReadPixels( int x, int y, int width, int height, size_t offset ) { GLint packAlign; int lineLen, paddedLineLen; byte *buffer, *pixels; int i; glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign ); lineLen = width * 3; paddedLineLen = PAD( lineLen, packAlign ); // Allocate a few more bytes so that we can choose an alignment we like buffer = ( byte * ) ri.Hunk_AllocateTempMemory( offset + paddedLineLen * height + packAlign - 1 ); pixels = ( byte * ) PADP( buffer + offset, packAlign ); glReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels ); // Drop alignment and line padding bytes for ( i = 0; i < height; ++i ) { memmove( buffer + offset + i * lineLen, pixels + i * paddedLineLen, lineLen ); } return buffer; }
/* * RB_ExecuteRenderCommands * * This function will be called synchronously if running without * smp extensions, or asynchronously by another thread. */ void RB_ExecuteRenderCommands(const void *data) { int t1, t2; t1 = ri.Milliseconds (); if(!r_smp->integer || data == backEndData[0]->commands.cmds){ backEnd.smpFrame = 0; }else{ backEnd.smpFrame = 1; } while(1){ data = PADP(data, sizeof(void *)); switch(*(const int*)data){ case RC_SET_COLOR: data = RB_SetColor(data); break; case RC_STRETCH_PIC: data = RB_StretchPic(data); break; case RC_DRAW_SURFS: data = RB_DrawSurfs(data); break; case RC_DRAW_BUFFER: data = RB_DrawBuffer(data); break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers(data); break; case RC_SCREENSHOT: data = RB_TakeScreenshotCmd(data); break; case RC_VIDEOFRAME: data = RB_TakeVideoFrameCmd(data); break; case RC_COLORMASK: data = RB_ColorMask(data); break; case RC_CLEARDEPTH: data = RB_ClearDepth(data); break; case RC_CAPSHADOWMAP: data = RB_CapShadowMap(data); break; case RC_POSTPROCESS: data = RB_PostProcess(data); break; case RC_END_OF_LIST: default: /* stop rendering on this thread */ t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; return; } } }
/* ==================== RB_ExecuteRenderCommands ==================== */ void RB_ExecuteRenderCommands( const void *data ) { int t1, t2; t1 = ri.Milliseconds (); while ( 1 ) { data = PADP(data, sizeof(void *)); switch ( *(const int *)data ) { case RC_SET_COLOR: data = RB_SetColor( data ); break; case RC_STRETCH_PIC: data = RB_StretchPic( data ); break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; case RC_DRAW_BUFFER: data = RB_DrawBuffer( data ); break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; case RC_SCREENSHOT: data = RB_TakeScreenshotCmd( data ); break; case RC_VIDEOFRAME: data = RB_TakeVideoFrameCmd( data ); break; case RC_COLORMASK: data = RB_ColorMask(data); break; case RC_CLEARDEPTH: data = RB_ClearDepth(data); break; case RC_CAPSHADOWMAP: data = RB_CapShadowMap(data); break; case RC_POSTPROCESS: data = RB_PostProcess(data); break; case RC_EXPORT_CUBEMAPS: data = RB_ExportCubemaps(data); break; case RC_END_OF_LIST: default: // finish any 2D drawing if needed if(tess.numIndexes) RB_EndSurface(); // stop rendering t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; return; } } }
/* ==================== RB_ExecuteRenderCommands ==================== */ void RB_ExecuteRenderCommands( const void *data ) { int t1, t2; t1 = ri.Milliseconds (); while ( 1 ) { data = PADP(data, sizeof(void *)); switch ( *(const int *)data ) { case RC_SET_COLOR: data = RB_SetColor( data ); break; case RC_STRETCH_PIC: data = RB_StretchPic( data ); break; case RC_ROTATED_PIC: data = RB_RotatedPic( data ); break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; case RC_DRAW_BUFFER: data = RB_DrawBuffer( data ); break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; case RC_SCREENSHOT: data = RB_TakeScreenshotCmd( data ); break; case RC_VIDEOFRAME: data = RB_TakeVideoFrameCmd( data ); break; case RC_COLORMASK: data = RB_ColorMask(data); break; case RC_CLEARDEPTH: data = RB_ClearDepth(data); break; case RC_END_OF_LIST: default: // stop rendering t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; return; } } }
byte* RB_ReadPixels(int x, int y, int width, int height, size_t* offset, int* padlen) { byte* buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); bufstart = PADP((intptr_t) buffer + *offset, packAlign); qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); *offset = bufstart - buffer; *padlen = padwidth - linelen; return buffer; }
byte *RB_ReadZBuffer(int x, int y, int width, int height, int *padlen) { byte *buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + packAlign - 1); bufstart = PADP(( intptr_t ) buffer, packAlign); qglDepthRange(0.0f, 1.0f); memset(buffer, 0, padwidth * height + packAlign - 1); //*TODO* find something to read DepthBuffer ?! *padlen = padwidth - linelen; return buffer; }
byte *RB_ReadZBuffer(int x, int y, int width, int height, int *padlen) { byte *buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + packAlign - 1); bufstart = PADP(( intptr_t ) buffer, packAlign); qglDepthRange(0.0f, 1.0f); qglReadPixels(x, y, width, height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, bufstart); *padlen = padwidth - linelen; return buffer; }
byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) { byte *buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); #ifdef USE_OPENGLES bufstart=buffer; padwidth=linelen; int p2width=1, p2height=1; int xx, yy, aa; while (p2width<glConfig.vidWidth) p2width*=2; while (p2height<glConfig.vidHeight) p2height*=2; byte *source = (byte*) ri.Z_Malloc( p2width * p2height * 4 ); qglReadPixels( 0, 0, p2width, p2height, GL_RGBA, GL_UNSIGNED_BYTE, source ); for (yy=y; yy<height; yy++) for (xx=x; xx<width; xx++) for (aa=0; aa<3; aa++) buffer[yy*width*3+xx*3+aa]=source[(yy+y)*p2width*4+(xx+x)*4+aa]; ri.Free(source); #else bufstart = PADP((intptr_t) buffer + *offset, packAlign); qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); #endif *offset = bufstart - buffer; *padlen = padwidth - linelen; return buffer; }
/* ================== RB_TakeVideoFrameCmd ================== */ const void *RB_TakeVideoFrameCmd( const void *data ) { const videoFrameCommand_t *cmd; byte *cBuf; size_t memcount, linelen; int padwidth, avipadwidth, padlen, avipadlen; GLint packAlign; cmd = (const videoFrameCommand_t *)data; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = cmd->width * 3; // Alignment stuff for glReadPixels padwidth = PAD(linelen, packAlign); padlen = padwidth - linelen; // AVI line padding avipadwidth = PAD(linelen, AVI_LINE_PADDING); avipadlen = avipadwidth - linelen; cBuf = PADP(cmd->captureBuffer, packAlign); qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, cBuf); memcount = padwidth * cmd->height; // gamma correct if(glConfig.deviceSupportsGamma) R_GammaCorrect(cBuf, memcount); if(cmd->motionJpeg) { memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, r_aviMotionJpegQuality->integer, cmd->width, cmd->height, cBuf, padlen); ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); } else { byte *lineend, *memend; byte *srcptr, *destptr; srcptr = cBuf; destptr = cmd->encodeBuffer; memend = srcptr + memcount; // swap R and B and remove line paddings while(srcptr < memend) { lineend = srcptr + linelen; while(srcptr < lineend) { *destptr++ = srcptr[2]; *destptr++ = srcptr[1]; *destptr++ = srcptr[0]; srcptr += 3; } Com_Memset(destptr, '\0', avipadlen); destptr += avipadlen; srcptr += padlen; } ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); } return (const void *)(cmd + 1); }
/* ================== RB_TakeVideoFrameCmd ================== */ const void *RB_TakeVideoFrameCmd( const void *data ) { const videoFrameCommand_t *cmd; GLint packAlign; int lineLen, captureLineLen; byte *pixels; int i; int outputSize; int j; int aviLineLen; cmd = ( const videoFrameCommand_t * ) data; // RB: it is possible to we still have a videoFrameCommand_t but we already stopped // video recording if ( ri.CL_VideoRecording() ) { // take care of alignment issues for reading RGB images.. glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign ); lineLen = cmd->width * 3; captureLineLen = PAD( lineLen, packAlign ); pixels = ( byte * ) PADP( cmd->captureBuffer, packAlign ); glReadPixels( 0, 0, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, pixels ); if ( tr.overbrightBits > 0 && glConfig.deviceSupportsGamma ) { // this also runs over the padding... R_GammaCorrect( pixels, captureLineLen * cmd->height ); } if ( cmd->motionJpeg ) { // Drop alignment and line padding bytes for ( i = 0; i < cmd->height; ++i ) { memmove( cmd->captureBuffer + i * lineLen, pixels + i * captureLineLen, lineLen ); } outputSize = SaveJPGToBuffer( cmd->encodeBuffer, 3 * cmd->width * cmd->height, 90, cmd->width, cmd->height, cmd->captureBuffer ); ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, outputSize ); } else { aviLineLen = PAD( lineLen, AVI_LINE_PADDING ); for ( i = 0; i < cmd->height; ++i ) { for ( j = 0; j < lineLen; j += 3 ) { cmd->encodeBuffer[ i * aviLineLen + j + 0 ] = pixels[ i * captureLineLen + j + 2 ]; cmd->encodeBuffer[ i * aviLineLen + j + 1 ] = pixels[ i * captureLineLen + j + 1 ]; cmd->encodeBuffer[ i * aviLineLen + j + 2 ] = pixels[ i * captureLineLen + j + 0 ]; } while ( j < aviLineLen ) { cmd->encodeBuffer[ i * aviLineLen + j++ ] = 0; } } ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, aviLineLen * cmd->height ); } } return ( const void * )( cmd + 1 ); }
/* ==================== RB_ExecuteRenderCommands This function will be called synchronously if running without smp extensions, or asynchronously by another thread. ==================== */ void RB_ExecuteRenderCommands( const void *data ) { int t1, t2; t1 = ri.Milliseconds (); if ( !r_smp->integer || data == backEndData[0]->commands.cmds ) { backEnd.smpFrame = 0; } else { backEnd.smpFrame = 1; } while ( 1 ) { data = PADP(data, sizeof(void *)); switch ( *(const int *)data ) { case RC_SET_COLOR: data = RB_SetColor( data ); break; case RC_STRETCH_PIC: #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT R_FrameBuffer_EndFrame(); #endif data = RB_StretchPic( data ); break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; case RC_DRAW_BUFFER: #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT data = useFrameBuffer ? RB_DrawFrameBuffer( data ) : RB_DrawBuffer( data ); #else data = RB_DrawBuffer( data ); #endif break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; //these two use a hack to let them copy the framebuffer effects too case RC_SCREENSHOT: #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT R_FrameBufferUnBind(); #endif data = RB_TakeScreenshotCmd( data ); #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT R_FrameBufferBind(); #endif break; case RC_VIDEOFRAME: #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT R_FrameBufferUnBind(); #endif data = RB_TakeVideoFrameCmd( data ); #ifdef FRAMEBUFFER_AND_GLSL_SUPPORT R_FrameBufferBind(); #endif break; case RC_COLORMASK: data = RB_ColorMask(data); break; case RC_CLEARDEPTH: data = RB_ClearDepth(data); break; case RC_END_OF_LIST: default: // stop rendering on this thread t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; return; } } }
/* ================= R_LoadIQModel Load an IQM model and compute the joint matrices for every frame. ================= */ bool R_LoadIQModel( model_t *mod, void *buffer, int filesize, const char *mod_name ) { iqmHeader_t *header; iqmVertexArray_t *vertexarray; iqmTriangle_t *triangle; iqmMesh_t *mesh; iqmJoint_t *joint; iqmPose_t *pose; iqmAnim_t *anim; unsigned short *framedata; char *str, *name; int len; transform_t *trans, *poses; float *bounds; size_t size, len_names; IQModel_t *IQModel; IQAnim_t *IQAnim; srfIQModel_t *surface; vboData_t vboData; float *weightbuf; int *indexbuf; i16vec4_t *qtangentbuf; VBO_t *vbo; IBO_t *ibo; void *ptr; u8vec4_t *weights; if( !LoadIQMFile( buffer, filesize, mod_name, &len_names ) ) { return false; } header = (iqmHeader_t *)buffer; // compute required space size = sizeof(IQModel_t); size += header->num_meshes * sizeof( srfIQModel_t ); size += header->num_anims * sizeof( IQAnim_t ); size += header->num_joints * sizeof( transform_t ); size = PAD( size, 16 ); size += header->num_joints * header->num_frames * sizeof( transform_t ); if(header->ofs_bounds) size += header->num_frames * 6 * sizeof(float); // model bounds size += header->num_vertexes * 3 * sizeof(float); // positions size += header->num_vertexes * 3 * sizeof(float); // normals size += header->num_vertexes * 3 * sizeof(float); // tangents size += header->num_vertexes * 3 * sizeof(float); // bitangents size += header->num_vertexes * 2 * sizeof(int16_t); // texcoords size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes size += header->num_vertexes * 4 * sizeof(byte); // blendWeights size += header->num_vertexes * 4 * sizeof(byte); // colors size += header->num_triangles * 3 * sizeof(int); // triangles size += header->num_joints * sizeof(int); // parents size += len_names; // joint and anim names IQModel = (IQModel_t *)ri.Hunk_Alloc( size, ha_pref::h_low ); mod->type = modtype_t::MOD_IQM; mod->iqm = IQModel; ptr = IQModel + 1; // fill header and setup pointers IQModel->num_vertexes = header->num_vertexes; IQModel->num_triangles = header->num_triangles; IQModel->num_frames = header->num_frames; IQModel->num_surfaces = header->num_meshes; IQModel->num_joints = header->num_joints; IQModel->num_anims = header->num_anims; IQModel->surfaces = (srfIQModel_t *)ptr; ptr = IQModel->surfaces + header->num_meshes; if( header->ofs_anims ) { IQModel->anims = (IQAnim_t *)ptr; ptr = IQModel->anims + header->num_anims; } else { IQModel->anims = nullptr; } IQModel->joints = (transform_t *)PADP(ptr, 16); ptr = IQModel->joints + header->num_joints; if( header->ofs_poses ) { poses = (transform_t *)ptr; ptr = poses + header->num_poses * header->num_frames; } else { poses = nullptr; } if( header->ofs_bounds ) { bounds = (float *)ptr; ptr = bounds + 6 * header->num_frames; } else { bounds = nullptr; } IQModel->positions = (float *)ptr; ptr = IQModel->positions + 3 * header->num_vertexes; IQModel->normals = (float *)ptr; ptr = IQModel->normals + 3 * header->num_vertexes; IQModel->tangents = (float *)ptr; ptr = IQModel->tangents + 3 * header->num_vertexes; IQModel->bitangents = (float *)ptr; ptr = IQModel->bitangents + 3 * header->num_vertexes; IQModel->texcoords = (int16_t *)ptr; ptr = IQModel->texcoords + 2 * header->num_vertexes; IQModel->blendIndexes = (byte *)ptr; ptr = IQModel->blendIndexes + 4 * header->num_vertexes; IQModel->blendWeights = (byte *)ptr; ptr = IQModel->blendWeights + 4 * header->num_vertexes; IQModel->colors = (byte *)ptr; ptr = IQModel->colors + 4 * header->num_vertexes; IQModel->jointParents = (int *)ptr; ptr = IQModel->jointParents + header->num_joints; IQModel->triangles = (int *)ptr; ptr = IQModel->triangles + 3 * header->num_triangles; str = (char *)ptr; IQModel->jointNames = str; // copy joint names joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++ ) { name = ( char* )IQMPtr( header, header->ofs_text + joint->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // setup animations IQAnim = IQModel->anims; anim = ( iqmAnim_t* )IQMPtr( header, header->ofs_anims ); for(int i = 0; i < IQModel->num_anims; i++, IQAnim++, anim++ ) { IQAnim->num_frames = anim->num_frames; IQAnim->framerate = anim->framerate; IQAnim->num_joints = header->num_joints; IQAnim->flags = anim->flags; IQAnim->jointParents = IQModel->jointParents; if( poses ) { IQAnim->poses = poses + anim->first_frame * header->num_poses; } else { IQAnim->poses = nullptr; } if( bounds ) { IQAnim->bounds = bounds + anim->first_frame * 6; } else { IQAnim->bounds = nullptr; } IQAnim->name = str; IQAnim->jointNames = IQModel->jointNames; name = ( char* )IQMPtr( header, header->ofs_text + anim->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // calculate joint transforms trans = IQModel->joints; joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++, trans++ ) { if( joint->parent >= (int) i ) { Log::Warn("R_LoadIQModel: file %s contains an invalid parent joint number.", mod_name ); return false; } TransInitRotationQuat( joint->rotate, trans ); TransAddScale( joint->scale[0], trans ); TransAddTranslation( joint->translate, trans ); if( joint->parent >= 0 ) { TransCombine( trans, &IQModel->joints[ joint->parent ], trans ); } IQModel->jointParents[i] = joint->parent; } // calculate pose transforms framedata = ( short unsigned int* )IQMPtr( header, header->ofs_frames ); trans = poses; for(unsigned i = 0; i < header->num_frames; i++ ) { pose = ( iqmPose_t* )IQMPtr( header, header->ofs_poses ); for(unsigned j = 0; j < header->num_poses; j++, pose++, trans++ ) { vec3_t translate; quat_t rotate; vec3_t scale; translate[0] = pose->channeloffset[0]; if( pose->mask & 0x001) translate[0] += *framedata++ * pose->channelscale[0]; translate[1] = pose->channeloffset[1]; if( pose->mask & 0x002) translate[1] += *framedata++ * pose->channelscale[1]; translate[2] = pose->channeloffset[2]; if( pose->mask & 0x004) translate[2] += *framedata++ * pose->channelscale[2]; rotate[0] = pose->channeloffset[3]; if( pose->mask & 0x008) rotate[0] += *framedata++ * pose->channelscale[3]; rotate[1] = pose->channeloffset[4]; if( pose->mask & 0x010) rotate[1] += *framedata++ * pose->channelscale[4]; rotate[2] = pose->channeloffset[5]; if( pose->mask & 0x020) rotate[2] += *framedata++ * pose->channelscale[5]; rotate[3] = pose->channeloffset[6]; if( pose->mask & 0x040) rotate[3] += *framedata++ * pose->channelscale[6]; scale[0] = pose->channeloffset[7]; if( pose->mask & 0x080) scale[0] += *framedata++ * pose->channelscale[7]; scale[1] = pose->channeloffset[8]; if( pose->mask & 0x100) scale[1] += *framedata++ * pose->channelscale[8]; scale[2] = pose->channeloffset[9]; if( pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; if( scale[0] < 0.0f || (int)( scale[0] - scale[1] ) || (int)( scale[1] - scale[2] ) ) { Log::Warn("R_LoadIQM: file %s contains an invalid scale.", mod_name ); return false; } // construct transformation TransInitRotationQuat( rotate, trans ); TransAddScale( scale[0], trans ); TransAddTranslation( translate, trans ); } } // copy vertexarrays and indexes vertexarray = ( iqmVertexArray_t* )IQMPtr( header, header->ofs_vertexarrays ); for(unsigned i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { int n; // total number of values n = header->num_vertexes * vertexarray->size; switch( vertexarray->type ) { case IQM_POSITION: ClearBounds( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); Com_Memcpy( IQModel->positions, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); for( int j = 0; j < n; j += vertexarray->size ) { AddPointToBounds( &IQModel->positions[ j ], IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); } IQModel->internalScale = BoundsMaxExtent( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); if( IQModel->internalScale > 0.0f ) { float inverseScale = 1.0f / IQModel->internalScale; for( int j = 0; j < n; j += vertexarray->size ) { VectorScale( &IQModel->positions[ j ], inverseScale, &IQModel->positions[ j ] ); } } break; case IQM_NORMAL: Com_Memcpy( IQModel->normals, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); break; case IQM_TANGENT: BuildTangents( header->num_vertexes, ( float* )IQMPtr( header, vertexarray->offset ), IQModel->normals, IQModel->tangents, IQModel->bitangents ); break; case IQM_TEXCOORD: for( int j = 0; j < n; j++ ) { IQModel->texcoords[ j ] = floatToHalf( ((float *)IQMPtr( header, vertexarray->offset ))[ j ] ); } break; case IQM_BLENDINDEXES: Com_Memcpy( IQModel->blendIndexes, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; case IQM_BLENDWEIGHTS: weights = (u8vec4_t *)IQMPtr( header, vertexarray->offset ); for(unsigned j = 0; j < header->num_vertexes; j++ ) { IQModel->blendWeights[ 4 * j + 0 ] = 255 - weights[ j ][ 1 ] - weights[ j ][ 2 ] - weights[ j ][ 3 ]; IQModel->blendWeights[ 4 * j + 1 ] = weights[ j ][ 1 ]; IQModel->blendWeights[ 4 * j + 2 ] = weights[ j ][ 2 ]; IQModel->blendWeights[ 4 * j + 3 ] = weights[ j ][ 3 ]; } break; case IQM_COLOR: Com_Memcpy( IQModel->colors, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; } } // copy triangles triangle = ( iqmTriangle_t* )IQMPtr( header, header->ofs_triangles ); for(unsigned i = 0; i < header->num_triangles; i++, triangle++ ) { IQModel->triangles[3*i+0] = triangle->vertex[0]; IQModel->triangles[3*i+1] = triangle->vertex[1]; IQModel->triangles[3*i+2] = triangle->vertex[2]; } // convert data where necessary and create VBO if( r_vboModels->integer && glConfig2.vboVertexSkinningAvailable && IQModel->num_joints <= glConfig2.maxVertexSkinningBones ) { if( IQModel->blendIndexes ) { indexbuf = (int *)ri.Hunk_AllocateTempMemory( sizeof(int[4]) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { indexbuf[ 4 * i + 0 ] = IQModel->blendIndexes[ 4 * i + 0 ]; indexbuf[ 4 * i + 1 ] = IQModel->blendIndexes[ 4 * i + 1 ]; indexbuf[ 4 * i + 2 ] = IQModel->blendIndexes[ 4 * i + 2 ]; indexbuf[ 4 * i + 3 ] = IQModel->blendIndexes[ 4 * i + 3 ]; } } else { indexbuf = nullptr; } if( IQModel->blendWeights ) { const float weightscale = 1.0f / 255.0f; weightbuf = (float *)ri.Hunk_AllocateTempMemory( sizeof(vec4_t) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { if( IQModel->blendWeights[ 4 * i + 0 ] == 0 && IQModel->blendWeights[ 4 * i + 1 ] == 0 && IQModel->blendWeights[ 4 * i + 2 ] == 0 && IQModel->blendWeights[ 4 * i + 3 ] == 0 ) IQModel->blendWeights[ 4 * i + 0 ] = 255; weightbuf[ 4 * i + 0 ] = weightscale * IQModel->blendWeights[ 4 * i + 0 ]; weightbuf[ 4 * i + 1 ] = weightscale * IQModel->blendWeights[ 4 * i + 1 ]; weightbuf[ 4 * i + 2 ] = weightscale * IQModel->blendWeights[ 4 * i + 2 ]; weightbuf[ 4 * i + 3 ] = weightscale * IQModel->blendWeights[ 4 * i + 3 ]; } } else { weightbuf = nullptr; } qtangentbuf = (i16vec4_t *)ri.Hunk_AllocateTempMemory( sizeof( i16vec4_t ) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { R_TBNtoQtangents( &IQModel->tangents[ 3 * i ], &IQModel->bitangents[ 3 * i ], &IQModel->normals[ 3 * i ], qtangentbuf[ i ] ); } vboData.xyz = (vec3_t *)IQModel->positions; vboData.qtangent = qtangentbuf; vboData.numFrames = 0; vboData.color = (u8vec4_t *)IQModel->colors; vboData.st = (i16vec2_t *)IQModel->texcoords; vboData.noLightCoords = true; vboData.boneIndexes = (int (*)[4])indexbuf; vboData.boneWeights = (vec4_t *)weightbuf; vboData.numVerts = IQModel->num_vertexes; vbo = R_CreateStaticVBO( "IQM surface VBO", vboData, vboLayout_t::VBO_LAYOUT_SKELETAL ); if( qtangentbuf ) { ri.Hunk_FreeTempMemory( qtangentbuf ); } if( weightbuf ) { ri.Hunk_FreeTempMemory( weightbuf ); } if( indexbuf ) { ri.Hunk_FreeTempMemory( indexbuf ); } // create IBO ibo = R_CreateStaticIBO( "IQM surface IBO", ( glIndex_t* )IQModel->triangles, IQModel->num_triangles * 3 ); } else { vbo = nullptr; ibo = nullptr; } // register shaders // overwrite the material offset with the shader index mesh = ( iqmMesh_t* )IQMPtr( header, header->ofs_meshes ); surface = IQModel->surfaces; for(unsigned i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { surface->surfaceType = surfaceType_t::SF_IQM; if( mesh->name ) { surface->name = str; name = ( char* )IQMPtr( header, header->ofs_text + mesh->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } else { surface->name = nullptr; } surface->shader = R_FindShader( ( char* )IQMPtr(header, header->ofs_text + mesh->material), shaderType_t::SHADER_3D_DYNAMIC, RSF_DEFAULT ); if( surface->shader->defaultShader ) surface->shader = tr.defaultShader; surface->data = IQModel; surface->first_vertex = mesh->first_vertex; surface->num_vertexes = mesh->num_vertexes; surface->first_triangle = mesh->first_triangle; surface->num_triangles = mesh->num_triangles; surface->vbo = vbo; surface->ibo = ibo; } // copy model bounds if(header->ofs_bounds) { iqmBounds_t *ptr = ( iqmBounds_t* )IQMPtr( header, header->ofs_bounds ); for(unsigned i = 0; i < header->num_frames; i++) { VectorCopy( ptr->bbmin, bounds ); bounds += 3; VectorCopy( ptr->bbmax, bounds ); bounds += 3; ptr++; } } // register animations IQAnim = IQModel->anims; if( header->num_anims == 1 ) { RE_RegisterAnimationIQM( mod_name, IQAnim ); } for(unsigned i = 0; i < header->num_anims; i++, IQAnim++ ) { char name[ MAX_QPATH ]; Com_sprintf( name, MAX_QPATH, "%s:%s", mod_name, IQAnim->name ); RE_RegisterAnimationIQM( name, IQAnim ); } // build VBO return true; }
/* ==================== R_LevelShot levelshots are specialized 128*128 thumbnails for the menu system, sampled down from full screen distorted images ==================== */ void R_LevelShot( screenshotType_e type, const char *ext ) { char fileName[MAX_OSPATH]; byte *source; byte *resample, *resamplestart; size_t offset = 0, memcount; int spadlen, rpadlen; int padwidth, linelen; GLint packAlign; byte *src, *dst; int x, y; int r, g, b; float xScale, yScale; int xx, yy; int width, height; int arg; // Allow custom resample width/height arg = atoi(ri.Cmd_Argv(2)); if (arg > 0) width = height = arg; else width = height = 128; if (width > glConfig.vidWidth) width = glConfig.vidWidth; if (height > glConfig.vidHeight) height = glConfig.vidHeight; Com_sprintf(fileName, sizeof(fileName), "levelshots/%s_small%s", tr.world->baseName, ext); source = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &spadlen); // // Based on RB_ReadPixels qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like resample = ri.Hunk_AllocateTempMemory(padwidth * height + offset + packAlign - 1); resamplestart = PADP((intptr_t) resample + offset, packAlign); offset = resamplestart - resample; rpadlen = padwidth - linelen; // // resample from source xScale = glConfig.vidWidth / (float)(width * 4.0f); yScale = glConfig.vidHeight / (float)(height * 3.0f); for ( y = 0 ; y < height ; y++ ) { for ( x = 0 ; x < width ; x++ ) { r = g = b = 0; for ( yy = 0 ; yy < 3 ; yy++ ) { for ( xx = 0 ; xx < 4 ; xx++ ) { src = source + (3 * glConfig.vidWidth + spadlen) * (int)((y*3 + yy) * yScale) + 3 * (int) ((x*4 + xx) * xScale); r += src[0]; g += src[1]; b += src[2]; } } dst = resample + 3 * ( y * width + x ); dst[0] = r / 12; dst[1] = g / 12; dst[2] = b / 12; } } memcount = (width * 3 + rpadlen) * height; // gamma correct if(glConfig.deviceSupportsGamma) R_GammaCorrect(resample + offset, memcount); if ( type == ST_TGA ) RE_SaveTGA(fileName, width, height, resample + offset, rpadlen); else if ( type == ST_JPEG ) RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, resample + offset, rpadlen); else if ( type == ST_PNG ) RE_SavePNG(fileName, width, height, resample + offset, rpadlen); ri.Hunk_FreeTempMemory(resample); ri.Hunk_FreeTempMemory(source); ri.Printf( PRINT_ALL, "Wrote %s\n", fileName ); }