/* model_sprite.c sprite model loading and caching Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include "QF/qendian.h" #include "QF/sys.h" #include "mod_internal.h" #include "qfalloca.h" void Mod_LoadSpriteFrame (mspriteframe_t *frame, const dspriteframe_t *dframe) { frame->width = dframe->width; frame->height = dframe->height; frame->up = dframe->origin[1]; frame->down = dframe->origin[1] - dframe->height; frame->left = dframe->origin[0]; frame->right = dframe->width + dframe->origin[0]; } static void * skip_frame (dspriteframe_t *frame) { __auto_type pixels = (byte *) (frame + 1); return pixels + frame->width * frame->height; } static void * swap_frame (dspriteframe_t *frame) { for (int i = 0; i < 2; i++) { frame->origin[i] = LittleLong (frame->origin[i]); } frame->width = LittleLong (frame->width); frame->height = LittleLong (frame->height); return skip_frame (frame); } static void * swap_group (dspritegroup_t *group) { group->numframes = LittleLong (group->numframes); __auto_type interval = (dspriteinterval_t *) (group + 1); for (int i = 0; i < group->numframes; i++) { interval->interval = LittleFloat (interval->interval); interval++; } __auto_type frame = (dspriteframe_t *) interval; for (int i = 0; i < group->numframes; i++) { frame = swap_frame (frame); } return frame + 1; } static int swap_sprite (dsprite_t *sprite) { sprite->ident = LittleLong (sprite->ident); sprite->version = LittleLong (sprite->version); sprite->type = LittleLong (sprite->type); sprite->boundingradius = LittleFloat (sprite->boundingradius); sprite->width = LittleLong (sprite->width); sprite->height = LittleLong (sprite->height); sprite->numframes = LittleLong (sprite->numframes); sprite->beamlength = LittleFloat (sprite->beamlength); sprite->synctype = LittleLong (sprite->synctype); int numframes = 0; __auto_type type = (dspriteframetype_t *) (sprite + 1); for (int i = 0; i < sprite->numframes; i++) { type->type = LittleLong (type->type); if (type->type == SPR_SINGLE) { __auto_type frame = (dspriteframe_t *) (type + 1); type = swap_frame (frame); numframes += 1; } else { __auto_type group = (dspritegroup_t *) (type + 1); type = swap_group (group); numframes += group->numframes; } } return numframes; } static void * find_group_frames (mspritegroup_t **group, dspritegroup_t *dgroup, mspriteframe_t ***frames, dspriteframe_t **dframes, int *frame_numbers, const char *modname) { int numframes = dgroup->numframes; size_t size = field_offset (mspritegroup_t, frames[numframes]); *group = Hunk_AllocName (0, size, modname); (*group)->numframes = numframes; (*group)->intervals = Hunk_AllocName (0, numframes * sizeof (float), modname); __auto_type interval = (dspriteinterval_t *) (dgroup + 1); for (int i = 0; i < numframes; i++) { (*group)->intervals[i] = interval->interval; interval++; } __auto_type dframe = (dspriteframe_t *) interval; for (int i = 0; i < numframes; i++) { frames[i] = &(*group)->frames[i]; dframes[i] = dframe; frame_numbers[i] = i; dframe = skip_frame (dframe); } return dframe; } static void find_frames (msprite_t *sprite, dsprite_t *dsprite, mspriteframe_t ***frames, dspriteframe_t **dframes, int *frame_numbers, const char *modname) { int frame_index = 0; __auto_type type = (dspriteframetype_t *) (dsprite + 1); for (int i = 0; i < dsprite->numframes; i++) { sprite->frames[i].type = type->type; if (type->type == SPR_SINGLE) { __auto_type frame = (dspriteframe_t *) (type + 1); dframes[frame_index] = frame; frames[frame_index] = &sprite->frames[i].frame; frame_numbers[frame_index] = i; frame_index += 1; type = skip_frame (frame); } else { __auto_type group = (dspritegroup_t *) (type + 1); type = find_group_frames (&sprite->frames[i].group, group, frames + frame_index, dframes + frame_index, frame_numbers + frame_index, modname); for (int j = 0; j < group->numframes; j++) { frame_numbers[frame_index + j] += i * 100; } frame_index += group->numframes; } } } void Mod_LoadSpriteModel (model_t *mod, void *buffer) { __auto_type dsprite = (dsprite_t *) buffer; msprite_t *sprite; if (LittleLong (dsprite->version) != SPR_VERSION) { Sys_Error ("%s has wrong version number (%i should be %i)", mod->path, LittleLong (dsprite->version), SPR_VERSION); } // total number of frames (direct and in groups) int numframes = swap_sprite (dsprite); if (numframes < 1) { Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d", numframes); } sprite = Hunk_AllocName (0, field_offset (msprite_t, frames[dsprite->numframes]), mod->name); sprite->type = dsprite->type; sprite->beamlength = dsprite->beamlength; sprite->numframes = dsprite->numframes; sprite->data = 0; mod->cache.data = sprite; mod->mins[0] = mod->mins[1] = -dsprite->width / 2; mod->maxs[0] = mod->maxs[1] = dsprite->width / 2; mod->mins[2] = -dsprite->height / 2; mod->maxs[2] = dsprite->height / 2; mod->numframes = dsprite->numframes; mod->type = mod_sprite; mod_sprite_ctx_t sprite_ctx = { .mod = mod, .dsprite = dsprite, .sprite = sprite, .numframes = numframes, .frame_numbers = alloca (numframes * sizeof (int)), .dframes = alloca (numframes * sizeof (dspriteframe_t *)), .frames = alloca (numframes * sizeof (mspriteframe_t **)), }; find_frames (sprite, dsprite, sprite_ctx.frames, sprite_ctx.dframes, sprite_ctx.frame_numbers, mod->name); m_funcs->Mod_SpriteLoadFrames (&sprite_ctx); }