Andrey Akhmichin
5 years ago
committed by
Alibek Omarov
16 changed files with 2032 additions and 1 deletions
@ -0,0 +1,52 @@ |
|||||||
|
# Makefile for mdldec
|
||||||
|
# Copyright (c) nekonomicon 2020
|
||||||
|
|
||||||
|
MODULE = mdldec |
||||||
|
|
||||||
|
CC ?= gcc |
||||||
|
CFLAGS ?= -O3 -pipe -DHAVE_TGMATH_H |
||||||
|
LDFLAGS ?= -Wl,--no-undefined |
||||||
|
|
||||||
|
SYS = $(shell $(CC) -dumpmachine) |
||||||
|
|
||||||
|
ifneq (, $(findstring mingw, $(SYS))) |
||||||
|
EXT = .exe |
||||||
|
else |
||||||
|
EXT = |
||||||
|
endif |
||||||
|
|
||||||
|
APP = $(MODULE)$(EXT) |
||||||
|
|
||||||
|
SRC = mdldec.c \
|
||||||
|
qc.c \
|
||||||
|
smd.c \
|
||||||
|
texture.c \
|
||||||
|
utils.c \
|
||||||
|
../../public/xash3d_mathlib.c \
|
||||||
|
../../public/matrixlib.c \
|
||||||
|
../../public/crtlib.c |
||||||
|
|
||||||
|
INCLUDE = -I. \
|
||||||
|
-I../../common \
|
||||||
|
-I../../engine \
|
||||||
|
-I../../engine/common \
|
||||||
|
-I../../engine/common/imagelib \
|
||||||
|
-I../../public |
||||||
|
|
||||||
|
LIBS = -lm |
||||||
|
|
||||||
|
OBJS = $(SRC:%.c=%.o) |
||||||
|
|
||||||
|
all: $(APP) |
||||||
|
|
||||||
|
$(APP): $(OBJS) |
||||||
|
$(CC) $(LDFLAGS) -o $(APP) $(OBJS) $(LIBS) |
||||||
|
|
||||||
|
.c.o: |
||||||
|
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ |
||||||
|
|
||||||
|
.PHONY: all clean |
||||||
|
|
||||||
|
clean: |
||||||
|
$(RM) $(OBJS) |
||||||
|
$(RM) $(APP) |
@ -0,0 +1,302 @@ |
|||||||
|
/*
|
||||||
|
mdldec.c - Half-Life Studio Model Decompiler |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include "const.h" |
||||||
|
#include "com_model.h" |
||||||
|
#include "crtlib.h" |
||||||
|
#include "studio.h" |
||||||
|
#include "qc.h" |
||||||
|
#include "smd.h" |
||||||
|
#include "texture.h" |
||||||
|
#include "utils.h" |
||||||
|
#include "version.h" |
||||||
|
|
||||||
|
char destdir[MAX_SYSPATH]; |
||||||
|
char modelfile[MAX_SYSPATH]; |
||||||
|
studiohdr_t *model_hdr; |
||||||
|
studiohdr_t *texture_hdr; |
||||||
|
studiohdr_t **anim_hdr; |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
SequenceNameFix |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void SequenceNameFix( void ) |
||||||
|
{ |
||||||
|
int i, j, counter; |
||||||
|
qboolean hasduplicates = false; |
||||||
|
mstudioseqdesc_t *seqdesc, *seqdesc1; |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numseq; i++ ) |
||||||
|
{ |
||||||
|
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i; |
||||||
|
|
||||||
|
counter = 1; |
||||||
|
|
||||||
|
for( j = 0; j < model_hdr->numseq; j++ ) |
||||||
|
{ |
||||||
|
seqdesc1 = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + j; |
||||||
|
|
||||||
|
if( j != i && !Q_strncmp( seqdesc1->label, seqdesc->label, sizeof( seqdesc1->label ) ) ) |
||||||
|
Q_snprintf( seqdesc1->label, sizeof( seqdesc1->label ), "%s_%i", seqdesc1->label, ++counter ); |
||||||
|
} |
||||||
|
|
||||||
|
if( counter > 1 ) |
||||||
|
{ |
||||||
|
printf( "WARNING: Sequence name \"%s\" is repeated %i times.\n", seqdesc->label, counter ); |
||||||
|
|
||||||
|
Q_snprintf( seqdesc->label, sizeof( seqdesc->label ), "%s_1", seqdesc->label ); |
||||||
|
|
||||||
|
hasduplicates = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if( hasduplicates ) |
||||||
|
puts( "WARNING: Added numeric suffix to repeated sequence name(s)." ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
BoneNameFix |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void BoneNameFix( void ) |
||||||
|
{ |
||||||
|
int i, counter = 0; |
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbones; i++ ) |
||||||
|
{ |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i; |
||||||
|
|
||||||
|
if( bone->name[0] == '\0' ) |
||||||
|
Q_sprintf( bone->name, "MDLDEC_Bone%i", ++counter ); |
||||||
|
} |
||||||
|
|
||||||
|
if( counter ) |
||||||
|
printf( "WARNING: Gived name to %i unnamed bone(s).\n", counter ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
LoadMDL |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static qboolean LoadMDL( const char *modelname ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
size_t len; |
||||||
|
char texturename[MAX_SYSPATH]; |
||||||
|
char seqgroupname[MAX_SYSPATH]; |
||||||
|
const char *ext; |
||||||
|
const char id_mdlhdr[] = {'I', 'D', 'S', 'T'}; |
||||||
|
const char id_seqhdr[] = {'I', 'D', 'S', 'Q'}; |
||||||
|
|
||||||
|
printf( "MDL: %s\n", modelname ); |
||||||
|
|
||||||
|
len = Q_strlen( modelname ); |
||||||
|
|
||||||
|
if( len > MAX_SYSPATH - 3 ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Source path is too long.\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ext = COM_FileExtension( modelname ); |
||||||
|
|
||||||
|
if( !ext ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Source file does not have extension.\n" ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( Q_strcmp( ext, "mdl" ) ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Only .mdl-files is supported.\n" ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
model_hdr = (studiohdr_t *)LoadFile( modelname ); |
||||||
|
|
||||||
|
if( !model_hdr ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't open %s\n", modelname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( memcmp( &model_hdr->ident, id_mdlhdr, sizeof( id_mdlhdr ) ) ) |
||||||
|
{ |
||||||
|
if( !memcmp( &model_hdr->ident, id_seqhdr, sizeof( id_seqhdr ) ) ) |
||||||
|
fprintf( stderr, "ERROR: %s is not a main HL model file.\n", modelname ); |
||||||
|
else |
||||||
|
fprintf( stderr, "ERROR: %s is not a valid HL model file.\n", modelname ); |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( model_hdr->version != STUDIO_VERSION ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: %s has unknown Studio MDL format version.\n", modelname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( !model_hdr->numbodyparts ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: %s is not a main HL model file.\n", modelname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( destdir[0] != '\0' ) |
||||||
|
{ |
||||||
|
if( !IsFileExists( destdir ) ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Couldn't find directory %s\n", destdir ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
COM_PathSlashFix( destdir ); |
||||||
|
} |
||||||
|
else |
||||||
|
COM_ExtractFilePath( modelname, destdir ); |
||||||
|
|
||||||
|
len -= 4; // path length without extension
|
||||||
|
|
||||||
|
if( !model_hdr->numtextures ) |
||||||
|
{ |
||||||
|
Q_strcpy( texturename, modelname ); |
||||||
|
Q_strcpy( &texturename[len], "t.mdl" ); |
||||||
|
|
||||||
|
texture_hdr = (studiohdr_t *)LoadFile( texturename ); |
||||||
|
|
||||||
|
if( !texture_hdr ) |
||||||
|
{ |
||||||
|
#if !XASH_WIN32 |
||||||
|
// dirty hack for casesensetive filesystems
|
||||||
|
texturename[len] = 'T'; |
||||||
|
|
||||||
|
texture_hdr = (studiohdr_t *)LoadFile( texturename ); |
||||||
|
|
||||||
|
if( !texture_hdr ) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't open external textures file %s\n", texturename ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if( memcmp( &texture_hdr->ident, id_mdlhdr, sizeof( id_mdlhdr ) ) ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: %s is not a valid external textures file.\n", texturename ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
texture_hdr = model_hdr; |
||||||
|
|
||||||
|
anim_hdr = malloc( sizeof( studiohdr_t* ) * model_hdr->numseqgroups ); |
||||||
|
|
||||||
|
if( !anim_hdr ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Couldn't allocate memory for sequences.\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
anim_hdr[0] = model_hdr; |
||||||
|
|
||||||
|
if( model_hdr->numseqgroups > 1 ) |
||||||
|
{ |
||||||
|
Q_strcpy( seqgroupname, modelname ); |
||||||
|
|
||||||
|
for( i = 1; i < model_hdr->numseqgroups; i++ ) |
||||||
|
{ |
||||||
|
Q_sprintf( &seqgroupname[len], "%02d.mdl", i ); |
||||||
|
|
||||||
|
anim_hdr[i] = (studiohdr_t *)LoadFile( seqgroupname ); |
||||||
|
|
||||||
|
if( !anim_hdr[i] ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't open sequence file %s\n", seqgroupname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( memcmp( &anim_hdr[i]->ident, id_seqhdr, sizeof( id_seqhdr ) ) ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: %s is not a valid sequence file.\n", seqgroupname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
COM_FileBase( modelname, modelfile ); |
||||||
|
|
||||||
|
SequenceNameFix(); |
||||||
|
|
||||||
|
BoneNameFix(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
ShowHelp |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void ShowHelp( const char *app_name ) |
||||||
|
{ |
||||||
|
printf( "usage: %s source_file\n", app_name ); |
||||||
|
printf( " %s source_file target_directory\n", app_name ); |
||||||
|
} |
||||||
|
|
||||||
|
int main( int argc, char *argv[] ) |
||||||
|
{ |
||||||
|
puts( "\nHalf-Life Studio Model Decompiler " APP_VERSION ); |
||||||
|
puts( "Copyright Flying With Gauss 2020 (c) " ); |
||||||
|
puts( "--------------------------------------------------" ); |
||||||
|
|
||||||
|
if( argc == 1 ) |
||||||
|
{ |
||||||
|
ShowHelp( argv[0] ); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
else if( argc == 3 ) |
||||||
|
{ |
||||||
|
if( Q_strlen( argv[2] ) > MAX_SYSPATH - 1 ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Destination path is too long.\n", stderr ); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
Q_strcpy( destdir, argv[2] ); |
||||||
|
} |
||||||
|
|
||||||
|
if( !LoadActivityList( argv[0] ) || !LoadMDL( argv[1] ) ) |
||||||
|
goto end; |
||||||
|
|
||||||
|
WriteQCScript(); |
||||||
|
WriteSMD(); |
||||||
|
WriteTextures(); |
||||||
|
|
||||||
|
puts( "Done." ); |
||||||
|
|
||||||
|
end: |
||||||
|
puts( "--------------------------------------------------" ); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
/*
|
||||||
|
mdldec.h - Half-Life Studio Model Decompiler |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifndef MDLDEC_H |
||||||
|
#define MDLDEC_H |
||||||
|
|
||||||
|
extern char destdir[MAX_SYSPATH]; |
||||||
|
extern char modelfile[MAX_SYSPATH]; |
||||||
|
extern studiohdr_t *model_hdr; |
||||||
|
extern studiohdr_t *texture_hdr; |
||||||
|
extern studiohdr_t **anim_hdr; |
||||||
|
|
||||||
|
#endif // MDLDEC_H
|
||||||
|
|
@ -0,0 +1,649 @@ |
|||||||
|
/*
|
||||||
|
qc.c - Quake C script writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include "eiface.h" |
||||||
|
#include "studio.h" |
||||||
|
#include "crtlib.h" |
||||||
|
#include "version.h" |
||||||
|
#include "mdldec.h" |
||||||
|
#include "utils.h" |
||||||
|
#include "qc.h" |
||||||
|
|
||||||
|
static char **activity_names; |
||||||
|
static int activity_count; |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
LoadActivityList |
||||||
|
============ |
||||||
|
*/ |
||||||
|
qboolean LoadActivityList( const char *appname ) |
||||||
|
{ |
||||||
|
FILE *fp; |
||||||
|
const char *p; |
||||||
|
char path[MAX_SYSPATH]; |
||||||
|
char buf[256]; |
||||||
|
|
||||||
|
fp = fopen( ACTIVITIES_FILE, "r" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
p = getenv( "MDLDEC_ACT_PATH" ); |
||||||
|
|
||||||
|
if( !p ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Couldn't find file " ACTIVITIES_FILE ".\n" \ |
||||||
|
"Place " ACTIVITIES_FILE " beside %s or set MDLDEC_ACT_PATH environment variable.\n", appname ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
Q_strncpy( path, p, MAX_SYSPATH - 1 ); |
||||||
|
|
||||||
|
COM_PathSlashFix( path ); |
||||||
|
|
||||||
|
Q_strncat( path, ACTIVITIES_FILE, MAX_SYSPATH ); |
||||||
|
|
||||||
|
fp = fopen( path, "r" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Can't open file " ACTIVITIES_FILE ".\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
while( fgets( buf, sizeof( buf ), fp ) ) |
||||||
|
{ |
||||||
|
activity_names = realloc( activity_names, sizeof( char* ) * ++activity_count ); |
||||||
|
|
||||||
|
if( !activity_names ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Couldn't allocate memory for activities strings.\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
COM_RemoveLineFeed( buf ); |
||||||
|
|
||||||
|
activity_names[activity_count - 1] = strdup( buf ); |
||||||
|
|
||||||
|
if( !activity_names[activity_count - 1] ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Couldn't allocate memory for activities strings.\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
FindActivityName |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static const char *FindActivityName( int type ) |
||||||
|
{ |
||||||
|
if( type >= 0 && type < activity_count ) |
||||||
|
return activity_names[type]; |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
GetMotionTypeString |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void GetMotionTypeString( int type, char *str, qboolean is_composite ) |
||||||
|
{ |
||||||
|
const char *p = NULL; |
||||||
|
|
||||||
|
str[0] = '\0'; |
||||||
|
|
||||||
|
if( is_composite ) |
||||||
|
{ |
||||||
|
if( type & STUDIO_X ) |
||||||
|
Q_strcat( str, " X" ); |
||||||
|
|
||||||
|
if( type & STUDIO_Y ) |
||||||
|
Q_strcat( str, " Y" ); |
||||||
|
|
||||||
|
if( type & STUDIO_Z ) |
||||||
|
Q_strcat( str, " Z" ); |
||||||
|
|
||||||
|
if( type & STUDIO_XR ) |
||||||
|
Q_strcat( str, " XR" ); |
||||||
|
|
||||||
|
if( type & STUDIO_YR ) |
||||||
|
Q_strcat( str, " YR" ); |
||||||
|
|
||||||
|
if( type & STUDIO_ZR ) |
||||||
|
Q_strcat( str, " ZR" ); |
||||||
|
|
||||||
|
if( type & STUDIO_LX ) |
||||||
|
Q_strcat( str, " LX" ); |
||||||
|
|
||||||
|
if( type & STUDIO_LY ) |
||||||
|
Q_strcat( str, " LY" ); |
||||||
|
|
||||||
|
if( type & STUDIO_LZ ) |
||||||
|
Q_strcat( str, " LZ" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AX ) |
||||||
|
Q_strcat( str, " AX" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AY ) |
||||||
|
Q_strcat( str, " AY" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AZ ) |
||||||
|
Q_strcat( str, " AZ" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AXR ) |
||||||
|
Q_strcat( str, " AXR" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AYR ) |
||||||
|
Q_strcat( str, " AYR" ); |
||||||
|
|
||||||
|
if( type & STUDIO_AZR ) |
||||||
|
Q_strcat( str, " AZR" ); |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
type &= STUDIO_TYPES; |
||||||
|
|
||||||
|
switch( type ) |
||||||
|
{ |
||||||
|
case STUDIO_X: p = "X"; break; |
||||||
|
case STUDIO_Y: p = "Y"; break; |
||||||
|
case STUDIO_Z: p = "Z"; break; |
||||||
|
case STUDIO_XR: p = "XR"; break; |
||||||
|
case STUDIO_YR: p = "YR"; break; |
||||||
|
case STUDIO_ZR: p = "ZR"; break; |
||||||
|
case STUDIO_LX: p = "LX"; break; |
||||||
|
case STUDIO_LY: p = "LY"; break; |
||||||
|
case STUDIO_LZ: p = "LZ"; break; |
||||||
|
case STUDIO_AX: p = "AX"; break; |
||||||
|
case STUDIO_AY: p = "AY"; break; |
||||||
|
case STUDIO_AZ: p = "AZ"; break; |
||||||
|
case STUDIO_AXR: p = "AXR"; break; |
||||||
|
case STUDIO_AYR: p = "AYR"; break; |
||||||
|
case STUDIO_AZR: p = "AZR"; break; |
||||||
|
default: break; |
||||||
|
} |
||||||
|
|
||||||
|
if( p ) |
||||||
|
Q_strcpy( str, p ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteTextureRenderMode |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteTextureRenderMode( FILE *fp ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiotexture_t *texture; |
||||||
|
|
||||||
|
for( i = 0; i < texture_hdr->numtextures; i++ ) |
||||||
|
{ |
||||||
|
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + i; |
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_FLATSHADE ) |
||||||
|
fprintf( fp,"$texrendermode \"%s\" \"flatshade\" \n", texture->name ); // sven-coop extension
|
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_CHROME ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"chrome\" \n", texture->name ); // sven-coop extension/may be added in HLMV
|
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_FULLBRIGHT ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"fullbright\" \n", texture->name ); // sven-coop extension/xash3d extension
|
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_NOMIPS ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"nomips\" \n", texture->name ); // sven-coop extension
|
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_NOSMOOTH ) |
||||||
|
{ |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"alpha\" \n", texture->name ); // sven-coop extension
|
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"nosmooth\" \n", texture->name ); // xash3d extension
|
||||||
|
} |
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_ADDITIVE ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"additive\" \n", texture->name ); |
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_MASKED ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"masked\" \n", texture->name ); |
||||||
|
|
||||||
|
if( texture->flags & ( STUDIO_NF_MASKED | STUDIO_NF_SOLID ) ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"masked_solid\" \n", texture->name ); // xash3d extension
|
||||||
|
|
||||||
|
if( texture->flags & STUDIO_NF_TWOSIDE ) |
||||||
|
fprintf( fp, "$texrendermode \"%s\" \"twoside\" \n", texture->name ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteSkinFamilyInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteSkinFamilyInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i, j, k; |
||||||
|
short *skinref, index; |
||||||
|
mstudiotexture_t *texture; |
||||||
|
|
||||||
|
if( texture_hdr->numskinfamilies < 2 ) |
||||||
|
return; |
||||||
|
|
||||||
|
fprintf( fp, "\n// %i skin families\n", texture_hdr->numskinfamilies ); |
||||||
|
|
||||||
|
fputs( "$texturegroup skinfamilies \n{\n", fp ); |
||||||
|
|
||||||
|
skinref = (short *)( (byte *)texture_hdr + texture_hdr->skinindex ); |
||||||
|
|
||||||
|
for( i = 0; i < texture_hdr->numskinfamilies; ++i ) |
||||||
|
{ |
||||||
|
fputs( "{", fp ); |
||||||
|
|
||||||
|
for( j = 0; j < texture_hdr->numskinref; ++j ) |
||||||
|
{ |
||||||
|
index = *( skinref + i * texture_hdr->numskinref + j ); |
||||||
|
|
||||||
|
for( k = 0; k < texture_hdr->numskinfamilies; ++k ) |
||||||
|
{ |
||||||
|
if( index == *( skinref + k * texture_hdr->numskinref + j ) ) |
||||||
|
continue; |
||||||
|
|
||||||
|
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + index; |
||||||
|
|
||||||
|
fprintf( fp, " \"%s\" ", texture->name ); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "}\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "}\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteAttachmentInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteAttachmentInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudioattachment_t *attachment; |
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
if( !model_hdr->numattachments ) |
||||||
|
return; |
||||||
|
|
||||||
|
fprintf( fp, "\n// %i attachment(s)\n", model_hdr->numattachments ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numattachments; ++i ) |
||||||
|
{ |
||||||
|
attachment = (mstudioattachment_t *)( (byte *)model_hdr + model_hdr->attachmentindex ) + i; |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + attachment->bone; |
||||||
|
|
||||||
|
fprintf( fp, "$attachment %i \"%s\" %f %f %f\n", i, bone->name, attachment->org[0], attachment->org[1], attachment->org[2] ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteBodyGroupInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteBodyGroupInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
mstudiobodyparts_t *bodypart; |
||||||
|
mstudiomodel_t *model; |
||||||
|
char modelname[64]; |
||||||
|
|
||||||
|
fputs( "\n// reference mesh(es)\n", fp ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbodyparts; ++i ) |
||||||
|
{ |
||||||
|
bodypart = (mstudiobodyparts_t *) ( (byte *)model_hdr + model_hdr->bodypartindex ) + i; |
||||||
|
|
||||||
|
if( bodypart->nummodels == 1 ) |
||||||
|
{ |
||||||
|
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex ); |
||||||
|
|
||||||
|
COM_FileBase( model->name, modelname ); |
||||||
|
|
||||||
|
fprintf( fp, "$body \"%s\" \"%s\"\n\n", bodypart->name, model->name ); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
fprintf( fp, "$bodygroup \"%s\"\n", bodypart->name ); |
||||||
|
|
||||||
|
fputs( "{\n", fp ); |
||||||
|
|
||||||
|
for( j = 0; j < bodypart->nummodels; ++j ) |
||||||
|
{ |
||||||
|
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex ) + j; |
||||||
|
|
||||||
|
if( !Q_strncmp( model->name, "blank", 5 ) ) |
||||||
|
{ |
||||||
|
fputs( "blank\n", fp ); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
COM_FileBase( model->name, modelname ); |
||||||
|
|
||||||
|
fprintf( fp, "studio \"%s\"\n", modelname ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "}\n\n" , fp ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteControllerInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteControllerInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiobonecontroller_t *bonecontroller; |
||||||
|
mstudiobone_t *bone; |
||||||
|
char motion_types[64]; |
||||||
|
|
||||||
|
if( !model_hdr->numbonecontrollers ) |
||||||
|
return; |
||||||
|
|
||||||
|
fprintf( fp, "\n// %i bone controller(s)\n", model_hdr->numbonecontrollers ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbonecontrollers; ++i ) |
||||||
|
{ |
||||||
|
bonecontroller = (mstudiobonecontroller_t *)( (byte *)model_hdr + model_hdr->bonecontrollerindex ) + i; |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + bonecontroller->bone; |
||||||
|
|
||||||
|
GetMotionTypeString( bonecontroller->type & ~STUDIO_RLOOP, motion_types, false ); |
||||||
|
|
||||||
|
fprintf( fp, "$controller %i \"%s\" %s %f %f\n", |
||||||
|
bonecontroller->index, bone->name, motion_types, |
||||||
|
bonecontroller->start, bonecontroller->end ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteHitBoxInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteHitBoxInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiobbox_t *hitbox; |
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
if( !model_hdr->numhitboxes ) |
||||||
|
return; |
||||||
|
|
||||||
|
fprintf( fp, "\n// %i hit box(es)\n", model_hdr->numhitboxes ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numhitboxes; i++ ) |
||||||
|
{ |
||||||
|
hitbox = (mstudiobbox_t *)( (byte *)model_hdr + model_hdr->hitboxindex ) + i; |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + hitbox->bone; |
||||||
|
|
||||||
|
fprintf( fp, "$hbox %i \"%s\" %f %f %f %f %f %f\n", |
||||||
|
hitbox->group, bone->name, |
||||||
|
hitbox->bbmin[0], hitbox->bbmin[1], hitbox->bbmin[2], |
||||||
|
hitbox->bbmax[0], hitbox->bbmax[1], hitbox->bbmax[2] ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteSequenceInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteSequenceInfo( FILE *fp ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
const char *activity; |
||||||
|
char motion_types[256]; |
||||||
|
mstudioevent_t *event; |
||||||
|
mstudioseqdesc_t *seqdesc; |
||||||
|
|
||||||
|
if( model_hdr->numseqgroups > 1 ) |
||||||
|
fputs( "\n$sequencegroupsize 64\n", fp ); |
||||||
|
|
||||||
|
if( model_hdr->numseq > 0 ) |
||||||
|
fprintf( fp, "\n// %i animation sequence(s)\n", model_hdr->numseq ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numseq; ++i ) |
||||||
|
{ |
||||||
|
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i; |
||||||
|
|
||||||
|
fprintf( fp, "$sequence \"%s\" ", seqdesc->label ); |
||||||
|
|
||||||
|
if( seqdesc->numblends > 1 ) |
||||||
|
{ |
||||||
|
if( seqdesc->numblends > 2 ) |
||||||
|
{ |
||||||
|
fputs( "{\n", fp ); |
||||||
|
|
||||||
|
for( j = 0; j < seqdesc->numblends; j++ ) |
||||||
|
{ |
||||||
|
fputs( " ", fp ); |
||||||
|
|
||||||
|
fprintf( fp, "\"%s_blend%i\" ", seqdesc->label, j + 1 ); |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( " ", fp ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
fprintf( fp, "\"%s_blend1\" ", seqdesc->label ); |
||||||
|
fprintf( fp, "\"%s_blend2\" ", seqdesc->label ); |
||||||
|
} |
||||||
|
|
||||||
|
GetMotionTypeString( seqdesc->blendtype[0], motion_types, false ); |
||||||
|
|
||||||
|
fprintf( fp, "blend %s %.0f %.0f", |
||||||
|
motion_types, seqdesc->blendstart[0], seqdesc->blendend[0] ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
fprintf( fp, "\"%s\"", seqdesc->label ); |
||||||
|
} |
||||||
|
|
||||||
|
if( seqdesc->motiontype ) |
||||||
|
{ |
||||||
|
GetMotionTypeString( seqdesc->motiontype, motion_types, true ); |
||||||
|
|
||||||
|
fprintf( fp, "%s", motion_types ); |
||||||
|
} |
||||||
|
|
||||||
|
fprintf( fp, " fps %.0f ", seqdesc->fps ); |
||||||
|
|
||||||
|
if( seqdesc->flags == 1 ) |
||||||
|
fputs( "loop ", fp ); |
||||||
|
|
||||||
|
if( seqdesc->activity ) |
||||||
|
{ |
||||||
|
activity = FindActivityName( seqdesc->activity ); |
||||||
|
|
||||||
|
if( activity ) |
||||||
|
{ |
||||||
|
fprintf( fp, "%s %i ", activity, seqdesc->actweight ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
printf( "WARNING: Sequence %s has a custom activity flag (ACT_%i %i).\n", |
||||||
|
seqdesc->label, seqdesc->activity, seqdesc->actweight ); |
||||||
|
|
||||||
|
fprintf( fp, "ACT_%i %i ", seqdesc->activity, seqdesc->actweight ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if( seqdesc->entrynode && seqdesc->exitnode ) |
||||||
|
{ |
||||||
|
if( seqdesc->entrynode == seqdesc->exitnode ) |
||||||
|
fprintf( fp, "node %i ", seqdesc->entrynode ); |
||||||
|
else if( seqdesc->nodeflags ) |
||||||
|
fprintf( fp, "rtransition %i %i ", seqdesc->entrynode, seqdesc->exitnode ); |
||||||
|
else |
||||||
|
fprintf( fp, "transition %i %i ", seqdesc->entrynode, seqdesc->exitnode ); |
||||||
|
} |
||||||
|
|
||||||
|
if( seqdesc->numevents > 2 ) |
||||||
|
{ |
||||||
|
fputs( "{\n ", fp ); |
||||||
|
|
||||||
|
for( j = 0; j < seqdesc->numevents; j++ ) |
||||||
|
{ |
||||||
|
if( seqdesc->numblends <= 2 ) |
||||||
|
fputs( " ", fp ); |
||||||
|
else |
||||||
|
fputs( " ", fp ); |
||||||
|
|
||||||
|
event = (mstudioevent_t *)( (byte *)model_hdr + seqdesc->eventindex ) + j; |
||||||
|
|
||||||
|
fprintf( fp, "{ event %i %i", event->event, event->frame ); |
||||||
|
|
||||||
|
if( event->options[0] != '\0' ) |
||||||
|
fprintf( fp, " \"%s\"", event->options ); |
||||||
|
|
||||||
|
fputs( " }\n ", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "}", fp ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
for( j = 0; j < seqdesc->numevents; j++ ) |
||||||
|
{ |
||||||
|
event = (mstudioevent_t *)( (byte *)model_hdr + seqdesc->eventindex ) + j; |
||||||
|
|
||||||
|
fprintf( fp, "{ event %i %i", event->event, event->frame ); |
||||||
|
|
||||||
|
if( event->options[0] != '\0') |
||||||
|
fprintf( fp, " \"%s\"", event->options ); |
||||||
|
|
||||||
|
fputs( " } ", fp ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
|
||||||
|
if( seqdesc->numblends > 2 ) |
||||||
|
fputs( "}\n", fp ); |
||||||
|
|
||||||
|
if( seqdesc->numpivots ) |
||||||
|
printf( "WARNING: Sequence %s uses %i foot pivots, feature not supported.\n", |
||||||
|
seqdesc->label, seqdesc->numpivots ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteQCScript |
||||||
|
============ |
||||||
|
*/ |
||||||
|
void WriteQCScript( void ) |
||||||
|
{ |
||||||
|
FILE *fp; |
||||||
|
char filename[MAX_SYSPATH]; |
||||||
|
size_t len; |
||||||
|
|
||||||
|
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.qc", destdir, modelfile ); |
||||||
|
|
||||||
|
if( len >= MAX_SYSPATH ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.qc\n", modelfile ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fp = fopen( filename, "w" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't write %s\n", filename ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "/*\n", fp ); |
||||||
|
fputs( "==============================================================================\n\n", fp ); |
||||||
|
fputs( "QC script generated by Half-Life Studio Model Decompiler " APP_VERSION "\n", fp ); |
||||||
|
|
||||||
|
fprintf( fp, "Copyright Flying With Gauss %s (c) \n\n", Q_timestamp( TIME_YEAR_ONLY ) ); |
||||||
|
fprintf( fp, "%s.mdl\n\n", modelfile ); |
||||||
|
|
||||||
|
fputs( "Original internal name:\n", fp ); |
||||||
|
|
||||||
|
fprintf( fp, "\"%s\"\n\n", model_hdr->name ); |
||||||
|
|
||||||
|
fputs( "==============================================================================\n", fp ); |
||||||
|
fputs( "*/\n\n", fp ); |
||||||
|
|
||||||
|
fprintf( fp, "$modelname \"%s.mdl\"\n", modelfile ); |
||||||
|
|
||||||
|
fputs( "$cd \".\\\"\n", fp ); |
||||||
|
fputs( "$cdtexture \".\\\"\n", fp ); |
||||||
|
fputs( "$scale 1.0\n", fp ); |
||||||
|
fputs( "$cliptotextures\n", fp ); |
||||||
|
fputs( "\n", fp ); |
||||||
|
|
||||||
|
if( !model_hdr->numtextures ) |
||||||
|
fputs( "$externaltextures\n", fp ); |
||||||
|
|
||||||
|
if( model_hdr->flags != 0 ) |
||||||
|
{ |
||||||
|
fprintf( fp, "$flags %i\n", model_hdr->flags ); |
||||||
|
|
||||||
|
printf( "WARNING: This model uses the $flags keyword set to %i\n", model_hdr->flags ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
|
||||||
|
fprintf( fp, "$bbox %f %f %f", model_hdr->min[0], model_hdr->min[1], model_hdr->min[2] ); |
||||||
|
fprintf( fp, " %f %f %f\n", model_hdr->max[0], model_hdr->max[1], model_hdr->max[2] ); |
||||||
|
fprintf( fp, "$cbox %f %f %f", model_hdr->bbmin[0], model_hdr->bbmin[1], model_hdr->bbmin[2] ); |
||||||
|
fprintf( fp, " %f %f %f\n", model_hdr->bbmax[0], model_hdr->bbmax[1], model_hdr->bbmax[2] ); |
||||||
|
fprintf( fp, "$eyeposition %f %f %f\n", model_hdr->eyeposition[0], model_hdr->eyeposition[1], model_hdr->eyeposition[2] ); |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
|
||||||
|
WriteBodyGroupInfo( fp ); |
||||||
|
WriteTextureRenderMode( fp ); |
||||||
|
WriteSkinFamilyInfo( fp ); |
||||||
|
WriteAttachmentInfo( fp ); |
||||||
|
WriteControllerInfo( fp ); |
||||||
|
WriteHitBoxInfo( fp ); |
||||||
|
WriteSequenceInfo( fp ); |
||||||
|
|
||||||
|
fputs( "\n// End of QC script.\n", fp ); |
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
printf( "QC Script: %s\n", filename ); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
/*
|
||||||
|
qc.h - Quake C script writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifndef QC_H |
||||||
|
#define QC_H |
||||||
|
|
||||||
|
#define ACTIVITIES_FILE "activities.txt" |
||||||
|
|
||||||
|
qboolean LoadActivityList( const char *appname ); |
||||||
|
void WriteQCScript( void ); |
||||||
|
|
||||||
|
#endif // QC_H
|
||||||
|
|
@ -0,0 +1,103 @@ |
|||||||
|
ACT_RESET |
||||||
|
ACT_IDLE |
||||||
|
ACT_GUARD |
||||||
|
ACT_WALK |
||||||
|
ACT_RUN |
||||||
|
ACT_FLY |
||||||
|
ACT_SWIM |
||||||
|
ACT_HOP |
||||||
|
ACT_LEAP |
||||||
|
ACT_FALL |
||||||
|
ACT_LAND |
||||||
|
ACT_STRAFE_LEFT |
||||||
|
ACT_STRAFE_RIGHT |
||||||
|
ACT_ROLL_LEFT |
||||||
|
ACT_ROLL_RIGHT |
||||||
|
ACT_TURN_LEFT |
||||||
|
ACT_TURN_RIGHT |
||||||
|
ACT_CROUCH |
||||||
|
ACT_CROUCHIDLE |
||||||
|
ACT_STAND |
||||||
|
ACT_USE |
||||||
|
ACT_SIGNAL1 |
||||||
|
ACT_SIGNAL2 |
||||||
|
ACT_SIGNAL3 |
||||||
|
ACT_TWITCH |
||||||
|
ACT_COWER |
||||||
|
ACT_SMALL_FLINCH |
||||||
|
ACT_BIG_FLINCH |
||||||
|
ACT_RANGE_ATTACK1 |
||||||
|
ACT_RANGE_ATTACK2 |
||||||
|
ACT_MELEE_ATTACK1 |
||||||
|
ACT_MELEE_ATTACK2 |
||||||
|
ACT_RELOAD |
||||||
|
ACT_ARM |
||||||
|
ACT_DISARM |
||||||
|
ACT_EAT |
||||||
|
ACT_DIESIMPLE |
||||||
|
ACT_DIEBACKWARD |
||||||
|
ACT_DIEFORWARD |
||||||
|
ACT_DIEVIOLENT |
||||||
|
ACT_BARNACLE_HIT |
||||||
|
ACT_BARNACLE_PULL |
||||||
|
ACT_BARNACLE_CHOMP |
||||||
|
ACT_BARNACLE_CHEW |
||||||
|
ACT_SLEEP |
||||||
|
ACT_INSPECT_FLOOR |
||||||
|
ACT_INSPECT_WALL |
||||||
|
ACT_IDLE_ANGRY |
||||||
|
ACT_WALK_HURT |
||||||
|
ACT_RUN_HURT |
||||||
|
ACT_HOVER |
||||||
|
ACT_GLIDE |
||||||
|
ACT_FLY_LEFT |
||||||
|
ACT_FLY_RIGHT |
||||||
|
ACT_DETECT_SCENT |
||||||
|
ACT_SNIFF |
||||||
|
ACT_BITE |
||||||
|
ACT_THREAT_DISPLAY |
||||||
|
ACT_FEAR_DISPLAY |
||||||
|
ACT_EXCITED |
||||||
|
ACT_SPECIAL_ATTACK1 |
||||||
|
ACT_SPECIAL_ATTACK2 |
||||||
|
ACT_COMBAT_IDLE |
||||||
|
ACT_WALK_SCARED |
||||||
|
ACT_RUN_SCARED |
||||||
|
ACT_VICTORY_DANCE |
||||||
|
ACT_DIE_HEADSHOT |
||||||
|
ACT_DIE_CHESTSHOT |
||||||
|
ACT_DIE_GUTSHOT |
||||||
|
ACT_DIE_BACKSHOT |
||||||
|
ACT_FLINCH_HEAD |
||||||
|
ACT_FLINCH_CHEST |
||||||
|
ACT_FLINCH_STOMACH |
||||||
|
ACT_FLINCH_LEFTARM |
||||||
|
ACT_FLINCH_RIGHTARM |
||||||
|
ACT_FLINCH_LEFTLEG |
||||||
|
ACT_FLINCH_RIGHTLEG |
||||||
|
ACT_VM_NONE |
||||||
|
ACT_VM_DEPLOY |
||||||
|
ACT_VM_DEPLOY_EMPTY |
||||||
|
ACT_VM_HOLSTER |
||||||
|
ACT_VM_HOLSTER_EMPTY |
||||||
|
ACT_VM_IDLE1 |
||||||
|
ACT_VM_IDLE2 |
||||||
|
ACT_VM_IDLE3 |
||||||
|
ACT_VM_RANGE_ATTACK1 |
||||||
|
ACT_VM_RANGE_ATTACK2 |
||||||
|
ACT_VM_RANGE_ATTACK3 |
||||||
|
ACT_VM_MELEE_ATTACK1 |
||||||
|
ACT_VM_MELEE_ATTACK2 |
||||||
|
ACT_VM_MELEE_ATTACK3 |
||||||
|
ACT_VM_SHOOT_EMPTY |
||||||
|
ACT_VM_START_RELOAD |
||||||
|
ACT_VM_RELOAD |
||||||
|
ACT_VM_RELOAD_EMPTY |
||||||
|
ACT_VM_TURNON |
||||||
|
ACT_VM_TURNOFF |
||||||
|
ACT_VM_PUMP |
||||||
|
ACT_VM_PUMP_EMPTY |
||||||
|
ACT_VM_START_CHARGE |
||||||
|
ACT_VM_CHARGE |
||||||
|
ACT_VM_OVERLOAD |
||||||
|
ACT_VM_IDLE_EMPTY |
@ -0,0 +1,549 @@ |
|||||||
|
/*
|
||||||
|
smd.c - Studio Model Data format writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include "const.h" |
||||||
|
#include "com_model.h" |
||||||
|
#include "xash3d_mathlib.h" |
||||||
|
#include "crtlib.h" |
||||||
|
#include "studio.h" |
||||||
|
#include "mdldec.h" |
||||||
|
#include "smd.h" |
||||||
|
|
||||||
|
static matrix3x4 *bonetransform; |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
CreateBoneTransformMatrices |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static qboolean CreateBoneTransformMatrices( void ) |
||||||
|
{ |
||||||
|
bonetransform = calloc( model_hdr->numbones, sizeof( matrix3x4 ) ); |
||||||
|
|
||||||
|
if( !bonetransform ) |
||||||
|
{ |
||||||
|
fputs( "ERROR: Couldn't allocate memory for bone transformation matrices!\n", stderr ); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
FillBoneTransformMatrices |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void FillBoneTransformMatrices( void ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiobone_t *bone; |
||||||
|
matrix3x4 bonematrix; |
||||||
|
vec4_t q; |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbones; i++ ) |
||||||
|
{ |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i; |
||||||
|
|
||||||
|
AngleQuaternion( &bone->value[3], q, true ); |
||||||
|
Matrix3x4_FromOriginQuat( bonematrix, q, bone->value ); |
||||||
|
|
||||||
|
if( bone->parent == -1 ) |
||||||
|
{ |
||||||
|
Matrix3x4_Copy( bonetransform[i], bonematrix ); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
Matrix3x4_ConcatTransforms( bonetransform[i], bonetransform[bone->parent], bonematrix ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
RemoveBoneTransformMatrices |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void RemoveBoneTransformMatrices( void ) |
||||||
|
{ |
||||||
|
free( bonetransform ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
ClipRotations |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void ClipRotations( vec3_t angle ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
|
||||||
|
for( i = 0; i < 3; i++ ) |
||||||
|
{ |
||||||
|
while( angle[i] >= M_PI_F ) |
||||||
|
angle[i] -= M_PI2_F; |
||||||
|
|
||||||
|
while( angle[i] < -M_PI_F ) |
||||||
|
angle[i] += M_PI2_F; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
ProperBoneRotationZ |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void ProperBoneRotationZ( mstudioseqdesc_t *seqdesc, vec_t *motion, int frame, float angle ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
float c, s, x, y; |
||||||
|
float rot; |
||||||
|
|
||||||
|
for( i = 0; i < 3; i++ ) |
||||||
|
motion[i] += frame * 1.0f / seqdesc->numframes * seqdesc->linearmovement[i]; |
||||||
|
|
||||||
|
rot = DEG2RAD( angle ); |
||||||
|
|
||||||
|
s = sin( rot ); |
||||||
|
c = cos( rot ); |
||||||
|
|
||||||
|
x = motion[0]; |
||||||
|
y = motion[1]; |
||||||
|
|
||||||
|
motion[0] = c * x - s * y; |
||||||
|
motion[1] = s * x + c * y; |
||||||
|
|
||||||
|
motion[5] += rot; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
CalcBonePosition |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void CalcBonePosition( mstudioanim_t *anim, mstudiobone_t *bone, vec_t *motion, int frame ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
float value; |
||||||
|
mstudioanimvalue_t *animvalue; |
||||||
|
|
||||||
|
for( i = 0; i < 6; i++ ) |
||||||
|
{ |
||||||
|
motion[i] = bone->value[i]; |
||||||
|
|
||||||
|
if( !anim->offset[i] ) |
||||||
|
continue; |
||||||
|
|
||||||
|
animvalue = (mstudioanimvalue_t *)( (byte *)anim + anim->offset[i] ); |
||||||
|
|
||||||
|
j = frame; |
||||||
|
|
||||||
|
while( animvalue->num.total <= j ) |
||||||
|
{ |
||||||
|
j -= animvalue->num.total; |
||||||
|
animvalue += animvalue->num.valid + 1; |
||||||
|
} |
||||||
|
|
||||||
|
if( animvalue->num.valid > j ) |
||||||
|
value = animvalue[j + 1].value; |
||||||
|
else |
||||||
|
value = animvalue[animvalue->num.valid].value; |
||||||
|
|
||||||
|
motion[i] += value * bone->scale[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteNodes |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteNodes( FILE *fp ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
fputs( "nodes\n", fp ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbones; i++ ) |
||||||
|
{ |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i; |
||||||
|
|
||||||
|
fprintf( fp, "%3i \"%s\" %i\n", i, bone->name, bone->parent ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "end\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteSkeleton |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteSkeleton( FILE *fp ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
fputs( "skeleton\n", fp ); |
||||||
|
fputs( "time 0\n", fp ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbones; i++ ) |
||||||
|
{ |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i; |
||||||
|
|
||||||
|
fprintf( fp, "%3i", i ); |
||||||
|
|
||||||
|
for( j = 0; j < 6; j++ ) |
||||||
|
fprintf( fp, " %f", bone->value[j] ); |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "end\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteTriangleInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteTriangleInfo( FILE *fp, mstudiomodel_t *model, mstudiotexture_t *texture, mstudiotrivert_t **triverts, qboolean isevenstrip ) |
||||||
|
{ |
||||||
|
int i, indices[3]; |
||||||
|
int vert_index; |
||||||
|
int norm_index; |
||||||
|
int bone_index; |
||||||
|
float s, t, u, v; |
||||||
|
byte *vertbone; |
||||||
|
vec3_t *studioverts; |
||||||
|
vec3_t *studionorms; |
||||||
|
vec3_t vert, norm; |
||||||
|
|
||||||
|
if( isevenstrip ) |
||||||
|
{ |
||||||
|
indices[0] = 1; |
||||||
|
indices[1] = 2; |
||||||
|
indices[2] = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
indices[0] = 0; |
||||||
|
indices[1] = 1; |
||||||
|
indices[2] = 2; |
||||||
|
} |
||||||
|
|
||||||
|
vertbone = ( (byte *)model_hdr + model->vertinfoindex ); |
||||||
|
studioverts = (vec3_t *)( (byte *)model_hdr + model->vertindex ); |
||||||
|
studionorms = (vec3_t *)( (byte *)model_hdr + model->normindex ); |
||||||
|
|
||||||
|
s = 1.0f / texture->width; |
||||||
|
t = 1.0f / texture->height; |
||||||
|
|
||||||
|
fprintf( fp, "%s\n", texture->name ); |
||||||
|
|
||||||
|
for( i = 0; i < 3; i++ ) |
||||||
|
{ |
||||||
|
vert_index = triverts[indices[i]]->vertindex; |
||||||
|
norm_index = triverts[indices[i]]->normindex; |
||||||
|
bone_index = vertbone[vert_index]; |
||||||
|
|
||||||
|
Matrix3x4_VectorTransform( bonetransform[bone_index], studioverts[vert_index], vert ); |
||||||
|
Matrix3x4_VectorRotate( bonetransform[bone_index], studionorms[norm_index], norm ); |
||||||
|
VectorNormalize( norm ); |
||||||
|
|
||||||
|
u = ( triverts[indices[i]]->s + 1.0f ) * s; |
||||||
|
v = 1.0f - triverts[indices[i]]->t * t; |
||||||
|
|
||||||
|
fprintf( fp, "%3i %f %f %f %f %f %f %f %f\n", |
||||||
|
bone_index, |
||||||
|
vert[0], vert[1], vert[2], |
||||||
|
norm[0], norm[1], norm[2], |
||||||
|
u, v ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteTriangles |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteTriangles( FILE *fp, mstudiomodel_t *model ) |
||||||
|
{ |
||||||
|
int i, j, k; |
||||||
|
mstudiomesh_t *mesh; |
||||||
|
mstudiotexture_t *texture; |
||||||
|
mstudiotrivert_t *triverts[3]; |
||||||
|
short *tricmds; |
||||||
|
|
||||||
|
fputs( "triangles\n", fp ); |
||||||
|
|
||||||
|
for( i = 0; i < model->nummesh; i++ ) |
||||||
|
{ |
||||||
|
mesh = (mstudiomesh_t *)( (byte *)model_hdr + model->meshindex ) + i; |
||||||
|
tricmds = (short *)( (byte *)model_hdr + mesh->triindex ); |
||||||
|
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + mesh->skinref; |
||||||
|
|
||||||
|
while( ( j = *( tricmds++ ) ) ) |
||||||
|
{ |
||||||
|
if( j >= 0 ) |
||||||
|
{ |
||||||
|
// triangle fan
|
||||||
|
for( k = 0; j > 0; j--, k++, tricmds += 4 ) |
||||||
|
{ |
||||||
|
if( k == 0 ) |
||||||
|
{ |
||||||
|
triverts[0] = (mstudiotrivert_t *)tricmds; |
||||||
|
} |
||||||
|
else if( k == 1 ) |
||||||
|
{ |
||||||
|
triverts[2] = (mstudiotrivert_t *)tricmds; |
||||||
|
} |
||||||
|
else if( k == 2 ) |
||||||
|
{ |
||||||
|
triverts[1] = (mstudiotrivert_t *)tricmds; |
||||||
|
|
||||||
|
WriteTriangleInfo( fp, model, texture, triverts, true ); |
||||||
|
} |
||||||
|
else if( k % 2 ) |
||||||
|
{ |
||||||
|
triverts[0] = triverts[2]; |
||||||
|
triverts[2] = (mstudiotrivert_t *)tricmds; |
||||||
|
|
||||||
|
WriteTriangleInfo( fp, model, texture, triverts, false ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
triverts[0] = triverts[1]; |
||||||
|
triverts[1] = (mstudiotrivert_t *)tricmds; |
||||||
|
|
||||||
|
WriteTriangleInfo( fp, model, texture, triverts, true ); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// triangle strip
|
||||||
|
j = abs( j ); |
||||||
|
|
||||||
|
for( k = 0; j > 0; j--, k++, tricmds += 4 ) |
||||||
|
{ |
||||||
|
if( k == 0 ) |
||||||
|
{ |
||||||
|
triverts[0] = (mstudiotrivert_t *)tricmds; |
||||||
|
} |
||||||
|
else if( k == 1 ) |
||||||
|
{ |
||||||
|
triverts[2] = (mstudiotrivert_t *)tricmds; |
||||||
|
} |
||||||
|
else if( k == 2 ) |
||||||
|
{ |
||||||
|
triverts[1] = (mstudiotrivert_t *)tricmds; |
||||||
|
|
||||||
|
WriteTriangleInfo( fp, model, texture, triverts, false ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
triverts[2] = triverts[1]; |
||||||
|
triverts[1] = (mstudiotrivert_t *)tricmds; |
||||||
|
|
||||||
|
WriteTriangleInfo( fp, model, texture, triverts, false ); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "end\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteFrameInfo |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteFrameInfo( FILE *fp, mstudioanim_t *anim, mstudioseqdesc_t *seqdesc, int frame ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
vec_t motion[6]; // x, y, z, xr, yr, zr
|
||||||
|
mstudiobone_t *bone; |
||||||
|
|
||||||
|
fprintf( fp, "time %i\n", frame ); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbones; i++, anim++ ) |
||||||
|
{ |
||||||
|
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i; |
||||||
|
|
||||||
|
CalcBonePosition( anim, bone, motion, frame ); |
||||||
|
|
||||||
|
if( bone->parent == -1 ) |
||||||
|
ProperBoneRotationZ( seqdesc, motion, frame, 270.0f ); |
||||||
|
|
||||||
|
ClipRotations( &motion[3] ); |
||||||
|
|
||||||
|
fprintf( fp, "%3i ", i ); |
||||||
|
|
||||||
|
for( j = 0; j < 6; j++ ) |
||||||
|
fprintf( fp, " %f", motion[j] ); |
||||||
|
|
||||||
|
fputs( "\n", fp ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteAnimations |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteAnimations( FILE *fp, mstudioseqdesc_t *seqdesc, int blend ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudioanim_t *anim; |
||||||
|
|
||||||
|
fputs( "skeleton\n", fp ); |
||||||
|
|
||||||
|
anim = (mstudioanim_t *)( (byte *)anim_hdr[seqdesc->seqgroup] + seqdesc->animindex ); |
||||||
|
anim += blend * model_hdr->numbones; |
||||||
|
|
||||||
|
for( i = 0; i < seqdesc->numframes; i++ ) |
||||||
|
WriteFrameInfo( fp, anim, seqdesc, i ); |
||||||
|
|
||||||
|
fputs( "end\n", fp ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteReferences |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteReferences( void ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
size_t len; |
||||||
|
FILE *fp; |
||||||
|
mstudiomodel_t *model; |
||||||
|
mstudiobodyparts_t *bodypart; |
||||||
|
char name[64]; |
||||||
|
char filename[MAX_SYSPATH]; |
||||||
|
|
||||||
|
if( !CreateBoneTransformMatrices() ) |
||||||
|
return; |
||||||
|
|
||||||
|
FillBoneTransformMatrices(); |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numbodyparts; i++ ) |
||||||
|
{ |
||||||
|
bodypart = (mstudiobodyparts_t *)( (byte *)model_hdr + model_hdr->bodypartindex ) + i; |
||||||
|
|
||||||
|
for( j = 0; j < bodypart->nummodels; j++ ) |
||||||
|
{ |
||||||
|
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex ) + j; |
||||||
|
|
||||||
|
if( !Q_strncmp( model->name, "blank", 5 ) ) |
||||||
|
continue; |
||||||
|
|
||||||
|
COM_FileBase( model->name, name ); |
||||||
|
|
||||||
|
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.smd", destdir, name ); |
||||||
|
|
||||||
|
if( len >= MAX_SYSPATH ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.smd\n", name ); |
||||||
|
RemoveBoneTransformMatrices(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fp = fopen( filename, "w" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't write %s\n", filename ); |
||||||
|
RemoveBoneTransformMatrices(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "version 1\n", fp ); |
||||||
|
|
||||||
|
WriteNodes( fp ); |
||||||
|
WriteSkeleton( fp ); |
||||||
|
WriteTriangles( fp, model ); |
||||||
|
|
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
printf( "Reference: %s\n", filename ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RemoveBoneTransformMatrices(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteSequences |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteSequences( void ) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
size_t len; |
||||||
|
FILE *fp; |
||||||
|
char filename[MAX_SYSPATH]; |
||||||
|
mstudioseqdesc_t *seqdesc; |
||||||
|
|
||||||
|
for( i = 0; i < model_hdr->numseq; i++ ) |
||||||
|
{ |
||||||
|
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i; |
||||||
|
|
||||||
|
for( j = 0; j < seqdesc->numblends; j++ ) |
||||||
|
{ |
||||||
|
if( seqdesc->numblends == 1 ) |
||||||
|
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.smd", destdir, seqdesc->label ); |
||||||
|
else |
||||||
|
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s_blend%i.smd", destdir, seqdesc->label, j + 1 ); |
||||||
|
|
||||||
|
if( len >= MAX_SYSPATH ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.smd\n", seqdesc->label ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fp = fopen( filename, "w" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't write %s\n", filename ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fputs( "version 1\n", fp ); |
||||||
|
|
||||||
|
WriteNodes( fp ); |
||||||
|
WriteAnimations( fp, seqdesc, j ); |
||||||
|
|
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
printf( "Sequence: %s\n", filename ); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void WriteSMD( void ) |
||||||
|
{ |
||||||
|
WriteReferences(); |
||||||
|
WriteSequences(); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
/*
|
||||||
|
smd.h - Studio Model Data format writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifndef SMD_H |
||||||
|
#define SMD_H |
||||||
|
|
||||||
|
void WriteSMD( void ); |
||||||
|
|
||||||
|
#endif // SMD_H
|
||||||
|
|
@ -0,0 +1,127 @@ |
|||||||
|
/*
|
||||||
|
texture.c - texture writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include "const.h" |
||||||
|
#include "crtlib.h" |
||||||
|
#include "studio.h" |
||||||
|
#include "img_bmp.h" |
||||||
|
#include "mdldec.h" |
||||||
|
#include "texture.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteBMP |
||||||
|
============ |
||||||
|
*/ |
||||||
|
static void WriteBMP( mstudiotexture_t *texture ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
FILE *fp; |
||||||
|
const byte *p; |
||||||
|
byte *palette, *pic, *buf; |
||||||
|
char filename[MAX_SYSPATH], texturename[64]; |
||||||
|
rgba_t rgba_palette[256]; |
||||||
|
bmp_t bmp_hdr = {0,}; |
||||||
|
size_t texture_size, len; |
||||||
|
|
||||||
|
COM_FileBase( texture->name, texturename ); |
||||||
|
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.bmp", destdir, texturename ); |
||||||
|
|
||||||
|
if( len >= MAX_SYSPATH ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.bmp\n", texturename ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fp = fopen( filename, "wb" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
{ |
||||||
|
fprintf( stderr, "ERROR: Can't write texture file %s\n", filename ); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
texture_size = texture->height * texture->width; |
||||||
|
pic = (byte *)texture_hdr + texture->index; |
||||||
|
palette = pic + texture_size; |
||||||
|
|
||||||
|
bmp_hdr.id[0] = 'B'; |
||||||
|
bmp_hdr.id[1] = 'M'; |
||||||
|
bmp_hdr.width = texture->width; |
||||||
|
bmp_hdr.height = texture->height; |
||||||
|
bmp_hdr.planes = 1; |
||||||
|
bmp_hdr.bitsPerPixel = 8; |
||||||
|
bmp_hdr.bitmapDataSize = texture_size; |
||||||
|
bmp_hdr.colors = 256; |
||||||
|
|
||||||
|
bmp_hdr.fileSize = sizeof( bmp_hdr ) + texture_size + sizeof( rgba_palette ); |
||||||
|
bmp_hdr.bitmapDataOffset = sizeof( bmp_hdr ) + sizeof( rgba_palette ); |
||||||
|
bmp_hdr.bitmapHeaderSize = BI_SIZE; |
||||||
|
|
||||||
|
fwrite( &bmp_hdr, sizeof( bmp_hdr ), 1, fp ); |
||||||
|
|
||||||
|
p = palette; |
||||||
|
|
||||||
|
for( i = 0; i < (int)bmp_hdr.colors; i++ ) |
||||||
|
{ |
||||||
|
rgba_palette[i][2] = *p++; |
||||||
|
rgba_palette[i][1] = *p++; |
||||||
|
rgba_palette[i][0] = *p++; |
||||||
|
rgba_palette[i][3] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
fwrite( rgba_palette, sizeof( rgba_palette ), 1, fp ); |
||||||
|
|
||||||
|
buf = malloc( texture_size ); |
||||||
|
|
||||||
|
p = pic; |
||||||
|
p += ( bmp_hdr.height - 1 ) * bmp_hdr.width; |
||||||
|
|
||||||
|
for( i = 0; i < bmp_hdr.height; i++ ) |
||||||
|
{ |
||||||
|
memcpy( buf + bmp_hdr.width * i, p, bmp_hdr.width ); |
||||||
|
p -= bmp_hdr.width; |
||||||
|
} |
||||||
|
|
||||||
|
fwrite( buf, texture_size, 1, fp ); |
||||||
|
|
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
free( buf ); |
||||||
|
|
||||||
|
printf( "Texture: %s\n", filename ); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
WriteTextures |
||||||
|
============ |
||||||
|
*/ |
||||||
|
void WriteTextures( void ) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
mstudiotexture_t *texture; |
||||||
|
|
||||||
|
for( i = 0; i < texture_hdr->numtextures; i++ ) |
||||||
|
{ |
||||||
|
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + i; |
||||||
|
|
||||||
|
WriteBMP( texture ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
/*
|
||||||
|
texture.h - texture writer |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifndef TEXTURE_H |
||||||
|
#define TEXTURE_H |
||||||
|
|
||||||
|
void WriteTextures( void ); |
||||||
|
|
||||||
|
#endif // TEXTURE_H
|
||||||
|
|
@ -0,0 +1,86 @@ |
|||||||
|
/*
|
||||||
|
utils.c - Useful helper functions |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include "xash3d_types.h" |
||||||
|
#include "crtlib.h" |
||||||
|
#include "utils.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
IsFileExists |
||||||
|
============ |
||||||
|
*/ |
||||||
|
qboolean IsFileExists( const char *filename ) |
||||||
|
{ |
||||||
|
struct stat st; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = stat( filename, &st ); |
||||||
|
|
||||||
|
if( ret == -1 ) |
||||||
|
return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
GetFileSize |
||||||
|
============ |
||||||
|
*/ |
||||||
|
off_t GetFileSize( FILE *fp ) |
||||||
|
{ |
||||||
|
struct stat st; |
||||||
|
int fd; |
||||||
|
|
||||||
|
fd = fileno( fp ); |
||||||
|
fstat( fd, &st ); |
||||||
|
|
||||||
|
return st.st_size; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
============ |
||||||
|
LoadFile |
||||||
|
============ |
||||||
|
*/ |
||||||
|
byte *LoadFile( const char *filename ) |
||||||
|
{ |
||||||
|
FILE *fp; |
||||||
|
byte *buf; |
||||||
|
off_t size; |
||||||
|
|
||||||
|
fp = fopen( filename, "rb" ); |
||||||
|
|
||||||
|
if( !fp ) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
size = GetFileSize( fp ); |
||||||
|
|
||||||
|
buf = malloc( size ); |
||||||
|
|
||||||
|
if( !buf ) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
fread( buf, size, 1, fp ); |
||||||
|
fclose( fp ); |
||||||
|
|
||||||
|
return buf; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,24 @@ |
|||||||
|
/*
|
||||||
|
utils.h - Useful helper functions |
||||||
|
Copyright (C) 2020 Andrey Akhmichin |
||||||
|
|
||||||
|
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 3 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. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifndef UTILS_H |
||||||
|
#define UTILS_H |
||||||
|
|
||||||
|
qboolean IsFileExists( const char *filename ); |
||||||
|
off_t GetFileSize( FILE *fp ); |
||||||
|
byte *LoadFile( const char *filename ); |
||||||
|
|
||||||
|
#endif // UTILS_H
|
||||||
|
|
Loading…
Reference in new issue