summaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'unittests')
-rw-r--r--unittests/autotestscripts/check_debug_dlls.pl25
-rw-r--r--unittests/autotestscripts/check_dynamic_shader_compile.pl15
-rw-r--r--unittests/autotestscripts/datafiles/gwolf.tgabin0 -> 4194348 bytes
-rw-r--r--unittests/autotestscripts/filecompare_tests.cfg16
-rw-r--r--unittests/autotestscripts/reference_output/file_size_monitor.txt6
-rw-r--r--unittests/autotestscripts/reference_output/mathlib_test.txt3
-rw-r--r--unittests/autotestscripts/reference_output/rt_test.txt6
-rw-r--r--unittests/autotestscripts/reference_output/testprocess.txt1
-rw-r--r--unittests/autotestscripts/reference_output/vtex.vtfbin0 -> 1398360 bytes
-rw-r--r--unittests/autotestscripts/run_file_comparison_tests.pl181
-rw-r--r--unittests/autotestscripts/subtests/file_size_monitor.pl40
-rw-r--r--unittests/autotestscripts/test_error_reporting.pl13
-rw-r--r--unittests/autotestscripts/test_shader_crcs.pl36
-rw-r--r--unittests/autotestscripts/verify_compiled_sheet_files_match_src.pl69
-rw-r--r--unittests/autotestscripts_graphics/keyimages/abovewater.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/belowwater.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_fullbright_1.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_fullbright_2.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_luxels_1.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_1.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_2.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/keyimages/mat_wireframe_1.tgabin0 -> 2359314 bytes
-rw-r--r--unittests/autotestscripts_graphics/rendering_regression_test.pl36
-rw-r--r--unittests/avitest/avitest.cpp233
-rw-r--r--unittests/avitest/avitest.vpc27
-rw-r--r--unittests/dmxtest/dmxtest.cpp45
-rw-r--r--unittests/dmxtest/dmxtest.vpc57
-rw-r--r--unittests/dmxtest/dmxtest_dmeloglayers.cpp908
-rw-r--r--unittests/dmxtest/dmxtest_vcdtodme.cpp110
-rw-r--r--unittests/dmxtest/dmxtestarray.cpp76
-rw-r--r--unittests/dmxtest/dmxtestdmelog.cpp393
-rw-r--r--unittests/dmxtest/dmxtestloader.cpp211
-rw-r--r--unittests/dmxtest/dmxtestnotify.cpp111
-rw-r--r--unittests/dmxtest/dmxtestserialization.cpp760
-rw-r--r--unittests/dmxtest/dmxtestundoredo.cpp1096
-rw-r--r--unittests/dmxtest/dmxtestvalue.cpp104
-rw-r--r--unittests/ihvtest1/ihvtest1.cpp1508
-rw-r--r--unittests/ihvtest1/ihvtest1.vpc106
-rw-r--r--unittests/ihvtest1/sys_clock.cpp254
-rw-r--r--unittests/inputtest/inputtest.cpp250
-rw-r--r--unittests/inputtest/inputtest.vpc24
-rw-r--r--unittests/materialsystemtest/materialsystemtest.cpp492
-rw-r--r--unittests/materialsystemtest/materialsystemtest.vpc24
-rw-r--r--unittests/mathlib_test/mathlib_test.cpp79
-rw-r--r--unittests/mathlib_test/mathlib_test.vpc38
-rw-r--r--unittests/networktest/networktest.cpp395
-rw-r--r--unittests/networktest/networktest.vpc23
-rw-r--r--unittests/rt_test/gwolf.tgabin0 -> 3141676 bytes
-rw-r--r--unittests/rt_test/rt_test.cpp87
-rw-r--r--unittests/rt_test/rt_test.exebin0 -> 735232 bytes
-rw-r--r--unittests/rt_test/rt_test.pdbbin0 -> 5255168 bytes
-rw-r--r--unittests/rt_test/rt_test.vpc39
-rw-r--r--unittests/shaderapitest/shaderapitest.cpp1056
-rw-r--r--unittests/shaderapitest/shaderapitest.vpc25
-rw-r--r--unittests/soundtest/soundtest.cpp306
-rw-r--r--unittests/soundtest/soundtest.vpc24
-rw-r--r--unittests/testprocess/testprocess.cpp191
-rw-r--r--unittests/testprocess/testprocess.exebin0 -> 100864 bytes
-rw-r--r--unittests/testprocess/testprocess.pdbbin0 -> 3223552 bytes
-rw-r--r--unittests/testprocess/testprocess.vpc38
-rw-r--r--unittests/tier1test/commandbuffertest.cpp308
-rw-r--r--unittests/tier1test/processtest.cpp52
-rw-r--r--unittests/tier1test/tier1test.cpp47
-rw-r--r--unittests/tier1test/tier1test.vpc39
-rw-r--r--unittests/tier1test/utlstringtest.cpp224
-rw-r--r--unittests/tier2test/tier2test.cpp44
-rw-r--r--unittests/tier2test/tier2test.vpc38
-rw-r--r--unittests/tier3test/tier3test.cpp44
-rw-r--r--unittests/tier3test/tier3test.vpc41
69 files changed, 10374 insertions, 0 deletions
diff --git a/unittests/autotestscripts/check_debug_dlls.pl b/unittests/autotestscripts/check_debug_dlls.pl
new file mode 100644
index 0000000..7f1e16c
--- /dev/null
+++ b/unittests/autotestscripts/check_debug_dlls.pl
@@ -0,0 +1,25 @@
+#!perl
+
+use File::Find;
+use Win32::API;
+
+find(\&ProcessFile, "../../../game/bin/" );
+
+sub ProcessFile
+ {
+ return if (/360/);
+ return unless( /\.dll$/i );
+ my $LoadLibrary = Win32::API->new( "kernel32", "LoadLibrary","P","L" );
+ my $GetProcAddress = Win32::API->new( "kernel32", "GetProcAddress","LP","L" );
+ my $FreeLibrary = Win32::API->new( "kernel32", "FreeLibrary", "P", "V" );
+ my $handle=$LoadLibrary->Call($_);
+ if ( $handle )
+ {
+ my $proc = $GetProcAddress->Call($handle, "BuiltDebug\0");
+ if ( $proc )
+ {
+ print "Error $_ is built debug\n";
+ }
+ $FreeLibrary->Call( $handle );
+ }
+ }
diff --git a/unittests/autotestscripts/check_dynamic_shader_compile.pl b/unittests/autotestscripts/check_dynamic_shader_compile.pl
new file mode 100644
index 0000000..39e8e3a
--- /dev/null
+++ b/unittests/autotestscripts/check_dynamic_shader_compile.pl
@@ -0,0 +1,15 @@
+#!perl
+
+open(DLL,"../../../game/bin/shaderapidx9.dll" ) || die "can't open shaderapi";
+
+binmode DLL;
+my $dllcode = do { local( $/ ) ; <DLL> } ; # slurp comparison output in
+close DLL;
+
+if ( $dllcode =~ /dynamic_shader_compile_is_on/s )
+ {
+ open(ERRORS,">errors.txt") || die "huh - can't write";
+ print ERRORS "stdshader_dx9.dll was built with dynamic shader compile!\n";
+ close ERRORS;
+ }
+
diff --git a/unittests/autotestscripts/datafiles/gwolf.tga b/unittests/autotestscripts/datafiles/gwolf.tga
new file mode 100644
index 0000000..c813d8a
--- /dev/null
+++ b/unittests/autotestscripts/datafiles/gwolf.tga
Binary files differ
diff --git a/unittests/autotestscripts/filecompare_tests.cfg b/unittests/autotestscripts/filecompare_tests.cfg
new file mode 100644
index 0000000..4f4cda6
--- /dev/null
+++ b/unittests/autotestscripts/filecompare_tests.cfg
@@ -0,0 +1,16 @@
+// this config file defines all unit tests which are run by running a program and comparing its output to reference output
+// which is checked in in the directory src/unittests/autotestscripts/reference_output
+
+// the format of entries is TESTNAME,OUTPUTFILE,COMMAND-LINE-TO-EXECUTE
+// if the OUTPUTFILE part is blank, it assumes the test is meant to compare the output of stdio
+
+file_size_monitor,,perl subtests/file_size_monitor.pl
+testprocess,,..\testprocess\testprocess -message "testprocess autotest1"
+vtex,gwolf.vtf,vtex -outdir . -nopause -nop4 -crcforce datafiles\gwolf.tga
+rt_test,,..\rt_test\rt_test ..\rt_test\gwolf.tga test 1024 1024
+// mathlib_test,,..\mathlib_test\mathlib_test
+
+
+
+
+
diff --git a/unittests/autotestscripts/reference_output/file_size_monitor.txt b/unittests/autotestscripts/reference_output/file_size_monitor.txt
new file mode 100644
index 0000000..1f3ef70
--- /dev/null
+++ b/unittests/autotestscripts/reference_output/file_size_monitor.txt
@@ -0,0 +1,6 @@
+Running file size monitor
+PC shader size := 291934326
+PC Game Bin DLL size := 108442741
+360 shader size := 0
+360 Game Bin DLL size := 0
+tf texture size := 1396651036
diff --git a/unittests/autotestscripts/reference_output/mathlib_test.txt b/unittests/autotestscripts/reference_output/mathlib_test.txt
new file mode 100644
index 0000000..e581561
--- /dev/null
+++ b/unittests/autotestscripts/reference_output/mathlib_test.txt
@@ -0,0 +1,3 @@
+right spherical triangle projected percentage=0.1250
+small spherical triangle projected percentage=0.00156
+sum of areas of cubemap cells = 1.00
diff --git a/unittests/autotestscripts/reference_output/rt_test.txt b/unittests/autotestscripts/reference_output/rt_test.txt
new file mode 100644
index 0000000..1cee230
--- /dev/null
+++ b/unittests/autotestscripts/reference_output/rt_test.txt
@@ -0,0 +1,6 @@
+reading src texture
+n triangles 1567238
+Creating kd-tree
+kd built time := 79
+Rendering
+pixels traced and lit per second := 1559694.998968
diff --git a/unittests/autotestscripts/reference_output/testprocess.txt b/unittests/autotestscripts/reference_output/testprocess.txt
new file mode 100644
index 0000000..63b55cd
--- /dev/null
+++ b/unittests/autotestscripts/reference_output/testprocess.txt
@@ -0,0 +1 @@
+testprocess autotest1 \ No newline at end of file
diff --git a/unittests/autotestscripts/reference_output/vtex.vtf b/unittests/autotestscripts/reference_output/vtex.vtf
new file mode 100644
index 0000000..f36a030
--- /dev/null
+++ b/unittests/autotestscripts/reference_output/vtex.vtf
Binary files differ
diff --git a/unittests/autotestscripts/run_file_comparison_tests.pl b/unittests/autotestscripts/run_file_comparison_tests.pl
new file mode 100644
index 0000000..7408a56
--- /dev/null
+++ b/unittests/autotestscripts/run_file_comparison_tests.pl
@@ -0,0 +1,181 @@
+#!perl
+
+# read stdio_test_list.cfg and perform all tests
+
+$create_refs=0;
+$subset_string=shift;
+
+@months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+@weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
+($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
+$year = 1900 + $yearOffset;
+$dstamp = "$weekDays[$dayOfWeek] $months[$month] $dayOfMonth $year".sprintf(" %02d:%02d:%02d", $hour,$minute,$second);
+$changelist_counter=`p4 counter main_changelist`; # grab value of p4 counter
+
+$dstamp.=" $changelist_counter";
+$dstamp=~ s/[\n\r]//g;
+
+$computername=$ENV{'COMPUTERNAME'};
+
+
+# first, set our priority to high and affinity to 1 to try to get more repeatable benchmark results
+#my $pid = $$;
+
+#my $cmd="datafiles\\process.exe -p $pid High";
+#print STDERR `$cmd`;
+#$cmd="datafiles\\process.exe -a $pid 01";
+#print STDERR `$cmd`;
+
+if ( open(CFGFILE, "filecompare_tests.cfg") )
+ {
+ while(<CFGFILE>)
+ {
+ s/[\n\r]//g;
+ s@//.*$@@; # kill comments
+ if (/^([^,]*),([^,]*),(.*$)/)
+ {
+ $testname=$1;
+ $testfile=$2;
+ $testcommand=$3;
+
+ next if ( length($subset_string) && ( ! ( $testname=~/$subset_string/i) ) );
+
+ $ext=".txt";
+ if ( length($testfile ) )
+ {
+ $ext="";
+ $ext = $1 if ( $testfile=~/(\..*)$/ ); # copy extension
+ unlink $testfile if ( -e $testfile); # kill it if it exists
+ }
+
+ print STDOUT "running $testname : $testcommand ($testfile)\n";
+ # suck the reference output in. use binary mode unless stdio
+ $refname="reference_output/$testname$ext";
+
+ # run the test
+ my $stime=time;
+ $output=`$testcommand`;
+ $stime=time-$stime;
+
+ if ( open(REF,$refname))
+ {
+ if ( length($testfile ))
+ {
+ binmode REF;
+ }
+ $ref_output= do { local( $/ ) ; <REF> } ; # slurp comparison output in
+ close REF;
+
+ if ( length( $testfile ) )
+ {
+ print STDERR $output;
+ # file case
+ if ( open(TESTFILE, $testfile ))
+ {
+ binmode TESTFILE;
+ $new_output= do { local( $/ ) ; <TESTFILE> } ; # slurp comparison output in
+ close TESTFILE;
+ if ($new_output ne $ref_output )
+ {
+ $errout.="ERROR: test $testname ($testcommand) : test produced file $testfile (length=".
+ length($new_output).") but it doesn't match the reference (length=".length($ref_output).")\n";
+ }
+ else
+ {
+ &UpdateMetrics( $testname, $output, $stime );
+ }
+ }
+ else
+ {
+ $errout.="ERROR: test $testname ($testcommand) : test was supposed to create $testfile, but didn't.\n";
+ }
+ }
+ else
+ {
+ # strip metrics (like timing) for comparison
+ my $massaged_ref = $ref_output;
+ my $massaged_output = $output;
+ $massaged_ref =~ s/:=\s*[0-9\.]+//g;
+ $massaged_output =~ s/:=\s*[0-9\.]+//g;
+ if ($massaged_output ne $massaged_ref )
+ {
+# print STDERR "o=$massaged_output r=$massaged_ref\n";
+ $errout.="ERROR: test $testname ($testcommand) : output does not match reference output.\n";
+ }
+ else
+ {
+ &UpdateMetrics( $testname, $output, $stime );
+ }
+ }
+ }
+ else
+ {
+ $errout.="ERROR: Can't open reference $refname for $testname\n";
+ if ($create_refs)
+ {
+ if ( length($testfile ) )
+ {
+ if ( -e $testfile )
+ {
+ $oname=$refname;
+ $oname=~s@/@\\@g;
+ print STDERR "copy $testfile $oname";
+ print STDERR `copy $testfile $oname`;
+ print STDERR `p4 add $oname`;
+ }
+ }
+ else
+ {
+ if ( open(REFOUT,">$refname") )
+ {
+ print REFOUT $output;
+ }
+ close REFOUT;
+ print STDERR `p4 add $refname`;
+ }
+ }
+ }
+ }
+ }
+ }
+else
+ {
+ $errout.="Can't open stdio_test_list.cfg\n";
+ }
+
+if (length($errout))
+{
+ print STDERR "There were errors: $errout";
+ open(ERRORS,">errors.txt") || die "huh - can't write";
+ print ERRORS $errout;
+ close ERRORS;
+}
+
+
+
+
+sub UpdateMetrics
+ {
+ return unless length($computername);
+ local( $tname, $output, $runtime) = @_;
+ $output .= "\ntest runtime := $runtime\n";
+ foreach $_ ( split(/\n/,$output))
+ {
+ if (/^(.+):=(.*$)/)
+ {
+ my $varname=$1;
+ my $etime=$2;
+ $varname=~s@^\s*@@g;
+ $varname=~s@\s*$@@g;
+ mkdir "\\\\fileserver\\user\\perf\\$computername";
+ mkdir "\\\\fileserver\\user\\perf\\$computername\\$tname";
+ if ( open(COUT,">>\\\\fileserver\\user\\perf\\$computername\\$tname\\$varname.csv") )
+ {
+ print COUT "\"$dstamp\",$etime\n";
+ close COUT;
+ }
+
+ }
+ }
+
+ }
diff --git a/unittests/autotestscripts/subtests/file_size_monitor.pl b/unittests/autotestscripts/subtests/file_size_monitor.pl
new file mode 100644
index 0000000..fa834d3
--- /dev/null
+++ b/unittests/autotestscripts/subtests/file_size_monitor.pl
@@ -0,0 +1,40 @@
+#!perl
+
+use File::Find;
+
+# customize here
+print "Running file size monitor\n";
+
+LogDirectorySize("PC shader size", "../../../game/hl2/shaders","\.vcs","\.360\.vcs");
+LogDirectorySize("PC Game Bin DLL size", "../../../game/bin/","\.dll","_360\.dll");
+LogDirectorySize("360 shader size", "../../../game/hl2/shaders","\.360\.vcs");
+LogDirectorySize("360 Game Bin DLL size", "../../../game/bin/","_360\.dll");
+LogDirectorySize("tf texture size","../../../game/tf/materials/","\.vtf");
+
+
+
+
+
+
+
+sub LogDirectorySize
+ {
+ my ($label, $basedir, $filepattern, $excludepattern ) = @_;
+ undef @FileList;
+ find(\&ProcessFile, $basedir);
+ my $total_size = 0;
+ foreach $_ (@FileList)
+ {
+ next if ( length($excludepattern) && ( /$excludepattern/i ) );
+ if (/$filepattern/i)
+ {
+ $total_size += (-s $_ );
+ }
+ }
+ print "$label := $total_size\n";
+ }
+
+sub ProcessFile
+ {
+ push @FileList, $File::Find::name;
+ }
diff --git a/unittests/autotestscripts/test_error_reporting.pl b/unittests/autotestscripts/test_error_reporting.pl
new file mode 100644
index 0000000..f705f9c
--- /dev/null
+++ b/unittests/autotestscripts/test_error_reporting.pl
@@ -0,0 +1,13 @@
+#! perl
+$errfname="//fileserver/user/cgreen/force_an_error.txt";
+
+if (-e $errfname )
+ {
+ unlink $errfname;
+ open(ERROUT,">errors.txt") || die "huh - can't write";
+ {
+ print ERROUT "This is not an error - its just a test of the error script system.\n";
+ close ERROUT;
+ }
+
+ }
diff --git a/unittests/autotestscripts/test_shader_crcs.pl b/unittests/autotestscripts/test_shader_crcs.pl
new file mode 100644
index 0000000..517aa02
--- /dev/null
+++ b/unittests/autotestscripts/test_shader_crcs.pl
@@ -0,0 +1,36 @@
+use Cwd;
+
+my $dir = getcwd;
+
+chdir "../../materialsystem/stdshaders";
+
+@output = `perl ..\\..\\devtools\\bin\\checkshaderchecksums.pl stdshader_dx9_20b.txt`;
+foreach $_ (@output)
+{
+ $output.=$_ unless(/appchooser360/i);
+}
+
+@output = `perl ..\\..\\devtools\\bin\\checkshaderchecksums.pl stdshader_dx9_30.txt`;
+foreach $_ (@output)
+{
+ $output.=$_ unless(/appchooser360/i);
+}
+
+my $errors;
+
+foreach $_ (@output )
+{
+ $errors.=$_ unless (/appchooser360movie/);
+}
+
+chdir $dir;
+
+print $errors;
+
+if( length( $errors ) > 0 )
+{
+ print "writing errors.txt\n";
+ open FP, ">errors.txt";
+ print FP "$errors";
+ close FP;
+}
diff --git a/unittests/autotestscripts/verify_compiled_sheet_files_match_src.pl b/unittests/autotestscripts/verify_compiled_sheet_files_match_src.pl
new file mode 100644
index 0000000..bce70ba
--- /dev/null
+++ b/unittests/autotestscripts/verify_compiled_sheet_files_match_src.pl
@@ -0,0 +1,69 @@
+#! perl
+
+
+use File::Find;
+use Cwd;
+use File::Basename;
+
+# find(\&Visitfile,"../../../content/tf");
+
+foreach $_ ( @sheetfiles )
+ {
+ $dest_sheet_name=$_;
+ $dest_sheet_name =~ s/\.mks/.sht/i;
+ $dest_sheet_name =~ s@/content/([^/]+)/materialsrc/@/game/\1/materials/@gi;
+ print "**Checking $_\n";
+ if (! -e $dest_sheet_name )
+ {
+ push @errors,"sheet $_ exists but not $dest_sheet_name";
+ }
+ else
+ {
+ # buid it and make sure they match
+ $cmd="cd ".dirname($_)." & mksheet ".basename($_)." test.sht";
+ $cmd=~tr/\//\\/;
+ $cmdout=`$cmd`;
+ if ( open(NEWFILE, dirname($_)."/test.sht") )
+ {
+ binmode NEWFILE;
+ open(OLDFILE, $dest_sheet_name ) || die "strange error - cant find $dest_sheet_name";
+ binmode OLDFILE;
+ {
+ local( $/, *FH ) ;
+ $old_data=<OLDFILE>;
+ $new_data=<NEWFILE>;
+ if ( $new_data ne $old_data )
+ {
+ push @errors,"Sheet source file $_ does not compile to the same output as the checked in $dest_sheet_name";
+ }
+ close OLDFILE;
+ close NEWFILE;
+ unlink dirname($_)."/test.sht";
+ }
+ }
+ else
+ {
+ push @errors, "Couldn't compile sheet $_ by running $cmd : \n$cmdout";
+ }
+ }
+ }
+
+$errout=join("\n", @errors);
+print $errout;
+if (length($errout))
+ {
+ open(ERRFILE,">errors.txt") || die "can't write errors.txt";
+ print ERRFILE $errout;
+ close ERRFILE;
+ }
+
+
+sub Visitfile
+ {
+ local($_)= $File::Find::name;
+ s@\\@\/@g;
+ if (m@content/(\S+)/.*\.mks$@i)
+ {
+ push @sheetfiles, $_;
+ }
+ }
diff --git a/unittests/autotestscripts_graphics/keyimages/abovewater.tga b/unittests/autotestscripts_graphics/keyimages/abovewater.tga
new file mode 100644
index 0000000..9700ce3
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/abovewater.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/belowwater.tga b/unittests/autotestscripts_graphics/keyimages/belowwater.tga
new file mode 100644
index 0000000..5641376
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/belowwater.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_fullbright_1.tga b/unittests/autotestscripts_graphics/keyimages/mat_fullbright_1.tga
new file mode 100644
index 0000000..ce60746
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_fullbright_1.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_fullbright_2.tga b/unittests/autotestscripts_graphics/keyimages/mat_fullbright_2.tga
new file mode 100644
index 0000000..ef516c5
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_fullbright_2.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_luxels_1.tga b/unittests/autotestscripts_graphics/keyimages/mat_luxels_1.tga
new file mode 100644
index 0000000..80e5bd1
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_luxels_1.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_1.tga b/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_1.tga
new file mode 100644
index 0000000..ac2b726
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_1.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_2.tga b/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_2.tga
new file mode 100644
index 0000000..04af7cb
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_showmiplevels_2.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/keyimages/mat_wireframe_1.tga b/unittests/autotestscripts_graphics/keyimages/mat_wireframe_1.tga
new file mode 100644
index 0000000..862b241
--- /dev/null
+++ b/unittests/autotestscripts_graphics/keyimages/mat_wireframe_1.tga
Binary files differ
diff --git a/unittests/autotestscripts_graphics/rendering_regression_test.pl b/unittests/autotestscripts_graphics/rendering_regression_test.pl
new file mode 100644
index 0000000..a29bd57
--- /dev/null
+++ b/unittests/autotestscripts_graphics/rendering_regression_test.pl
@@ -0,0 +1,36 @@
+use Cwd;
+
+my $dir = getcwd;
+
+chdir "../../../game";
+
+if( 1 )
+{
+ system "rd /s /q ep2\\screenshots";
+ system "mkdir ep2\\screenshots";
+ @output = `hl2.exe -allowdebug -autoconfig -console -toconsole -dev -sw -width 1024 -game ep2 -testscript rendering_regression_test.vtest`;
+}
+
+$keydir = "\\\\fileserver\\user\\rendering_regression_test";
+
+open TESTSCRIPT, "<ep2/testscripts/rendering_regression_test.vtest" || die;
+foreach $line (<TESTSCRIPT>)
+{
+ $line =~ s,//.*,,g; # remove comments
+ if( $line =~ m/\s*screenshot\s+(.*)$/i )
+ {
+ push @screenshots, $1;
+ }
+}
+close TESTSCRIPT;
+
+foreach $screenshot (@screenshots)
+{
+ $cmd = "tgamse $keydir\\$screenshot.tga ep2\\screenshots\\$screenshot.tga 0";
+ $output = `$cmd`;
+ if( $output =~ m/FAIL/ )
+ {
+ $cmd = "tgadiff $keydir\\$screenshot.tga ep2\\screenshots\\$screenshot.tga ep2\\screenshots\\$screenshot" . "_diff.tga";
+ system $cmd;
+ }
+}
diff --git a/unittests/avitest/avitest.cpp b/unittests/avitest/avitest.cpp
new file mode 100644
index 0000000..0002930
--- /dev/null
+++ b/unittests/avitest/avitest.cpp
@@ -0,0 +1,233 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Material editor
+//=============================================================================
+#include <windows.h>
+#include "appframework/vguimatsysapp.h"
+#include "filesystem.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "vgui/ISurface.h"
+#include "vgui/IVGui.h"
+#include "vgui_controls/controls.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "vgui/ILocalize.h"
+#include "vgui/IScheme.h"
+#include "avi/iavi.h"
+#include "avi/ibik.h"
+#include "tier3/tier3.h"
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CAVITestApp : public CVguiMatSysApp
+{
+ typedef CVguiMatSysApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown( );
+ virtual const char *GetAppName() { return "AVITest"; }
+ virtual bool AppUsesReadPixels() { return true; }
+
+private:
+ // Draws a quad
+ void DrawStuff( AVIMaterial_t hMaterial );
+ IMaterial *m_pMaterial;
+ float m_flStartTime;
+};
+
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CAVITestApp );
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CAVITestApp::Create()
+{
+ if ( !BaseClass::Create() )
+ return false;
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "valve_avi.dll", AVI_INTERFACE_VERSION },
+ { "valve_avi.dll", BIK_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ return AddSystems( appSystems );
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CAVITestApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if ( !g_pFullFileSystem || !g_pMaterialSystem || !g_pVGui || !g_pVGuiSurface || !g_pAVI || !g_pBIK )
+ return false;
+
+ g_pAVI->SetMainWindow( GetAppWindow() );
+ return true;
+}
+
+void CAVITestApp::PostShutdown( )
+{
+ g_pAVI->SetMainWindow( NULL );
+ BaseClass::PostShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a quad
+//-----------------------------------------------------------------------------
+void CAVITestApp::DrawStuff( AVIMaterial_t hMaterial )
+{
+ int iViewableWidth = GetWindowWidth();
+ int iViewableHeight = GetWindowHeight();
+
+ g_pAVI->SetTime( hMaterial, Sys_FloatTime() - m_flStartTime );
+
+ float flMaxU, flMaxV;
+ g_pAVI->GetTexCoordRange( hMaterial, &flMaxU, &flMaxV );
+ IMaterial *pMaterial = g_pAVI->GetMaterial( hMaterial );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity();
+ pRenderContext->Ortho( 0, 0, iViewableWidth, iViewableHeight, 0, 1 );
+
+ pRenderContext->Bind( pMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ // Draw a polygon the size of the panel
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( -0.5, iViewableHeight + 0.5, 0 );
+ meshBuilder.TexCoord2f( 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( -0.5, 0.5, 0 );
+ meshBuilder.TexCoord2f( 0, 0, flMaxV );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( iViewableWidth - 0.5, 0.5, 0 );
+ meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.Position3f( iViewableWidth - 0.5, iViewableHeight + 0.5, 0 );
+ meshBuilder.TexCoord2f( 0, flMaxU, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CAVITestApp::Main()
+{
+ if (!SetVideoMode())
+ return 0;
+
+ // load scheme
+ if (!vgui::scheme()->LoadSchemeFromFile("resource/BoxRocket.res", "ElementViewer" ))
+ {
+ Assert( 0 );
+ }
+
+ // load the boxrocket localization file
+ g_pVGuiLocalize->AddFile( "resource/boxrocket_%language%.txt" );
+
+ // load the base localization file
+ g_pVGuiLocalize->AddFile( "Resource/valve_%language%.txt" );
+ g_pFullFileSystem->AddSearchPath("platform", "PLATFORM");
+ g_pVGuiLocalize->AddFile( "Resource/vgui_%language%.txt");
+
+ // start vgui
+ g_pVGui->Start();
+
+ // add our main window
+// vgui::Panel *mainPanel = CreateElementViewerPanel();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ AVIParams_t params;
+ Q_strcpy( params.m_pFileName, "c:\\temp\\out.avi" );
+ Q_strcpy( params.m_pPathID, "MOD" );
+ pRenderContext->GetWindowSize( params.m_nWidth, params.m_nHeight );
+ params.m_nFrameRate = 24;
+ params.m_nFrameScale = 1;
+ params.m_nNumChannels = 0;
+
+ AVIHandle_t h = g_pAVI->StartAVI( params );
+ AVIMaterial_t hAVIMaterial = g_pAVI->CreateAVIMaterial( "avitest", "c:\\temp\\test.avi", "MOD" );
+
+ // run app frame loop
+ vgui::VPANEL root = g_pVGuiSurface->GetEmbeddedPanel();
+ g_pVGuiSurface->Invalidate( root );
+
+ int imagesize = params.m_nWidth * params.m_nHeight;
+ BGR888_t *hp = new BGR888_t[ imagesize ];
+
+ m_flStartTime = Sys_FloatTime();
+ m_pMaterial = g_pMaterialSystem->FindMaterial( "debug/debugempty", "app" );
+ while ( g_pVGui->IsRunning() )
+ {
+ AppPumpMessages();
+
+ pRenderContext->ClearColor4ub( 76, 88, 68, 255 );
+ pRenderContext->ClearBuffers( true, true );
+
+ DrawStuff( hAVIMaterial );
+
+ g_pVGui->RunFrame();
+
+ g_pVGuiSurface->PaintTraverse( root );
+
+ // Get Bits from material system
+ Rect_t rect;
+ rect.x = rect.y = 0;
+ rect.width = params.m_nWidth;
+ rect.height = params.m_nHeight;
+
+ pRenderContext->ReadPixelsAndStretch( &rect, &rect, (unsigned char*)hp,
+ IMAGE_FORMAT_BGR888, rect.width * sizeof( BGR888_t ) );
+ g_pAVI->AppendMovieFrame( h, hp );
+
+ g_pMaterialSystem->SwapBuffers();
+ }
+
+ delete[] hp;
+ g_pAVI->FinishAVI( h );
+ g_pAVI->DestroyAVIMaterial( hAVIMaterial );
+
+// delete mainPanel;
+
+ return 1;
+}
+
+
+
diff --git a/unittests/avitest/avitest.vpc b/unittests/avitest/avitest.vpc
new file mode 100644
index 0000000..a4708eb
--- /dev/null
+++ b/unittests/avitest/avitest.vpc
@@ -0,0 +1,27 @@
+//-----------------------------------------------------------------------------
+// AVITEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_win_win32_base.vpc"
+
+$Project "Avitest"
+{
+ $Folder "Source Files"
+ {
+ $File "avitest.cpp"
+ $File "$SRCDIR\public\vgui_controls\vgui_controls.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $DynamicFile "$SRCDIR\lib\public\appframework.lib"
+ $DynamicFile "$SRCDIR\lib\public\tier2.lib"
+ $DynamicFile "$SRCDIR\lib\public\tier3.lib"
+ $DynamicFile "$SRCDIR\lib\public\vgui_controls.lib"
+ }
+}
diff --git a/unittests/dmxtest/dmxtest.cpp b/unittests/dmxtest/dmxtest.cpp
new file mode 100644
index 0000000..55261d6
--- /dev/null
+++ b/unittests/dmxtest/dmxtest.cpp
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "filesystem.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "tier3/tier3dm.h"
+
+
+//-----------------------------------------------------------------------------
+// Used to connect/disconnect the DLL
+//-----------------------------------------------------------------------------
+class CDmxTestAppSystem : public CTier3DmAppSystem< IAppSystem >
+{
+ typedef CTier3DmAppSystem< IAppSystem > BaseClass;
+
+public:
+ CDmxTestAppSystem()
+ {
+ }
+
+ virtual bool Connect( CreateInterfaceFn factory )
+ {
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !g_pFullFileSystem || !g_pDataModel || !g_pDmElementFramework )
+ return false;
+
+ return true;
+ }
+
+ virtual InitReturnVal_t Init()
+ {
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+ return BaseClass::Init();
+ }
+};
+
+USE_UNITTEST_APPSYSTEM( CDmxTestAppSystem )
diff --git a/unittests/dmxtest/dmxtest.vpc b/unittests/dmxtest/dmxtest.vpc
new file mode 100644
index 0000000..6f5069f
--- /dev/null
+++ b/unittests/dmxtest/dmxtest.vpc
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------------
+// DMXTEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,$SRCDIR\game\shared,$SRCDIR\utils\common"
+ $PreprocessorDefinitions "$BASE;DMXTEST_EXPORTS"
+ }
+}
+
+$Project "Dmxtest"
+{
+ $Folder "Source Files"
+ {
+ $File "$SRCDIR\movieobjects\dmx_to_vcd.cpp"
+ $File "dmxtest.cpp"
+ $File "dmxtest_dmeloglayers.cpp"
+ $File "dmxtest_vcdtodme.cpp"
+ $File "dmxtestarray.cpp"
+ $File "dmxtestdmelog.cpp"
+ $File "dmxtestloader.cpp"
+ $File "dmxtestnotify.cpp"
+ $File "dmxtestserialization.cpp"
+ $File "dmxtestundoredo.cpp"
+ $File "$SRCDIR\public\interpolatortypes.cpp"
+ $File "$SRCDIR\public\movieobjects\movieobjects.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\movieobjects\dmx_to_vcd.h"
+ $File "$SRCDIR\public\interpolatortypes.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib datamodel
+ $Lib choreoobjects
+ $Lib dmserializers
+ $Lib mathlib
+ $Lib dmxloader
+ $Lib movieobjects
+ $Lib tier2
+ $Lib tier3
+ $Lib unitlib
+ }
+}
diff --git a/unittests/dmxtest/dmxtest_dmeloglayers.cpp b/unittests/dmxtest/dmxtest_dmeloglayers.cpp
new file mode 100644
index 0000000..77626b1
--- /dev/null
+++ b/unittests/dmxtest/dmxtest_dmeloglayers.cpp
@@ -0,0 +1,908 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "movieobjects/movieobjects.h"
+#include "datamodel/idatamodel.h"
+
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmelog.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define NUM_CHANNELS 1
+#define NUM_LOG_ENTRIES 10
+
+enum
+{
+ SPEW_DIFFS = (1<<0),
+ SPEW_VALUES= (1<<1),
+ SPEW_KEYS= (1<<2),
+};
+
+static void ValidateDataSets( int spew, char const *testname, CUtlVector< CUtlVector< float > >& values, CUtlVector< CUtlVector< float > >& valuesbaked )
+{
+ int i, j;
+ // Compare baked, unbaked values
+ Assert( values.Count() == valuesbaked.Count() );
+ int c = values.Count();
+ bool differs = false;
+ bool spewvalues = ( spew & SPEW_VALUES ) ? true : false;
+
+ float tol = 0.0001f;
+
+ for ( i = 0; i < c; ++i )
+ {
+ CUtlVector< float >& v = values[ i ];
+ CUtlVector< float >& vb = valuesbaked[ i ];
+
+ Assert( v.Count() == vb.Count() );
+
+ // Now get the values of the samples in the log
+ for ( j = 0; j < v.Count(); ++j )
+ {
+ Assert( vb.IsValidIndex( j ) );
+ if ( !vb.IsValidIndex( j ) )
+ continue;
+
+ float v1 = v[ j ];
+ float v2 = vb[ j ];
+ if ( fabs( v1 - v2 ) > tol )
+ {
+ differs = true;
+ }
+
+ if ( spewvalues )
+ {
+ Msg( "%d %f %f\n", j, v[ j ], vb[ j ] );
+ }
+ }
+ }
+
+ Msg( " %s\n", differs ? "FAILED" : "OK" );
+
+ if ( !(spew & SPEW_DIFFS ) )
+ return;
+
+ for ( i = 0; i < c; ++i )
+ {
+ CUtlVector< float >& v = values[ i ];
+ CUtlVector< float >& vb = valuesbaked[ i ];
+
+ Assert( v.Count() == vb.Count() );
+
+ // Now get the values of the samples in the log
+ for ( j = 0; j < v.Count(); ++j )
+ {
+ Assert( vb.IsValidIndex( j ) );
+ if ( !vb.IsValidIndex( j ) )
+ continue;
+
+ if ( v[ j ] == vb[ j ] )
+ {
+ if ( differs )
+ {
+ Msg( "%d found %f to equal %f\n", j, v[ j ], vb[ j ] );
+ }
+ continue;
+ }
+
+ Msg( "%d expected %f to equal %f\n", j, v[ j ], vb[ j ] );
+ }
+ }
+
+ if ( differs )
+ {
+ Msg( "End Test '%s'\n---------------\n", testname );
+ }
+}
+
+static void CreateChannels( int num, CUtlVector< CDmeChannel * >& channels, DmFileId_t fileid )
+{
+ CDisableUndoScopeGuard guard;
+
+ for ( int i = 0; i < num; ++i )
+ {
+ CDmeChannel *channel = NULL;
+
+ channel = CreateElement< CDmeChannel >( "channel1", fileid );
+ channels.AddToTail( channel );
+ channel->CreateLog( AT_FLOAT ); // only care about float logs for now
+ channel->SetMode( CM_PLAY );// Make sure it's in playback mode
+ }
+}
+
+struct TestLayer_t
+{
+ enum
+ {
+ TYPE_SIMPLESLOPE = 0, // value == time
+ TYPE_SINE, // sinusoidal
+ TYPE_CONSTANT,
+ };
+
+ TestLayer_t() :
+ startTime( 0 ),
+ endTime( 0 ),
+ timeStep( 0 ),
+ usecurvetype( false ),
+ curvetype( CURVE_DEFAULT ),
+ type( TYPE_SIMPLESLOPE ),
+ constantvalue( 0.0f )
+ {
+ }
+
+ float ValueForTime( DmeTime_t time ) const
+ {
+ float t = (float)time.GetSeconds();
+ switch ( type )
+ {
+ default:
+ case TYPE_SIMPLESLOPE:
+ return t;
+ case TYPE_SINE:
+ return constantvalue * ( 1.0f + sin( ( t * 0.002f ) * 2 * M_PI ) ) * 0.5f;
+ case TYPE_CONSTANT:
+ return constantvalue;
+ }
+
+ return t;
+ }
+
+ int type;
+ DmeTime_t startTime;
+ DmeTime_t endTime;
+ DmeTime_t timeStep;
+
+ bool usecurvetype;
+ int curvetype;
+
+ float constantvalue;
+};
+
+struct TestParams_t
+{
+ TestParams_t() :
+ testundo( false ),
+ usecurves( false ),
+ purgevalues( true ),
+ testabort( false ),
+ spew( 0 ),
+ spewnontopmostlayers( false ),
+ defaultcurve( CURVE_DEFAULT ),
+ mintime( DmeTime_t( 0 ) ),
+ maxtime( DmeTime_t( 100 ) )
+ {
+ }
+ int spew;
+ bool usecurves;
+ bool purgevalues;
+ bool testundo;
+ bool testabort;
+ bool spewnontopmostlayers;
+ int defaultcurve;
+ DmeTime_t mintime;
+ DmeTime_t maxtime;
+ CUtlVector< TestLayer_t > layers;
+
+ void Reset()
+ {
+ purgevalues = true;
+ usecurves = false;
+ testundo = false;
+ testabort = false;
+ spewnontopmostlayers = false;
+ spew = 0;
+ mintime = DmeTime_t( 0 );
+ maxtime = DmeTime_t( 100 );
+ defaultcurve = CURVE_DEFAULT;
+ layers.RemoveAll();
+ }
+
+ void AddLayer( DmeTime_t start, DmeTime_t end, DmeTime_t step, int curvetype, int valuetype, float constantvalue = 0.0f )
+ {
+ TestLayer_t tl;
+ tl.startTime = start;
+ tl.endTime = end;
+ tl.timeStep = step;
+ tl.curvetype = curvetype;
+ tl.type = valuetype;
+ tl.constantvalue = constantvalue;
+
+ layers.AddToTail( tl );
+ }
+};
+
+static void RunLayerTest( char const *testname, CUtlVector< CDmeChannel * >& channels, const TestParams_t& params )
+{
+ if ( params.layers.Count() == 0 )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ Msg( "Test '%s'...\n", testname );
+
+ g_pDataModel->StartUndo( testname, testname );
+
+ int i;
+ int c = channels.Count();
+
+ {
+ CDisableUndoScopeGuard guard;
+
+ for ( i = 0; i < NUM_CHANNELS; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+ pLog->ClearKeys(); // reset it
+
+ CDmeCurveInfo *pCurveInfo = NULL;
+ if ( params.usecurves )
+ {
+ pCurveInfo = pLog->GetOrCreateCurveInfo();
+ pCurveInfo->SetDefaultCurveType( params.defaultcurve );
+ pCurveInfo->SetMinValue( 0.0f );
+ pCurveInfo->SetMaxValue( 1000.0f );
+ }
+ else
+ {
+ if ( pLog->GetCurveInfo() )
+ {
+ g_pDataModel->DestroyElement( pLog->GetCurveInfo()->GetHandle() );
+ }
+ pLog->SetCurveInfo( NULL );
+ }
+
+ const TestLayer_t& tl = params.layers[ 0 ];
+ // Now add entries
+ DmeTime_t logStep = tl.timeStep;
+ DmeTime_t logStart = tl.startTime;
+
+ for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep )
+ {
+ DmeTime_t useTime = t;
+ if ( useTime > tl.endTime )
+ {
+ useTime = tl.endTime;
+ }
+ float value = tl.ValueForTime( useTime );
+ if ( tl.usecurvetype )
+ {
+ pLog->SetKey( useTime, value, tl.curvetype );
+ }
+ else
+ {
+ pLog->SetKey( useTime, value );
+ }
+ }
+ }
+ }
+
+ for ( int layer = 1; layer < params.layers.Count(); ++layer )
+ {
+ const TestLayer_t& tl = params.layers[ layer ];
+
+ // Test creating a layer and collapsing it back down
+ g_pChannelRecordingMgr->StartLayerRecording( "layer operations" );
+ for ( i = 0; i < c; ++i )
+ {
+ g_pChannelRecordingMgr->AddChannelToRecordingLayer( channels[ i ] ); // calls log->CreateNewLayer()
+ }
+
+ // Now add values to channel logs
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+
+ // Now add entries
+ DmeTime_t logStep = tl.timeStep;
+ DmeTime_t logStart = tl.startTime;
+
+ for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep )
+ {
+ DmeTime_t useTime = t;
+ if ( useTime > tl.endTime )
+ {
+ useTime = tl.endTime;
+ }
+ float value = tl.ValueForTime( useTime );
+ if ( tl.usecurvetype )
+ {
+ pLog->SetKey( useTime, value, tl.curvetype );
+ }
+ else
+ {
+ pLog->SetKey( useTime, value );
+ }
+ }
+ }
+
+ g_pChannelRecordingMgr->FinishLayerRecording( 0.0f, false ); // don't flatten layers here, we'll do it manually
+ }
+
+ // Now sample values
+ CUtlVector< CUtlVector< float > > values;
+ CUtlVector< CUtlVector< float > > valuesbaked;
+
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+
+ int idx = values.AddToTail();
+
+ CUtlVector< float >& v = values[ idx ];
+
+ // Now get the values of the samples in the log
+ for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) )
+ {
+ float fval = pLog->GetValue( j );
+ v.AddToTail( fval );
+ }
+ }
+
+ if ( params.spewnontopmostlayers )
+ {
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+
+ // Now get the values of the samples in the log
+ for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) )
+ {
+ float topValue = pLog->GetValue( j );
+ float underlyingValue = pLog->GetValueSkippingTopmostLayer( j );
+
+ Msg( "t(%d) top [%f] rest [%f]\n",
+ j.GetTenthsOfMS(), topValue, underlyingValue );
+ }
+ }
+ }
+
+ // Now test creating a layer and "undo/redo" of the layer
+ if ( params.testundo )
+ {
+ g_pDataModel->FinishUndo();
+ g_pDataModel->Undo();
+ g_pDataModel->Redo();
+ g_pDataModel->StartUndo( testname, testname );
+ }
+
+ {
+ CUndoScopeGuard guard( "Bake Layers" );
+ // Now bake down and resample values
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+
+ pLog->FlattenLayers( 0.0f, params.spew & SPEW_DIFFS );
+
+ int idx = valuesbaked.AddToTail();
+
+ CUtlVector< float >& v = valuesbaked[ idx ];
+
+ // Now get the values of the samples in the log
+ for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) )
+ {
+ float fval = pLog->GetValue( j );
+ v.AddToTail( fval );
+ }
+ }
+ }
+
+ ValidateDataSets( params.spew, testname, values, valuesbaked );
+
+ // Now test creating a layer and "undo/redo" of the layer
+ if ( params.testundo )
+ {
+ g_pDataModel->FinishUndo();
+ g_pDataModel->Undo();
+ g_pDataModel->Redo();
+ g_pDataModel->StartUndo( testname, testname );
+ }
+
+ if ( params.testabort )
+ {
+ g_pDataModel->AbortUndoableOperation();
+ }
+ else
+ {
+ g_pDataModel->FinishUndo();
+ }
+}
+
+static void RunTimeSelectionTest( char const *testname, CUtlVector< CDmeChannel * >& channels,
+ const TestParams_t& params, DmeTime_t tHeadPosition, DmeLog_TimeSelection_t& ts, float value )
+{
+ if ( params.layers.Count() == 0 )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ Msg( "Test '%s'...\n", testname );
+
+ int i, j;
+ int c = channels.Count();
+
+ if ( params.purgevalues )
+ {
+ CDisableUndoScopeGuard guard;
+
+ for ( i = 0; i < NUM_CHANNELS; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+ pLog->ClearKeys(); // reset it
+
+ CDmeCurveInfo *pCurveInfo = params.usecurves ? pLog->GetOrCreateCurveInfo() : pLog->GetCurveInfo();
+ if ( params.usecurves )
+ {
+ pCurveInfo->SetDefaultCurveType( params.defaultcurve );
+ pCurveInfo->SetMinValue( 0.0f );
+ pCurveInfo->SetMaxValue( 1000.0f );
+ }
+ else if ( !params.usecurves && pCurveInfo )
+ {
+ g_pDataModel->DestroyElement( pCurveInfo->GetHandle() );
+ pLog->SetCurveInfo( NULL );
+ }
+
+ const TestLayer_t& tl = params.layers[ 0 ];
+ // Now add entries
+ DmeTime_t logStep = tl.timeStep;
+ DmeTime_t logStart = tl.startTime;
+
+ for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep )
+ {
+ DmeTime_t useTime = t;
+ if ( useTime > tl.endTime )
+ useTime = tl.endTime;
+
+ float value = tl.ValueForTime( useTime );
+ if ( tl.usecurvetype )
+ {
+ pLog->SetKey( useTime, value, tl.curvetype );
+ }
+ else
+ {
+ pLog->SetKey( useTime, value );
+ }
+ }
+ }
+ }
+
+ // Test creating a layer and collapsing it back down
+ g_pChannelRecordingMgr->StartLayerRecording( "layer operations", &ts );
+ for ( i = 0; i < c; ++i )
+ {
+ g_pChannelRecordingMgr->AddChannelToRecordingLayer( channels[ i ] ); // calls log->CreateNewLayer()
+ }
+
+ // Now add values to channel logs
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+
+ pLog->StampKeyAtHead( tHeadPosition, tHeadPosition, ts, value );
+ }
+
+ // Flattens the layers
+ g_pChannelRecordingMgr->FinishLayerRecording( 0.0f, true );
+
+ if ( params.spew & SPEW_VALUES )
+ {
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+ Assert( pLog->GetNumLayers() == 1 );
+
+ for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) )
+ {
+ float fval = pLog->GetValue( j );
+ Msg( "%d %f\n", j.GetTenthsOfMS(), fval );
+ }
+ }
+ }
+
+ if ( params.spew & SPEW_KEYS )
+ {
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeChannel *channel = channels[ i ];
+ CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() );
+ Assert( pLog );
+ Assert( pLog->GetNumLayers() == 1 );
+
+ int kc = pLog->GetKeyCount();
+ for ( j = 0; j < kc; ++j )
+ {
+ DmeTime_t time = pLog->GetKeyTime( j );
+
+ float fval = pLog->GetValue( time );
+ Msg( "%d %f %f\n", j, time.GetSeconds(), fval );
+ }
+ }
+ }
+
+ // Now test creating a layer and "undo/redo" of the layer
+ if ( params.testundo )
+ {
+ g_pDataModel->Undo();
+ g_pDataModel->Redo();
+ }
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxTestDmeLogLayers )
+{
+ Msg( "Running CDmeTypedLog<float> layering tests...\n" );
+
+#ifdef _DEBUG
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+#endif
+
+ CUtlVector< CDmeChannel * > channels;
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLogLayers>" );
+
+ CreateChannels( NUM_CHANNELS, channels, fileid );
+
+ TestParams_t params;
+ {
+ params.testundo = false;
+ params.usecurves = false;
+ params.defaultcurve = CURVE_DEFAULT;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_DEFAULT, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_DEFAULT, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "One-Layer", channels, params );
+ params.Reset();
+ }
+
+ // Single layer using lerp everywhere
+ // -----------------------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "One-Layer Lerp", channels, params );
+ params.Reset();
+ }
+
+ // Two layers using lerp
+ // ----------------------------
+ // ------------------------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Two-Layer Lerp (contained)", channels, params );
+ params.Reset();
+ }
+
+ // Two layers using CURVE_EASE_IN_TO_EASE_OUT, there should be some disparity
+ // ----------------------------
+ // ------------------------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_EASE_IN_TO_EASE_OUT;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_EASE_IN_TO_EASE_OUT, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_EASE_IN_TO_EASE_OUT, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Two-Layer Ease In/Out (contained)", channels, params );
+ params.Reset();
+ }
+
+ // Two layers using lerp
+ // ----------------------------
+ // ---------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.mintime = DmeTime_t( -20 );
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( -20 ), DmeTime_t( 20 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Two-Layer Lerp (overhang start)", channels, params );
+ params.Reset();
+ }
+
+ // Two layers using lerp
+ // ----------------------------
+ // ------------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.maxtime = DmeTime_t( 120 );
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 80 ), DmeTime_t( 120 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Two-Layer Lerp (overhang end)", channels, params );
+ params.Reset();
+ }
+ // Three layers using lerp
+ // -------------
+ // ----- -----
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.mintime = DmeTime_t( -12 );
+ params.maxtime = DmeTime_t( 115 );
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( -12 ), DmeTime_t( 12 ), DmeTime_t( 4 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 85 ), DmeTime_t( 115 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Three-Layer Lerp (overhang start + end)", channels, params );
+ params.Reset();
+ }
+
+ // Three layers using lerp
+ // -------------
+ // -----
+ // --
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 40 ), DmeTime_t( 60 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Three-Layer Lerp (layer inside layer)", channels, params );
+ params.Reset();
+ }
+
+ // Three layers using lerp
+ // -------------
+ // -----
+ // --
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 70 ), DmeTime_t( 80 ), DmeTime_t( 2 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Three-Layer Lerp (first layer contained, second layer overlapping first at end)", channels, params );
+ params.Reset();
+ }
+
+ // Three layers using lerp
+ // -------------
+ // -----
+ // --
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 15 ), DmeTime_t( 35 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Three-Layer Lerp (first layer contained, second layer overlapping first at start)", channels, params );
+ params.Reset();
+ }
+
+ // Four layers using lerp
+ // ---------------
+ // -----
+ // ----
+ // -------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ // params.spewnontopmostlayers = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f );
+ params.AddLayer( DmeTime_t( 15 ), DmeTime_t( 40 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 60 ), DmeTime_t( 85 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ params.AddLayer( DmeTime_t( 35 ), DmeTime_t( 79 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Four-Layer Lerp (top overlapping end of 1st and start of 2nd layer)", channels, params );
+ params.Reset();
+ }
+
+ // Single layer using lerp everywhere
+ // -----------------------
+ {
+ params.testundo = false;
+ params.usecurves = true;
+ params.spew = 0; //SPEW_VALUES | SPEW_KEYS;
+ params.mintime = DmeTime_t( 0 );
+ params.maxtime = DmeTime_t( 10000 );
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 10000 ), DmeTime_t( 20 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SINE, 100.0f );
+
+ DmeTime_t tHeadPosition = DmeTime_t( 5000 );
+
+ DmeLog_TimeSelection_t ts;
+ ts.m_nTimes[ TS_LEFT_FALLOFF ] = tHeadPosition + DmeTime_t( -987 );
+ ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition;
+ ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 1052 );
+ ts.m_nFalloffInterpolatorTypes[ 0 ] = ts.m_nFalloffInterpolatorTypes[ 1 ] = INTERPOLATE_EASE_INOUT;
+
+ // Resample at 50 msec intervals
+ ts.m_bResampleMode = true;
+ ts.m_nResampleInterval = DmeTime_t( 50 );
+
+ ///params.spew |= SPEW_KEYS | SPEW_VALUES;
+
+ RunTimeSelectionTest( "One-Layer Time Selection at 50, falloff 25, EASE_INOUT interp", channels, params, tHeadPosition, ts, 250 );
+
+ params.purgevalues = false;
+ // params.spew |= SPEW_VALUES;
+
+ // Shift the head and do it all again
+ tHeadPosition = DmeTime_t( 2000 );
+ ts.m_nTimes[ TS_LEFT_FALLOFF ] = DmeTime_t( 1487 );
+ ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition;
+ ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 631 );
+
+ RunTimeSelectionTest( "2nd layer", channels, params, tHeadPosition, ts, 500 );
+ params.Reset();
+ }
+ // Single layer using lerp everywhere
+ // -----------------------
+ {
+ params.testundo = true;
+ params.usecurves = true;
+ params.spew = 0; //SPEW_VALUES | SPEW_KEYS;
+ params.mintime = DmeTime_t( 0 );
+ params.maxtime = DmeTime_t( 1000 );
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 1000 ), DmeTime_t( 20 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 100.0f );
+
+ DmeTime_t tHeadPosition = DmeTime_t( 500 );
+ DmeLog_TimeSelection_t ts;
+ ts.m_nTimes[ TS_LEFT_FALLOFF ] = tHeadPosition + DmeTime_t( -100 );
+ ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition;
+ ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 100 );
+ ts.m_nFalloffInterpolatorTypes[ 0 ] = ts.m_nFalloffInterpolatorTypes[ 1 ] = INTERPOLATE_LINEAR_INTERP;
+
+ // Resample at 50 msec intervals
+ ts.m_bResampleMode = true;
+ ts.m_nResampleInterval = DmeTime_t( 10 );
+
+// params.spew |= SPEW_VALUES;
+
+ RunTimeSelectionTest( "Resetting layer", channels, params, tHeadPosition, ts, 200 );
+
+ params.purgevalues = false;
+ //params.spew |= SPEW_VALUES;
+
+ // Shift the head and do it all again
+ //ts.m_nRelativeFalloffTimes[ 0 ] = 1487 - 2000;
+ //ts.m_nRelativeHoldTimes[ 0 ] = ts.m_nRelativeHoldTimes[ 1 ] = 0;
+ //ts.m_nRelativeFalloffTimes[ 1 ] = 631;
+ //ts.SetHeadPosition( 2000 );
+
+ RunTimeSelectionTest( "2nd layer", channels, params, tHeadPosition, ts, 110 );
+ params.Reset();
+ }
+// g_pDataModel->TraceUndo( true );
+
+ // Test abort undo stuff
+ for ( int i = 0; i < 2; ++i )
+ // Four layers using lerp
+ // ---------------
+ // -----
+ // ----
+ // -------
+ {
+ params.testundo = false;
+ params.testabort = i != 1 ? true : false;
+ params.usecurves = false;
+ // params.spewnontopmostlayers = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 10 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f );
+ params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 6 ), DmeTime_t( 1 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Four-Layer Lerp (top overlapping end of 1st and start of 2nd layer)", channels, params );
+ params.Reset();
+ }
+
+ // g_pDataModel->TraceUndo( false );
+
+
+ //DestroyChannels( channels );
+
+ g_pDataModel->ClearUndo();
+
+ g_pDataModel->RemoveFileId( fileid );
+
+#ifdef _DEBUG
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEquals( nEndingCount, nStartingCount );
+ if ( nEndingCount != nStartingCount )
+ {
+ for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ;
+ hElement != DMELEMENT_HANDLE_INVALID;
+ hElement = g_pDataModel->NextAllocatedElement( hElement ) )
+ {
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return;
+
+ Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() );
+ }
+ }
+#endif
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxTestDmeLogLayersUndo )
+{
+ Msg( "Running CDmeTypedLog<float> layering UNDO tests...\n" );
+
+#ifdef _DEBUG
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+#endif
+
+ CUtlVector< CDmeChannel * > channels;
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLogLayersUndo>" );
+
+ CreateChannels( NUM_CHANNELS, channels, fileid );
+
+ TestParams_t params;
+
+// g_pDataModel->TraceUndo( true );
+
+ // Test abort undo stuff
+ for ( int i = 0; i < 2; ++i )
+ {
+ params.testundo = false;
+ params.testabort = true;
+ params.usecurves = false;
+ // params.spewnontopmostlayers = true;
+ params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP;
+ params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 1000 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f );
+ params.AddLayer( DmeTime_t( 100 ), DmeTime_t( 900 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE );
+ RunLayerTest( "Abort undo", channels, params );
+ params.Reset();
+ }
+
+// g_pDataModel->TraceUndo( false );
+
+ g_pDataModel->ClearUndo();
+ g_pDataModel->RemoveFileId( fileid );
+
+#ifdef _DEBUG
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEquals( nEndingCount, nStartingCount );
+ if ( nEndingCount != nStartingCount )
+ {
+ for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ;
+ hElement != DMELEMENT_HANDLE_INVALID;
+ hElement = g_pDataModel->NextAllocatedElement( hElement ) )
+ {
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return;
+
+ Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() );
+ }
+ }
+#endif
+}
diff --git a/unittests/dmxtest/dmxtest_vcdtodme.cpp b/unittests/dmxtest/dmxtest_vcdtodme.cpp
new file mode 100644
index 0000000..900b310
--- /dev/null
+++ b/unittests/dmxtest/dmxtest_vcdtodme.cpp
@@ -0,0 +1,110 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "movieobjects/movieobjects.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "movieobjects/dmelog.h"
+#include "choreoscene.h"
+#include "choreoevent.h"
+#include "iscenetokenprocessor.h"
+#include "tier1/tokenreader.h"
+#include "characterset.h"
+#include "movieobjects/dmx_to_vcd.h"
+#include "tier3/scenetokenprocessor.h"
+#include "tier2/tier2.h"
+
+char const *vcdtestfile = "dmxtest.vcd";
+
+void RunSceneToDmxTests( CChoreoScene *scene )
+{
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( scene->GetFilename() );
+ CDmeFilmClip *dmx = CreateElement< CDmeFilmClip >( scene->GetFilename(), fileid );
+ Assert( dmx );
+
+ bool success = ConvertSceneToDmx( scene, dmx );
+ Assert( success );
+
+ CChoreoScene *scene2 = new CChoreoScene( NULL );
+ scene2->SetFileName( scene->GetFilename() );
+
+ success = ConvertDmxToScene( dmx, scene2 );
+ Assert( success );
+
+ char sz[ 512 ];
+ Q_StripExtension( scene->GetFilename(), sz, sizeof( sz ) );
+ Q_strncat( sz, "_2.vcd", sizeof( sz ), COPY_ALL_CHARACTERS );
+ scene2->SaveToFile( sz );
+
+ delete scene2;
+
+ g_pDataModel->RemoveFileId( fileid );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxTestVcdToDme )
+{
+ Msg( "Running .vcd (faceposer) to dmx tests\n" );
+
+#ifdef _DEBUG
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+#endif
+
+ CDisableUndoScopeGuard guard;
+
+ g_pDmElementFramework->BeginEdit();
+
+ const char *pFileName = vcdtestfile;
+ char pFullPathName[ MAX_PATH ];
+ char pDir[ MAX_PATH ];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof( pDir ) ) )
+ {
+ V_ComposeFileName( pDir, vcdtestfile, pFullPathName, sizeof( pFullPathName ) );
+ V_RemoveDotSlashes( pFullPathName );
+ pFileName = pFullPathName;
+ }
+
+ CUtlBuffer buf;
+ if ( g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) )
+ {
+ SetTokenProcessorBuffer( (char *)buf.Base() );
+ CChoreoScene *scene = ChoreoLoadScene( pFileName, NULL, GetTokenProcessor(), NULL );
+ if ( scene )
+ {
+ RunSceneToDmxTests( scene );
+ delete scene;
+ }
+ }
+ else
+ {
+ Msg( "Unable to load test file '%s'\n", pFileName );
+ }
+
+ g_pDataModel->ClearUndo();
+
+#ifdef _DEBUG
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEquals( nEndingCount, nStartingCount );
+ if ( nEndingCount != nStartingCount )
+ {
+ for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ;
+ hElement != DMELEMENT_HANDLE_INVALID;
+ hElement = g_pDataModel->NextAllocatedElement( hElement ) )
+ {
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return;
+
+ Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() );
+ }
+ }
+#endif
+} \ No newline at end of file
diff --git a/unittests/dmxtest/dmxtestarray.cpp b/unittests/dmxtest/dmxtestarray.cpp
new file mode 100644
index 0000000..e9c7468
--- /dev/null
+++ b/unittests/dmxtest/dmxtestarray.cpp
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing (testing the Array operations)
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "datamodel/dmehandle.h"
+#include "tier2/tier2.h"
+#include "movieobjects/dmeshape.h"
+
+
+DEFINE_TESTCASE_NOSUITE( DmxArrayTest )
+{
+ Msg( "Running dmx array tests...\n" );
+
+ CDisableUndoScopeGuard sg;
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunArrayTests>" );
+
+ CDmElement *pElement = CreateElement< CDmElement >( "root", fileid );
+
+ CDmElement *pElement2 = CreateElement<CDmElement>( "element1", fileid );
+ Assert( pElement2 );
+ CDmElement *pElement3 = CreateElement<CDmElement>( "element2", fileid );
+ Assert( pElement3 );
+ CDmeShape *pElement4 = CreateElement<CDmeShape>( "shape", fileid );
+ Assert( pElement4 );
+
+ CDmrStringArray stringVec( pElement, "string_array_test", true );
+ stringVec.AddToTail( "string1" );
+ stringVec.AddToTail( "string2" );
+ stringVec.AddToTail( "string3" );
+
+ CDmrArray< float > floatVec( pElement, "float_array_test", true );
+ floatVec.AddToTail( -1.0f );
+ floatVec.AddToTail( 0.0f );
+ floatVec.AddToTail( 1.0f );
+
+ CDmrElementArray< > elementVec( pElement, "element_array_test", true );
+ elementVec.AddToTail( pElement2 );
+ elementVec.AddToTail( pElement3 );
+ elementVec.AddToTail( pElement4 );
+
+ CDmrStringArray stringVec2( pElement, "string_array_test2", true );
+ stringVec2 = stringVec;
+ Shipping_Assert( stringVec2.Count() == 3 );
+
+ CDmrArray< float > floatVec2( pElement, "float_array_test2", true );
+ floatVec2 = floatVec;
+ Shipping_Assert( floatVec2.Count() == 3 );
+
+ CDmrElementArray< > elementVec2( pElement, "element_array_test2", true );
+ elementVec2 = elementVec;
+ Shipping_Assert( elementVec2.Count() == 3 );
+
+ CDmrElementArray< CDmeShape > elementVec3( pElement, "element_array_test3", true );
+ elementVec3 = elementVec2;
+ Shipping_Assert( elementVec3.Count() == 1 );
+
+ CUtlVector<DmElementHandle_t> val;
+ val.AddToTail( pElement2->GetHandle() );
+ val.AddToTail( pElement4->GetHandle() );
+
+ elementVec2 = val;
+ Shipping_Assert( elementVec2.Count() == 2 );
+
+ elementVec3 = val;
+ Shipping_Assert( elementVec3.Count() == 1 );
+
+ g_pDataModel->RemoveFileId( fileid );
+}
diff --git a/unittests/dmxtest/dmxtestdmelog.cpp b/unittests/dmxtest/dmxtestdmelog.cpp
new file mode 100644
index 0000000..3f98678
--- /dev/null
+++ b/unittests/dmxtest/dmxtestdmelog.cpp
@@ -0,0 +1,393 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "movieobjects/movieobjects.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "movieobjects/dmelog.h"
+#include "choreoscene.h"
+#include "choreoevent.h"
+
+struct data_t
+{
+ int tms; // tenths of a millisecond
+ float value;
+ int curvetype;
+};
+
+struct tvpair_t
+{
+ int tms;
+ float expectedvalue;
+};
+
+data_t data[] =
+{
+ { 0, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) },
+ { 10000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) },
+ { 20000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_IN, INTERPOLATE_EASE_OUT ) },
+ { 30000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_INOUT ) },
+ { 40000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_BSPLINE ) },
+ { 50000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_BSPLINE, INTERPOLATE_LINEAR_INTERP ) },
+ { 60000, 1.0f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_KOCHANEK_BARTELS ) },
+ { 70000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS_EARLY ) },
+ { 80000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_EARLY, INTERPOLATE_KOCHANEK_BARTELS_LATE ) },
+ { 90000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_LATE, INTERPOLATE_SIMPLE_CUBIC ) },
+ { 100000, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_SIMPLE_CUBIC, INTERPOLATE_CATMULL_ROM ) },
+ { 110000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM, INTERPOLATE_CATMULL_ROM_NORMALIZE ) },
+ { 120000, 0.125f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZE, INTERPOLATE_EXPONENTIAL_DECAY ) },
+ { 130000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_HOLD ) },
+ { 140000, 0.0625f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_EXPONENTIAL_DECAY ) },
+ { 150000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) },
+};
+
+#define NUM_DEF_TESTS 3
+
+static data_t values1[] =
+{
+ { -1, 0.0f, 0 },
+};
+static data_t values2[] =
+{
+ { 5000, 0.5f, CURVE_DEFAULT },
+ { -1, 0.0f, 0 },
+};
+static data_t values3[] =
+{
+ { 2500, 0.25f, CURVE_DEFAULT },
+ { 7500, 0.75f, CURVE_DEFAULT },
+ { -1, 0.0f, 0 },
+};
+
+static data_t *defaultvaluetest[ NUM_DEF_TESTS ] =
+{
+ values1,
+ values2,
+ values3
+};
+
+#define NUM_TEST_VALUES 3
+
+static tvpair_t expectedvalues1[NUM_TEST_VALUES] =
+{
+ { 0, 0.5f },
+ { 5000, 0.5f },
+ { 10000, 0.5f },
+};
+
+static tvpair_t expectedvalues2[NUM_TEST_VALUES] =
+{
+ { 0, 0.5f },
+ { 5000, 0.5f },
+ { 10000, 0.5f },
+};
+
+static tvpair_t expectedvalues3[NUM_TEST_VALUES] =
+{
+ { 0, 0.25f },
+ { 5000, 0.5f },
+ { 10000, 0.75f },
+};
+
+static tvpair_t *expectedvalues[ NUM_DEF_TESTS ] =
+{
+ expectedvalues1,
+ expectedvalues2,
+ expectedvalues3
+};
+
+void ResetLog( CDmeFloatLog *log, bool useCurveTypes, int startIndex = 0, int endIndex = -1 )
+{
+ log->ClearKeys();
+
+ CDmeCurveInfo *pCurveInfo = useCurveTypes ? log->GetOrCreateCurveInfo() : log->GetCurveInfo();
+ if ( useCurveTypes )
+ {
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) );
+ }
+ else if ( !useCurveTypes && pCurveInfo )
+ {
+ g_pDataModel->DestroyElement( pCurveInfo->GetHandle() );
+ log->SetCurveInfo( NULL );
+ }
+
+ int i;
+ int c;
+
+ c = ARRAYSIZE( data );
+ for ( i = startIndex; i < c; ++i )
+ {
+ log->SetKey( DmeTime_t( data[ i ].tms ), data[ i ].value, useCurveTypes ? data[ i ].curvetype : CURVE_DEFAULT );
+
+ if ( endIndex != -1 && i >= endIndex )
+ break;
+ }
+}
+
+void CompareFloats( float f1, float f2, float tol, char const *fmt, ... )
+{
+ float diff = fabs( f1 - f2 );
+ if ( diff < tol )
+ return;
+
+ char buf[ 256 ];
+ va_list argptr;
+ va_start( argptr, fmt );
+ _vsnprintf( buf, sizeof( buf ) - 1, fmt, argptr );
+ va_end( argptr );
+
+ Msg( buf );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxRunDefaultValueLogTest )
+{
+ Msg( "Running CDmeTypedLog<float> default value (stereo channel w/ value 0.5) tests...\n" );
+ CDisableUndoScopeGuard sg;
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLog>" );
+
+ for ( int i = 0; i < NUM_DEF_TESTS; ++i )
+ {
+ data_t *pdata = defaultvaluetest[ i ];
+ tvpair_t *pexpected = expectedvalues[ i ];
+
+ // Run each test
+
+ CDmeFloatLog *log = CreateElement<CDmeFloatLog>( "curve", fileid );
+ if ( !log )
+ {
+ Msg( "Unable to create CDmeFloatLog object!!!" );
+ continue;
+ }
+
+ log->SetDefaultValue( 0.5f );
+
+ if ( pdata )
+ {
+ // Run the test
+ for ( int j = 0; ; ++j )
+ {
+ if ( pdata[ j ].tms == -1 )
+ break;
+
+ log->SetKey( DmeTime_t( pdata[ j ].tms ), pdata[ j ].value );
+ }
+ }
+
+ // Now compare against expected values
+ for ( int j = 0; j < NUM_TEST_VALUES; ++j )
+ {
+ DmeTime_t t = DmeTime_t( pexpected[ j ].tms );
+ float v = pexpected[ j ].expectedvalue;
+ float logv = log->GetValue( t );
+ Shipping_Assert( v == logv );
+ }
+
+ DestroyElement( log );
+ }
+
+ g_pDataModel->RemoveFileId( fileid );
+}
+
+void RunDmeFloatLogTests( CDmeFloatLog *log )
+{
+ Msg( " Testing general log data...\n" );
+
+ ResetLog( log, false );
+
+ CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.0f ) ), 0.000001f, "log->GetValue( 2.0 ) expected to be 0.5f\n" );
+ CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" );
+ CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" );
+ CompareFloats( 0.5f, log->GetValue( DmeTime_t( 6.5f ) ), 0.000001f, "log->GetValue( 6.5 ) expected to be 0.5f\n" );
+
+ CDmeCurveInfo *pCurveInfo = log->GetOrCreateCurveInfo();
+
+ int idx = log->FindKeyWithinTolerance( DmeTime_t( 6.0f ), DmeTime_t( 0 ) );
+ Shipping_Assert( log->GetKeyTime( idx ) == DmeTime_t( 6.0f ) );
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) );
+
+ float val = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval = log->GetValue( DmeTime_t( 6.25f ) );
+
+ CompareFloats( 0.5f, val, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6500 ) expcted to be 0.5f\n" );
+ CompareFloats( 0.75f, qval, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6250 ) expcted to be 0.75f\n" );
+
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) );
+
+ float val2 = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval2 = log->GetValue( DmeTime_t( 6.25f ) );
+ Shipping_Assert( val2 == val );
+ Shipping_Assert( qval2 != val );
+
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) );
+
+ float val3 = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval3 = log->GetValue( DmeTime_t( 6.25f ) );
+ Shipping_Assert( val3 == val );
+ Shipping_Assert( qval3 != val );
+
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) );
+
+ float val4 = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval4 = log->GetValue( DmeTime_t( 6.25f ) );
+ Shipping_Assert( val4 != val );
+ Shipping_Assert( qval4 != val );
+
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) );
+
+ float val5 = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval5 = log->GetValue( DmeTime_t( 6.25f ) );
+ Shipping_Assert( val5 == val );
+ Shipping_Assert( qval5 != val );
+
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) );
+ log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) );
+ log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) );
+
+ float val6 = log->GetValue( DmeTime_t( 6.5f ) );
+ float qval6 = log->GetValue( DmeTime_t( 6.25f ) );
+ Shipping_Assert( val5 == val6 );
+ Shipping_Assert( qval6 == qval5 );
+
+}
+
+void CompareLogToChoreo( CFlexAnimationTrack *track, CDmeFloatLog *log )
+{
+ // Now run tests
+ for ( DmeTime_t t( 0 ); t < DmeTime_t( 20.0f ); t += DmeTime_t( 0.1f ) )
+ {
+ // Compare values
+ float dmevalue = log->GetValue( t );
+ float choreovalue = track->GetIntensity( t.GetSeconds() );
+
+ CompareFloats( dmevalue, choreovalue, 0.001f, "Time(%f sec) , dme [%f] choreo[%f], diff[%f]\n",
+ t.GetSeconds(),
+ dmevalue,
+ choreovalue,
+ fabs( dmevalue - choreovalue ) );
+ }
+}
+
+void ResetChoreo( CFlexAnimationTrack *track, bool useCurveTypes, int startIndex = 0, int endIndex = -1 )
+{
+ track->Clear();
+
+ int i;
+ int c;
+
+ c = ARRAYSIZE( data );
+ for ( i = startIndex; i < c; ++i )
+ {
+ data_t *e = &data[ i ];
+
+ float t = (float)e->tms / 10000.0f;
+
+ CExpressionSample *sample = track->AddSample( t, e->value );
+ Shipping_Assert( sample );
+ if ( useCurveTypes )
+ {
+ sample->SetCurveType( e->curvetype );
+ }
+
+ if ( endIndex != -1 && i >= endIndex )
+ break;
+ }
+}
+
+void RunDmeChoreoComparisons( CDmeFloatLog *log )
+{
+ Msg( " Testing choreo-style log data...\n" );
+
+ ResetLog( log, true );
+ log->SetRightEdgeTime( DmeTime_t( 15.0f ) );
+
+ CChoreoScene *scene = new CChoreoScene( NULL );
+ CChoreoEvent *event = new CChoreoEvent( scene, CChoreoEvent::FLEXANIMATION, "test" );
+ event->SetStartTime( 0.0f );
+ event->SetEndTime( 15.0f );
+ CFlexAnimationTrack *track = new CFlexAnimationTrack( event );
+ track->SetFlexControllerName( "flextest" );
+ track->SetComboType( false );
+
+ ResetChoreo( track, true );
+
+ Msg( " Comparing default data...\n" );
+
+ CompareLogToChoreo( track, log );
+
+ ResetLog( log, true, 3, 14 );
+ ResetChoreo( track, true, 3, 14 );
+
+ Msg( " Comparing subset of data...\n" );
+
+ CompareLogToChoreo( track, log );
+
+ Msg( " Comparing left/right edge settings...\n" );
+ // Now test right and left edge stuff
+ // Enable left edge stuff
+ track->SetEdgeActive( true, true );
+ track->SetEdgeInfo( true, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ), 0.75f );
+ track->SetEdgeActive( false, true );
+ track->SetEdgeInfo( false, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ), 0.25f );
+
+ // Same settings for log
+ log->SetUseEdgeInfo( true );
+ log->SetDefaultEdgeZeroValue( 0.0f );
+ log->SetEdgeInfo( 0, true, 0.75f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) );
+ log->SetEdgeInfo( 1, true, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ) );
+
+ CompareLogToChoreo( track, log );
+
+ int i;
+ for ( i = 1; i < NUM_INTERPOLATE_TYPES; ++i )
+ {
+ Msg( " Comparing left/right edge settings[ %s ]...\n", Interpolator_NameForInterpolator( i, true ) );
+
+ float val = (float)i / (float)( NUM_INTERPOLATE_TYPES - 1 ) ;
+ // Now test right and left edge stuff with different data
+ track->SetEdgeInfo( true, MAKE_CURVE_TYPE( i, i ), val );
+ track->SetEdgeInfo( false, MAKE_CURVE_TYPE( i, i ), val );
+ log->SetEdgeInfo( 0, true, val, MAKE_CURVE_TYPE( i, i ) );
+ log->SetEdgeInfo( 1, true, val, MAKE_CURVE_TYPE( i, i ) );
+
+ CompareLogToChoreo( track, log );
+ }
+
+ delete event;
+ delete scene;
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxTestDmeLog )
+{
+ Msg( "Running CDmeTypedLog<float> tests...\n" );
+ CDisableUndoScopeGuard sg;
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLog>" );
+
+ CDmeFloatLog *pElement = CreateElement<CDmeFloatLog>( "curve", fileid );
+ if ( !pElement )
+ {
+ Msg( "Unable to create CDmeFloatLog object!!!" );
+ return;
+ }
+
+ // Run tests
+ RunDmeFloatLogTests( pElement );
+
+ RunDmeChoreoComparisons( pElement );
+
+ g_pDataModel->RemoveFileId( fileid );
+} \ No newline at end of file
diff --git a/unittests/dmxtest/dmxtestloader.cpp b/unittests/dmxtest/dmxtestloader.cpp
new file mode 100644
index 0000000..ab3adbf
--- /dev/null
+++ b/unittests/dmxtest/dmxtestloader.cpp
@@ -0,0 +1,211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "dmxloader/dmxloader.h"
+#include "dmxloader/dmxelement.h"
+
+struct TestStruct_t
+{
+ DmObjectId_t m_nId;
+ bool m_bBool;
+ int m_nInt;
+ float m_flFloat;
+ Color m_Color;
+ Vector2D m_Vector2D;
+ Vector m_Vector3D;
+ Vector4D m_Vector4D;
+ QAngle m_Angles;
+ Quaternion m_Quaternion;
+ VMatrix m_Matrix;
+ char m_pStringBuf[256];
+};
+
+BEGIN_DMXELEMENT_UNPACK( TestStruct_t )
+ DMXELEMENT_UNPACK_FIELD( "id_test", NULL, DmObjectId_t, m_nId )
+ DMXELEMENT_UNPACK_FIELD( "bool_test", "1", bool, m_bBool )
+ DMXELEMENT_UNPACK_FIELD( "int_test", "5", int, m_nInt )
+ DMXELEMENT_UNPACK_FIELD( "float_test", "4.0", float, m_flFloat )
+ DMXELEMENT_UNPACK_FIELD( "color_test", "200 200 200 200", Color, m_Color )
+ DMXELEMENT_UNPACK_FIELD( "vector2d_test", "5.0 1.0", Vector2D, m_Vector2D )
+ DMXELEMENT_UNPACK_FIELD( "vector3d_test", "5.0 1.0 -3.0", Vector, m_Vector3D )
+ DMXELEMENT_UNPACK_FIELD( "vector4d_test", "5.0 1.0 -4.0 2.0", Vector4D, m_Vector4D )
+ DMXELEMENT_UNPACK_FIELD( "qangle_test", "5.0 1.0 -3.0", QAngle, m_Angles )
+ DMXELEMENT_UNPACK_FIELD( "quat_test", "5.0 1.0 -4.0 2.0", Quaternion, m_Quaternion )
+ DMXELEMENT_UNPACK_FIELD( "vmatrix_test", NULL, VMatrix, m_Matrix )
+ DMXELEMENT_UNPACK_FIELD_STRING( "string_test", "default", m_pStringBuf )
+END_DMXELEMENT_UNPACK( TestStruct_t, s_TestStructUnpack )
+
+void TestReadFile( CDmxElement *pRoot )
+{
+ VMatrix mattest, mat2test;
+ MatrixBuildRotateZ( mattest, 45 );
+ MatrixBuildRotateZ( mat2test, 30 );
+
+ int i;
+ unsigned char buftest[256];
+ unsigned char buf2test[256];
+ for ( i = 0; i < 256; ++i )
+ {
+ buftest[i] = i;
+ buf2test[i] = 255 - i;
+ }
+
+
+ // Make sure everything was read in ok.
+ AssertEquals( pRoot->GetValue<bool>( "bool_test" ), true );
+ AssertEquals( pRoot->GetValue<int>( "int_test" ), 2 );
+ AssertFloatEquals( pRoot->GetValue<float>( "float_test" ), 3.0f, 1e-3 );
+ const Color& color = pRoot->GetValue<Color>( "color_test" );
+ Shipping_Assert( color.r() == 0 && color.g() == 64 && color.b() == 128 && color.a() == 255 );
+ const Vector2D& vec2D = pRoot->GetValue<Vector2D>( "vector2d_test" );
+ Shipping_Assert( vec2D.x == 1.0f && vec2D.y == -1.0f );
+ const Vector& vec3D = pRoot->GetValue<Vector>( "vector3d_test" );
+ Shipping_Assert( vec3D.x == 1.0f && vec3D.y == -1.0f && vec3D.z == 0.0f );
+ const Vector4D& vec4D = pRoot->GetValue<Vector4D>( "vector4d_test" );
+ Shipping_Assert( vec4D.x == 1.0f && vec4D.y == -1.0f && vec4D.z == 0.0f && vec4D.w == 2.0f );
+ const QAngle& ang = pRoot->GetValue<QAngle>( "qangle_test" );
+ Shipping_Assert( ang.x == 0.0f && ang.y == 90.0f && ang.z == -90.0f );
+ const Quaternion& quat = pRoot->GetValue<Quaternion>( "quat_test" );
+ Shipping_Assert( quat.x == 1.0f && quat.y == -1.0f && quat.z == 0.0f && quat.w == 2.0f );
+
+ const VMatrix& mat = pRoot->GetValue<VMatrix>( "vmatrix_test" );
+ Shipping_Assert( MatricesAreEqual( mat, mattest, 1e-3 ) );
+
+ Shipping_Assert( !Q_stricmp( pRoot->GetValueString( "string_test" ), "test" ) );
+ const CUtlBinaryBlock& blob = pRoot->GetValue<CUtlBinaryBlock>( "binary_test" );
+ Shipping_Assert( blob.Length() == 256 );
+ Shipping_Assert( !memcmp( blob.Get(), buftest, 256 ) );
+
+ CDmxElement *pElement7 = pRoot->GetValue<CDmxElement*>( "element_test" );
+ Shipping_Assert( pElement7 != NULL );
+ CDmxElement *pElement6 = pRoot->GetValue<CDmxElement*>( "shared_element_test" );
+ Shipping_Assert( pElement6 != NULL );
+ const CUtlVector< CDmxElement* >& elementList = pRoot->GetArray<CDmxElement*>( "children" );
+ Shipping_Assert( elementList.Count() == 2 );
+ CDmxElement *pElement2 = elementList[0];
+ CDmxElement *pElement3 = elementList[1];
+ Shipping_Assert( pElement2 != NULL && pElement3 != NULL );
+ Shipping_Assert( pElement7->GetValue<CDmxElement*>( "shared_element_test" ) == pElement6 );
+ const CUtlVector< CDmxElement* >& elementList3 = pElement6->GetArray<CDmxElement*>( "element_array_test" );
+ CDmxElement *pElement4 = elementList3[0];
+ CDmxElement *pElement5 = elementList3[1];
+
+ const CUtlVector< bool > &boolVec = pElement2->GetArray<bool>( "bool_array_test" );
+ Shipping_Assert( boolVec.Count() == 2 && boolVec[0] == false && boolVec[1] == true );
+
+ const CUtlVector< int > &intVec = pElement2->GetArray<int>( "int_array_test" );
+ Shipping_Assert( intVec.Count() == 3 && intVec[0] == 0 && intVec[1] == 1 && intVec[2] == 2 );
+
+ const CUtlVector< float > &floatVec = pElement2->GetArray<float>( "float_array_test" );
+ Shipping_Assert( floatVec.Count() == 3 && floatVec[0] == -1.0f && floatVec[1] == 0.0f && floatVec[2] == 1.0f );
+
+ const CUtlVector< Color > &colorVec = pElement3->GetArray<Color>( "color_array_test" );
+ Shipping_Assert( colorVec.Count() == 3 && colorVec[0].r() == 0 && colorVec[1].r() == 64 && colorVec[2].r() == 128 );
+
+ const CUtlVector< Vector2D > &vec2DVec = pElement3->GetArray<Vector2D>( "vector2d_array_test" );
+ Shipping_Assert( vec2DVec.Count() == 2 && vec2DVec[0].x == -1.0f && vec2DVec[1].x == 1.0f );
+
+ const CUtlVector< Vector > &vec3DVec = pElement3->GetArray<Vector>( "vector3d_array_test" );
+ Shipping_Assert( vec3DVec.Count() == 2 && vec3DVec[0].x == 1.0f && vec3DVec[1].x == 2.0f );
+
+ const CUtlVector< Vector4D > &vec4DVec = pElement4->GetArray<Vector4D>( "vector4d_array_test" );
+ Shipping_Assert( vec4DVec.Count() == 2 && vec4DVec[0].x == 1.0f && vec4DVec[1].x == 2.0f );
+
+ const CUtlVector< QAngle > &angVec = pElement4->GetArray<QAngle>( "qangle_array_test" );
+ Shipping_Assert( angVec.Count() == 2 && angVec[0].x == 1.0f && angVec[1].x == 2.0f );
+
+ const CUtlVector< Quaternion > &quatVec = pElement4->GetArray<Quaternion>( "quat_array_test" );
+ Shipping_Assert( quatVec.Count() == 2 && quatVec[0].x == 1.0f && quatVec[1].x == 2.0f );
+
+ const CUtlVector< VMatrix > &matVec = pElement5->GetArray<VMatrix>( "vmatrix_array_test" );
+ Shipping_Assert( matVec.Count() == 2 );
+ Shipping_Assert( MatricesAreEqual( matVec[0], mattest, 1e-3 ) );
+ Shipping_Assert( MatricesAreEqual( matVec[1], mat2test, 1e-3 ) );
+
+ const CUtlVector< CUtlString > &stringVec = pElement5->GetArray<CUtlString>( "string_array_test" );
+ Shipping_Assert( stringVec.Count() == 3 && !Q_stricmp( stringVec[2], "string3" ) );
+
+ const CUtlVector< CUtlBinaryBlock > &binaryVec = pElement5->GetArray<CUtlBinaryBlock>( "binary_array_test" );
+ Shipping_Assert( binaryVec.Count() == 2 && !memcmp( binaryVec[1].Get(), buf2test, 256 ) );
+
+ const CUtlVector< DmObjectId_t > &idVec = pElement6->GetArray<DmObjectId_t>( "elementid_array_test" );
+ Shipping_Assert( idVec.Count() == 3 );
+
+ TestStruct_t testStruct;
+ pRoot->UnpackIntoStructure( &testStruct, sizeof( testStruct ), s_TestStructUnpack );
+
+ Shipping_Assert( testStruct.m_bBool == true );
+ Shipping_Assert( testStruct.m_nInt == 2 );
+ AssertFloatEquals( testStruct.m_flFloat, 3.0f, 1e-3 );
+ Shipping_Assert( testStruct.m_Color.r() == 0 && testStruct.m_Color.g() == 64 && testStruct.m_Color.b() == 128 && testStruct.m_Color.a() == 255 );
+ Shipping_Assert( testStruct.m_Vector2D.x == 1.0f && testStruct.m_Vector2D.y == -1.0f );
+ Shipping_Assert( testStruct.m_Vector3D.x == 1.0f && testStruct.m_Vector3D.y == -1.0f && testStruct.m_Vector3D.z == 0.0f );
+ Shipping_Assert( testStruct.m_Vector4D.x == 1.0f && testStruct.m_Vector4D.y == -1.0f && testStruct.m_Vector4D.z == 0.0f && testStruct.m_Vector4D.w == 2.0f );
+ Shipping_Assert( testStruct.m_Angles.x == 0.0f && testStruct.m_Angles.y == 90.0f && testStruct.m_Angles.z == -90.0f );
+ Shipping_Assert( testStruct.m_Quaternion.x == 1.0f && testStruct.m_Quaternion.y == -1.0f && testStruct.m_Quaternion.z == 0.0f && testStruct.m_Quaternion.w == 2.0f );
+ Shipping_Assert( MatricesAreEqual( testStruct.m_Matrix, mattest, 1e-3 ) );
+ Shipping_Assert( !Q_stricmp( testStruct.m_pStringBuf, "test" ) );
+
+ pElement6->UnpackIntoStructure( &testStruct, sizeof( testStruct ), s_TestStructUnpack );
+
+ Shipping_Assert( testStruct.m_bBool == true );
+ Shipping_Assert( testStruct.m_nInt == 5 );
+ AssertFloatEquals( testStruct.m_flFloat, 4.0f, 1e-3 );
+ Shipping_Assert( testStruct.m_Color.r() == 200 && testStruct.m_Color.g() == 200 && testStruct.m_Color.b() == 200 && testStruct.m_Color.a() == 200 );
+ Shipping_Assert( testStruct.m_Vector2D.x == 5.0f && testStruct.m_Vector2D.y == 1.0f );
+ Shipping_Assert( testStruct.m_Vector3D.x == 5.0f && testStruct.m_Vector3D.y == 1.0f && testStruct.m_Vector3D.z == -3.0f );
+ Shipping_Assert( testStruct.m_Vector4D.x == 5.0f && testStruct.m_Vector4D.y == 1.0f && testStruct.m_Vector4D.z == -4.0f && testStruct.m_Vector4D.w == 2.0f );
+ Shipping_Assert( testStruct.m_Angles.x == 5.0f && testStruct.m_Angles.y == 1.0f && testStruct.m_Angles.z == -3.0f );
+ Shipping_Assert( testStruct.m_Quaternion.x == 5.0f && testStruct.m_Quaternion.y == 1.0f && testStruct.m_Quaternion.z == -4.0f && testStruct.m_Quaternion.w == 2.0f );
+ Shipping_Assert( !Q_stricmp( testStruct.m_pStringBuf, "default" ) );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxLoaderTest )
+{
+ Msg( "Running dmx loader tests...\n" );
+
+ CDmxElement *pRoot;
+ bool bOk = UnserializeDMX( "dmxtestloader.dmx", NULL, false, &pRoot );
+ Shipping_Assert( bOk );
+ Shipping_Assert( pRoot );
+ if ( pRoot )
+ {
+ TestReadFile( pRoot );
+ CleanupDMX( pRoot );
+ }
+
+ bOk = UnserializeDMX( "dmxtestloadertext.dmx", NULL, true, &pRoot );
+ Shipping_Assert( bOk );
+ Shipping_Assert( pRoot );
+ if ( pRoot )
+ {
+ TestReadFile( pRoot );
+ CleanupDMX( pRoot );
+ }
+
+ // Test serialization
+ bOk = UnserializeDMX( "dmxtestloader.dmx", NULL, false, &pRoot );
+ Shipping_Assert( bOk );
+ Shipping_Assert( pRoot );
+ if ( pRoot )
+ {
+ bOk = SerializeDMX( "dmxtestscratch.dmx", NULL, false, pRoot );
+ Shipping_Assert( bOk );
+ CleanupDMX( pRoot );
+ }
+ CleanupDMX( pRoot );
+
+ bOk = UnserializeDMX( "dmxtestscratch.dmx", NULL, false, &pRoot );
+ Shipping_Assert( bOk );
+ Shipping_Assert( pRoot );
+ if ( pRoot )
+ {
+ TestReadFile( pRoot );
+ CleanupDMX( pRoot );
+ }
+}
diff --git a/unittests/dmxtest/dmxtestnotify.cpp b/unittests/dmxtest/dmxtestnotify.cpp
new file mode 100644
index 0000000..e5a24be
--- /dev/null
+++ b/unittests/dmxtest/dmxtestnotify.cpp
@@ -0,0 +1,111 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing (testing the Notify subsystem)
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "datamodel/dmehandle.h"
+#include "tier2/tier2.h"
+
+class CNotifyTest : public IDmNotify
+{
+public:
+ CNotifyTest() : m_nValueCount(0), m_nTopologyCount(0), m_nArrayCount(0) {}
+
+ virtual void NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
+ {
+ if ( nNotifyFlags & NOTIFY_CHANGE_ATTRIBUTE_VALUE )
+ {
+ m_nValueCount++;
+ }
+ if ( nNotifyFlags & NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE )
+ {
+ m_nArrayCount++;
+ }
+ if ( nNotifyFlags & NOTIFY_CHANGE_TOPOLOGICAL )
+ {
+ m_nTopologyCount++;
+ }
+ }
+
+ int m_nTopologyCount;
+ int m_nArrayCount;
+ int m_nValueCount;
+};
+
+
+DEFINE_TESTCASE_NOSUITE( DmxNotifyTest )
+{
+ Msg( "Running dmx notify tests...\n" );
+
+ CNotifyTest test1, test2;
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunNotifyTests>" );
+
+ g_pDataModel->InstallNotificationCallback( &test1 );
+
+ CDmElement *element = NULL;
+
+ {
+ CUndoScopeGuard guard( NOTIFY_SOURCE_APPLICATION, 0, "create" );
+ element = CreateElement< CDmElement >( "test", fileid );
+ }
+
+ Shipping_Assert( test1.m_nTopologyCount == 1 );
+ Shipping_Assert( test1.m_nArrayCount == 0 );
+
+ g_pDataModel->Undo();
+
+ Shipping_Assert( test1.m_nTopologyCount == 2 );
+ Shipping_Assert( test1.m_nArrayCount == 0 );
+
+ {
+ CNotifyScopeGuard notify( "test1", NOTIFY_SOURCE_APPLICATION, 0, &test2 );
+ CDisableUndoScopeGuard guard;
+ element = CreateElement< CDmElement >( "test", fileid );
+ }
+
+ Shipping_Assert( test1.m_nTopologyCount == 3 );
+ Shipping_Assert( test1.m_nArrayCount == 0 );
+ Shipping_Assert( test2.m_nTopologyCount == 1 );
+ Shipping_Assert( test2.m_nArrayCount == 0 );
+
+ {
+ CDisableUndoScopeGuard guard;
+
+ // NOTE: Nested scope guards referring to the same callback shouldn't double call it
+ CNotifyScopeGuard notify( "test2", NOTIFY_SOURCE_APPLICATION, 0, &test2 );
+ {
+ CNotifyScopeGuard notify( "test3", NOTIFY_SOURCE_APPLICATION, 0, &test2 );
+ DestroyElement( element );
+ }
+ }
+
+ Shipping_Assert( test1.m_nTopologyCount == 4 );
+ Shipping_Assert( test1.m_nArrayCount == 0 );
+ Shipping_Assert( test2.m_nTopologyCount == 2 );
+ Shipping_Assert( test2.m_nArrayCount == 0 );
+
+ {
+ CUndoScopeGuard guard( NOTIFY_SOURCE_APPLICATION, 0, "create" );
+ {
+ element = CreateElement< CDmElement >( "test", fileid );
+ element->SetValue( "test", 1.0f );
+ }
+ guard.Abort();
+ }
+
+ Shipping_Assert( test1.m_nTopologyCount == 4 );
+ Shipping_Assert( test1.m_nArrayCount == 0 );
+ Shipping_Assert( test2.m_nTopologyCount == 2 );
+ Shipping_Assert( test2.m_nArrayCount == 0 );
+
+ g_pDataModel->RemoveNotificationCallback( &test1 );
+ g_pDataModel->RemoveFileId( fileid );
+}
diff --git a/unittests/dmxtest/dmxtestserialization.cpp b/unittests/dmxtest/dmxtestserialization.cpp
new file mode 100644
index 0000000..57bd323
--- /dev/null
+++ b/unittests/dmxtest/dmxtestserialization.cpp
@@ -0,0 +1,760 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "datamodel/dmehandle.h"
+#include "tier2/tier2.h"
+
+bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 );
+bool AssertUnEqualElementHierarchies( DmElementHandle_t src1, DmElementHandle_t src2 )
+{
+ bool equal = AssertEqualElementHierarchies( true, src1, src2 );
+ if ( equal )
+ {
+ AssertMsg( 0, "Hierarchies equal, expecting mismatch\n" );
+ }
+ return !equal;
+}
+
+
+void CreateTestScene( CUtlVector< DmElementHandle_t >& handles, DmFileId_t fileid )
+{
+ DmObjectId_t id;
+ CreateUniqueId( &id );
+
+ VMatrix mat, mat2;
+ MatrixBuildRotateZ( mat, 45 );
+ MatrixBuildRotateZ( mat2, 30 );
+
+ int i;
+ unsigned char buf[256];
+ unsigned char buf2[256];
+ for ( i = 0; i < 256; ++i )
+ {
+ buf[i] = i;
+ buf2[i] = 255 - i;
+ }
+
+ CDmElement *pElement = CreateElement<CDmElement>( "root", fileid );
+ Assert( pElement );
+ CDmElement *pElement2 = CreateElement<CDmElement>( "shared_child", fileid );
+ Assert( pElement2 );
+ CDmElement *pElement3 = CreateElement<CDmElement>( "unique_child", fileid );
+ Assert( pElement3 );
+ CDmElement *pElement4 = CreateElement<CDmElement>( "shared_array_element", fileid );
+ Assert( pElement4 );
+ CDmElement *pElement5 = CreateElement<CDmElement>( "unique_array_element", fileid );
+ Assert( pElement5 );
+ CDmElement *pElement6 = CreateElement<CDmElement>( "shared_element", fileid );
+ Assert( pElement6 );
+ CDmElement *pElement7 = CreateElement<CDmElement>( "unique_element", fileid );
+ Assert( pElement7 );
+
+ g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() );
+
+ handles.AddToTail( pElement->GetHandle() );
+ handles.AddToTail( pElement2->GetHandle() );
+ handles.AddToTail( pElement3->GetHandle() );
+ handles.AddToTail( pElement4->GetHandle() );
+ handles.AddToTail( pElement5->GetHandle() );
+ handles.AddToTail( pElement6->GetHandle() );
+ handles.AddToTail( pElement7->GetHandle() );
+
+ pElement->SetValue( "id_test", id );
+ pElement->SetValue( "bool_test", true );
+ pElement->SetValue( "int_test", 2 );
+ pElement->SetValue( "float_test", 3.0f );
+ pElement->SetValue( "color_test", Color( 0, 64, 128, 255 ) );
+ pElement->SetValue( "vector2d_test", Vector2D( 1.0f, -1.0f ) );
+ pElement->SetValue( "vector3d_test", Vector( 1.0f, -1.0f, 0.0f ) );
+ pElement->SetValue( "vector4d_test", Vector4D( 1.0f, -1.0f, 0.0f, 2.0f ) );
+ pElement->SetValue( "qangle_test", QAngle( 0.0f, 90.0f, -90.0f ) );
+ pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 2.0f ) );
+ pElement->SetValue( "vmatrix_test", mat );
+ pElement->SetValue( "string_test", "test" );
+ pElement->SetValue( "binary_test", buf, 256 );
+
+ // Test DONTSAVE
+// pElement->SetValue( "dontsave", true );
+// CDmAttribute *pAttribute = pElement->GetAttribute( "dontsave" );
+// pAttribute->AddFlag( FATTRIB_DONTSAVE );
+
+ CDmrArray< bool > boolVec( pElement2, "bool_array_test", true );
+ boolVec.AddToTail( false );
+ boolVec.AddToTail( true );
+
+ CDmrArray< int > intVec( pElement2, "int_array_test", true );
+ intVec.AddToTail( 0 );
+ intVec.AddToTail( 1 );
+ intVec.AddToTail( 2 );
+
+ CDmrArray< float > floatVec( pElement2, "float_array_test", true );
+ floatVec.AddToTail( -1.0f );
+ floatVec.AddToTail( 0.0f );
+ floatVec.AddToTail( 1.0f );
+
+ CDmrArray< Color > colorVec( pElement3, "color_array_test", true );
+ colorVec.AddToTail( Color( 0, 0, 0, 255 ) );
+ colorVec.AddToTail( Color( 64, 64, 64, 255 ) );
+ colorVec.AddToTail( Color( 128, 128, 128, 255 ) );
+
+ CDmrArray< Vector2D > vector2DVec( pElement3, "vector2d_array_test", true );
+ vector2DVec.AddToTail( Vector2D( -1.0f, -1.0f ) );
+ vector2DVec.AddToTail( Vector2D( 1.0f, 1.0f ) );
+
+ CDmrArray< Vector > vector3DVec( pElement3, "vector3d_array_test", true );
+ vector3DVec.AddToTail( Vector( 1.0f, -1.0f, 0.0f ) );
+ vector3DVec.AddToTail( Vector( 2.0f, -2.0f, 0.0f ) );
+
+ CDmrArray< Vector4D > vector4DVec( pElement4, "vector4d_array_test", true );
+ vector4DVec.AddToTail( Vector4D( 1.0f, -1.0f, 0.0f, 2.0f ) );
+ vector4DVec.AddToTail( Vector4D( 2.0f, -2.0f, 0.0f, 4.0f ) );
+
+ CDmrArray< QAngle > angleVec( pElement4, "qangle_array_test", true );
+ angleVec.AddToTail( QAngle( 1.0f, -1.0f, 0.0f ) );
+ angleVec.AddToTail( QAngle( 2.0f, -2.0f, 0.0f ) );
+
+ CDmrArray< Quaternion > quatVec( pElement4, "quat_array_test", true );
+ quatVec.AddToTail( Quaternion( 1.0f, -1.0f, 0.0f, 2.0f ) );
+ quatVec.AddToTail( Quaternion( 2.0f, -2.0f, 0.0f, 4.0f ) );
+
+ CDmrArray< VMatrix > matVec( pElement5, "vmatrix_array_test", true );
+ matVec.AddToTail( mat );
+ matVec.AddToTail( mat2 );
+
+ CDmrStringArray stringVec( pElement5, "string_array_test", true );
+ stringVec.AddToTail( "string1" );
+ stringVec.AddToTail( "string2" );
+ stringVec.AddToTail( "string3" );
+
+ CDmrArray< CUtlBinaryBlock > binaryVec( pElement5, "binary_array_test", true );
+ CUtlBinaryBlock block( (const void *)buf, 256 );
+ i = binaryVec.AddToTail( block );
+ CUtlBinaryBlock block2( (const void *)buf2, 256 );
+ i = binaryVec.AddToTail( block2);
+
+ CDmrArray< DmObjectId_t > idVec( pElement6, "elementid_array_test", true );
+ i = idVec.AddToTail( pElement6->GetId() );
+ i = idVec.AddToTail( pElement5->GetId() );
+ i = idVec.AddToTail( pElement4->GetId() );
+
+ CDmrElementArray< > elementVec( pElement6, "element_array_test", true );
+ elementVec.AddToTail( pElement4 );
+ elementVec.AddToTail( pElement5 );
+
+ CDmrElementArray< > elementVec2( pElement7, "element_array_test", true );
+ elementVec2.AddToTail( pElement2 );
+ elementVec2.AddToTail( pElement4 );
+
+ pElement->SetValue( "element_test", pElement7 );
+ pElement->SetValue( "shared_element_test", pElement6 );
+ CDmrElementArray<> children( pElement, "children", true );
+ children.InsertBefore( 0, pElement2 );
+ children.InsertBefore( 1, pElement3 );
+
+ pElement7->SetValue( "shared_element_test", pElement6 );
+ CDmrElementArray<> children2( pElement7, "children", true );
+ children2.InsertBefore( 0, pElement2 );
+}
+
+DmElementHandle_t CreateTestScene( DmFileId_t fileid )
+{
+ CUtlVector< DmElementHandle_t > handles;
+ CreateTestScene( handles, fileid );
+ return handles[ 0 ];
+}
+
+DmElementHandle_t CreateKeyValuesTestScene( DmFileId_t fileid )
+{
+ CDmElement *pElement = CreateElement<CDmElement>( "root", fileid );
+ Assert( pElement );
+ CDmElement *pElement2 = CreateElement<CDmElement>( "shared_child", fileid );
+ Assert( pElement2 );
+ CDmElement *pElement3 = CreateElement<CDmElement>( "unique_child", fileid );
+ Assert( pElement3 );
+ CDmElement *pElement4 = CreateElement<CDmElement>( "shared_array_element", fileid );
+ Assert( pElement4 );
+ CDmElement *pElement5 = CreateElement<CDmElement>( "unique_array_element", fileid );
+ Assert( pElement5 );
+ CDmElement *pElement6 = CreateElement<CDmElement>( "shared_element", fileid );
+ Assert( pElement6 );
+ CDmElement *pElement7 = CreateElement<CDmElement>( "unique_element", fileid );
+ Assert( pElement7 );
+
+ g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() );
+
+ pElement->SetValue( "int_test", 2 );
+ pElement->SetValue( "float_test", 3.0f );
+ pElement->SetValue( "string_test", "test" );
+
+ CDmrElementArray<> eVec( pElement6, "element_array_test", true );
+ eVec.AddToTail( pElement4 );
+ eVec.AddToTail( pElement5 );
+
+ CDmrElementArray<> eVec2( pElement7, "element_array_test", true );
+ eVec2.AddToTail( pElement2 );
+ eVec2.AddToTail( pElement4 );
+
+ pElement->SetValue( "element_test", pElement7 );
+ pElement->SetValue( "shared_element_test", pElement6 );
+ CDmrElementArray<> children( pElement, "children", true );
+ children.InsertBefore( 0, pElement2 );
+ children.InsertBefore( 1, pElement3 );
+
+ pElement7->SetValue( "shared_element_test", pElement6 );
+ CDmrElementArray<> children2( pElement7, "children", true );
+ children2.InsertBefore( 0, pElement2 );
+
+ return pElement->GetHandle();
+}
+
+template< class T >
+bool AssertEqualsTest( bool quiet, const T& src1, const T& src2 )
+{
+ if ( !( src1 == src2 ))
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "Results not equal, expecting equal\n" );
+ }
+ return false;
+ }
+ return true;
+}
+
+template< class T >
+bool AssertEqualsUtlVector( bool quiet, const CUtlVector<T> &src1, const CUtlVector<T> &src2 )
+{
+ bool retval = true;
+ if ( src1.Count() != src2.Count() )
+ {
+ if ( !quiet )
+ {
+ AssertEqualsTest( quiet, src1.Count(), src2.Count() );
+ }
+ retval = false;
+ }
+
+ for ( int i = 0; i < src1.Count(); ++i )
+ {
+ if ( !src2.IsValidIndex( i ) )
+ continue;
+
+ if ( !( src1[i] == src2[i] ) )
+ {
+ if ( !quiet )
+ {
+ AssertEqualsTest( quiet, src1[i], src2[i] );
+ }
+ retval = false;
+ }
+ }
+ return retval;
+}
+
+template< class T >
+bool AssertEqualsUtlVector( bool quiet, CDmAttribute *pAttribute1, CDmAttribute *pAttribute2 )
+{
+ CDmrArray<T> src1( pAttribute1 );
+ CDmrArray<T> src2( pAttribute2 );
+ return AssertEqualsUtlVector( quiet, src1.Get(), src2.Get() );
+}
+
+bool AssertEqualAttributes( bool quiet, CDmAttribute *pAttribute1, CDmAttribute *pAttribute2 )
+{
+ // Always follow ptrs to elements...
+ if ( pAttribute1->GetType() != AT_ELEMENT_ARRAY &&
+ pAttribute1->GetType() != AT_ELEMENT )
+ {
+ // Dirty flag checking here is to avoid infinite recursive loops
+ if ( !pAttribute1->IsFlagSet( FATTRIB_DIRTY ) && !pAttribute2->IsFlagSet( FATTRIB_DIRTY ) )
+ return true;
+ }
+
+ if ( !pAttribute1 )
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "AssertEqualAttributes: pAttribute1 is NULL\n" );
+ }
+ return false;
+ }
+
+
+ if ( !pAttribute2 )
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "AssertEqualAttributes: pAttribute2 is NULL\n" );
+ }
+ return false;
+ }
+
+ bool retval = true;
+
+ pAttribute1->RemoveFlag( FATTRIB_DIRTY );
+ pAttribute2->RemoveFlag( FATTRIB_DIRTY );
+
+ if ( pAttribute1->GetType() != pAttribute2->GetType() )
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "pAttribute1->GetType() == pAttribute2->GetType()" );
+ }
+ retval = false;
+ }
+
+ switch( pAttribute1->GetType() )
+ {
+ case AT_INT:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<int>( ), pAttribute2->GetValue<int>( ) );
+
+ case AT_FLOAT:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<float>( ), pAttribute2->GetValue<float>( ) );
+
+ case AT_BOOL:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<bool>( ), pAttribute2->GetValue<bool>( ) );
+
+ case AT_STRING:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<CUtlString>( ), pAttribute2->GetValue<CUtlString>( ) );
+
+ case AT_VOID:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<CUtlBinaryBlock>( ), pAttribute2->GetValue<CUtlBinaryBlock>( ) );
+
+ case AT_OBJECTID:
+ return true; // skip this for now - two elements can't have the same id, and CreateTestScene currently creates random test_id's each time...
+/*
+ {
+ if ( !g_pDataModel->IsEqual( pAttribute1->GetValue<DmObjectId_t>( ), pAttribute2->GetValue<DmObjectId_t>( ) ) )
+ {
+ if ( !quiet )
+ {
+ Assert( g_pDataModel->IsEqual( pAttribute1->GetValue<DmObjectId_t>( ), pAttribute2->GetValue<DmObjectId_t>( ) ) );
+ }
+ return false;
+ }
+ return true;
+ }
+ break;
+*/
+
+ case AT_COLOR:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<Color>( ), pAttribute2->GetValue<Color>( ) );
+
+ case AT_VECTOR2:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector2D>( ), pAttribute2->GetValue<Vector2D>( ) );
+
+ case AT_VECTOR3:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector>( ), pAttribute2->GetValue<Vector>( ) );
+
+ case AT_VECTOR4:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector4D>( ), pAttribute2->GetValue<Vector4D>( ) );
+
+ case AT_QANGLE:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<QAngle>( ), pAttribute2->GetValue<QAngle>( ) );
+
+ case AT_QUATERNION:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<Quaternion>( ), pAttribute2->GetValue<Quaternion>( ) );
+
+ case AT_VMATRIX:
+ return AssertEqualsTest( quiet, pAttribute1->GetValue<VMatrix>( ), pAttribute2->GetValue<VMatrix>( ) );
+
+ case AT_ELEMENT:
+ return AssertEqualElementHierarchies( quiet, pAttribute1->GetValue<DmElementHandle_t>( ), pAttribute2->GetValue<DmElementHandle_t>( ) );
+
+ case AT_ELEMENT_ARRAY:
+ {
+ const CDmrElementArray< CDmElement > src1( pAttribute1 );
+ const CDmrElementArray< CDmElement > src2( pAttribute2 );
+
+ bool differs = !AssertEqualsTest( quiet, src1.Count(), src2.Count() );
+ bool differs2 = false;
+ for ( int i = 0; i < src1.Count(); ++i )
+ {
+ differs2 |= !AssertEqualElementHierarchies( quiet, src1[ i ]->GetHandle(), src2[ i ]->GetHandle() );
+ }
+
+ return ( !differs && !differs2 );
+ }
+ break;
+
+ case AT_INT_ARRAY:
+ return AssertEqualsUtlVector<int>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_FLOAT_ARRAY:
+ return AssertEqualsUtlVector<float>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_BOOL_ARRAY:
+ return AssertEqualsUtlVector<bool>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_STRING_ARRAY:
+ return AssertEqualsUtlVector<CUtlString>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_VOID_ARRAY:
+ return AssertEqualsUtlVector<CUtlBinaryBlock>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_OBJECTID_ARRAY:
+ {
+ const CDmrArray<DmObjectId_t> src1( pAttribute1 );
+ const CDmrArray<DmObjectId_t> src2( pAttribute2 );
+
+ bool differs = AssertEqualsTest( quiet, src1.Count(), src2.Count() );
+ return differs; // skip this for now - CreateTestScene currently creates random ids each time...
+/*
+ bool differs2 = false;
+ for ( int i = 0; i < src1.Count(); ++i )
+ {
+ if ( !g_pDataModel->IsEqual( src1[i], src2[i] ) )
+ {
+ differs2 = true;
+ if ( !quiet )
+ {
+ Assert( g_pDataModel->IsEqual( src1[i], src2[i] ) );
+ }
+ }
+ }
+
+ return ( !differs && !differs2 );
+*/
+ }
+ break;
+
+ case AT_COLOR_ARRAY:
+ return AssertEqualsUtlVector<Color>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_VECTOR2_ARRAY:
+ return AssertEqualsUtlVector<Vector2D>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_VECTOR3_ARRAY:
+ return AssertEqualsUtlVector<Vector>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_VECTOR4_ARRAY:
+ return AssertEqualsUtlVector<Vector4D>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_QANGLE_ARRAY:
+ return AssertEqualsUtlVector<QAngle>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_QUATERNION_ARRAY:
+ return AssertEqualsUtlVector<Quaternion>( quiet, pAttribute1, pAttribute2 );
+
+ case AT_VMATRIX_ARRAY:
+ return AssertEqualsUtlVector<VMatrix>( quiet, pAttribute1, pAttribute2 );
+ }
+
+ return retval;
+}
+
+bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 )
+{
+ CDmElement *pSrc1 = g_pDataModel->GetElement( src1 );
+ CDmElement *pSrc2 = g_pDataModel->GetElement( src2 );
+
+ if ( !pSrc1 || !pSrc2 )
+ return false;
+
+ // Assume equality
+ bool retval = true;
+
+ if ( pSrc1->GetType() != pSrc2->GetType() )
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "pSrc1->GetType() == pSrc2->GetType()" );
+ }
+ retval = false;
+ }
+
+ if ( Q_strcmp( pSrc1->GetName(), pSrc2->GetName() ) )
+ {
+ if ( !quiet )
+ {
+ AssertMsg2( 0, "Q_strcmp( %s, %s )", pSrc1->GetName(), pSrc2->GetName() );
+ }
+ retval = false;
+ }
+
+ if ( pSrc1->AttributeCount() != pSrc2->AttributeCount() )
+ {
+ if ( !quiet )
+ {
+ AssertMsg( 0, "pSrc1->NumAttributes() == pSrc2->NumAttributes()" );
+ }
+ retval = false;
+ }
+
+ for ( CDmAttribute *pAttribute1 = pSrc1->FirstAttribute(); pAttribute1; pAttribute1 = pAttribute1->NextAttribute() )
+ {
+ const char *pName = pAttribute1->GetName();
+ if ( !pSrc2->HasAttribute( pName ) )
+ {
+ if ( !quiet )
+ {
+ AssertMsg1( 0, "pSrc2->HasAttribute( %s ) failed\n", pName );
+ }
+ retval = false;
+ }
+ else
+ {
+ CDmAttribute *pAttribute2 = pSrc2->GetAttribute( pName );
+
+ bool differs = !AssertEqualAttributes( quiet, pAttribute1, pAttribute2 );
+ if ( differs )
+ {
+ retval = false;
+ }
+ }
+ }
+
+ return retval;
+}
+
+void TestDeleteOldCR( const char *pSerializationType )
+{
+ DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestDeleteOldCR>" );
+ DmElementHandle_t hRoot = CreateTestScene( testFileID );
+
+ int nTestElements = g_pDataModel->NumElementsInFile( testFileID );
+
+ const char *pFileName = "DeleteOld.dmx";
+ CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) );
+ bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot );
+ Shipping_Assert( bOk );
+
+ CDmElement *pReadInRoot = NULL;
+ DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_OLD );
+ Shipping_Assert( readFileID != DMFILEID_INVALID );
+
+ if ( pReadInRoot )
+ {
+ Shipping_Assert( pReadInRoot->GetHandle() == hRoot );
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pReadInRoot );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == 0 );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ CDmeHandle< CDmElement > rootHandle( hRoot ); // keeps a reference to root around, even after the file is unloaded
+ g_pDataModel->UnloadFile( readFileID );
+
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == NULL );
+
+ DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_OLD );
+ Shipping_Assert( readFileID2 == readFileID );
+
+ Shipping_Assert( pReadInRoot->GetHandle() == hRoot );
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pReadInRoot );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == 0 );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ g_pDataModel->RemoveFileId( readFileID );
+ }
+ else
+ {
+ Msg( "Failed to load %s back from disk!!!", pFileName );
+ }
+
+ g_pDataModel->RemoveFileId( testFileID );
+}
+
+void TestDeleteNewCR( const char *pSerializationType )
+{
+ DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestDeleteNewCR>" );
+ DmElementHandle_t hRoot = CreateTestScene( testFileID );
+
+ int nTestElements = g_pDataModel->NumElementsInFile( testFileID );
+
+ const char *pFileName = "DeleteNew.dmx";
+ CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) );
+ bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot );
+ Shipping_Assert( bOk );
+
+ CDmElement *pReadInRoot = NULL;
+ DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_NEW );
+ Shipping_Assert( readFileID != DMFILEID_INVALID );
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot == pRoot ); // RestoreFromFile now returns the old element when the new root is deleted
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+
+ g_pDataModel->UnloadFile( readFileID );
+
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+
+ DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_NEW );
+ Shipping_Assert( readFileID2 == readFileID );
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot == pRoot ); // RestoreFromFile now returns the old element when the new root is deleted
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+
+ g_pDataModel->RemoveFileId( readFileID );
+
+ g_pDataModel->RemoveFileId( testFileID );
+}
+
+void TestCopyNewCR( const char *pSerializationType )
+{
+ DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestCopyNewCR>" );
+ DmElementHandle_t hRoot = CreateTestScene( testFileID );
+
+ int nTestElements = g_pDataModel->NumElementsInFile( testFileID );
+
+ const char *pFileName = "CopyNew.dmx";
+ CDmElement *pRoot = g_pDataModel->GetElement( hRoot );
+ bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot );
+ Shipping_Assert( bOk );
+
+ CDmElement *pReadInRoot = NULL;
+ DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_COPY_NEW );
+ Shipping_Assert( readFileID != DMFILEID_INVALID );
+
+ if ( pReadInRoot )
+ {
+ DmElementHandle_t hReadInRoot = pReadInRoot->GetHandle();
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot->GetHandle() != hRoot );
+ Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ g_pDataModel->UnloadFile( readFileID );
+
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+ Shipping_Assert( g_pDataModel->GetElement( hReadInRoot ) == NULL );
+
+ DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_COPY_NEW );
+ Shipping_Assert( readFileID2 == readFileID );
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot->GetHandle() != hRoot );
+ Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ g_pDataModel->RemoveFileId( readFileID );
+ }
+ else
+ {
+ Msg( "Failed to load %s back from disk!!!", pFileName );
+ }
+
+ g_pDataModel->RemoveFileId( testFileID );
+}
+
+void TestForceCopyCR( const char *pSerializationType )
+{
+ DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestForceCopyCR>" );
+ DmElementHandle_t hRoot = CreateTestScene( testFileID );
+
+ int nTestElements = g_pDataModel->NumElementsInFile( testFileID );
+
+ const char *pFileName = "ForceCopy.dmx";
+ CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) );
+ bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot );
+ Shipping_Assert( bOk );
+
+ CDmElement *pReadInRoot = NULL;
+ DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY );
+ Shipping_Assert( readFileID != DMFILEID_INVALID );
+
+ if ( pReadInRoot )
+ {
+ DmElementHandle_t hReadInRoot = pReadInRoot->GetHandle();
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot->GetHandle() != hRoot );
+ Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ g_pDataModel->UnloadFile( readFileID );
+
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 );
+ Shipping_Assert( g_pDataModel->GetElement( hReadInRoot ) == NULL );
+
+ DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY );
+ Shipping_Assert( readFileID2 == readFileID );
+
+ Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot );
+ Shipping_Assert( pRoot->GetHandle() == hRoot );
+ Shipping_Assert( pReadInRoot->GetHandle() != hRoot );
+ Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements );
+ Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements );
+
+ g_pDataModel->RemoveFileId( readFileID );
+ }
+ else
+ {
+ Msg( "Failed to load %s back from disk!!!", pFileName );
+ }
+
+ g_pDataModel->RemoveFileId( testFileID );
+}
+
+void TestConflictResolution( const char *pSerializationType )
+{
+ TestDeleteOldCR( pSerializationType );
+ TestDeleteNewCR( pSerializationType );
+ TestCopyNewCR( pSerializationType );
+ TestForceCopyCR( pSerializationType );
+}
+
+void TestSerializationMethod( const char *pSerializationType )
+{
+ DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<CreateTestScene>" );
+ DmElementHandle_t hRoot = CreateTestScene( testFileID );
+
+ const char *pFileName = "dmxtest.dmx";
+ CDmElement *pRoot = static_cast<CDmElement*>(g_pDataModel->GetElement(hRoot));
+ bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot );
+ Shipping_Assert( bOk );
+
+ CDmElement *pReadInRoot = NULL;
+ DmFileId_t dmxFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY );
+ Shipping_Assert( dmxFileID != DMFILEID_INVALID );
+
+ if ( pReadInRoot )
+ {
+ AssertEqualElementHierarchies( false, hRoot, pReadInRoot->GetHandle() );
+ g_pDataModel->RemoveFileId( dmxFileID );
+ }
+ else
+ {
+ Msg( "Failed to load dmxtest.dmx back from disk!!!" );
+ }
+
+ g_pDataModel->RemoveFileId( testFileID );
+
+ TestConflictResolution( pSerializationType );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxSerializationTest )
+{
+ Msg( "Running dmx serialization tests...\n" );
+ CDisableUndoScopeGuard sg;
+
+ TestSerializationMethod( "keyvalues2" );
+ TestSerializationMethod( "keyvalues2_flat" );
+ TestSerializationMethod( "xml" );
+ TestSerializationMethod( "xml_flat" );
+ TestSerializationMethod( "binary" );
+
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEqualsTest( false, 0, nEndingCount );
+}
diff --git a/unittests/dmxtest/dmxtestundoredo.cpp b/unittests/dmxtest/dmxtestundoredo.cpp
new file mode 100644
index 0000000..991132c
--- /dev/null
+++ b/unittests/dmxtest/dmxtestundoredo.cpp
@@ -0,0 +1,1096 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/idatamodel.h"
+#include "movieobjects/movieobjects.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+
+
+// From dmxtestserialization.cpp
+
+bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 );
+bool AssertUnEqualElementHierarchies( DmElementHandle_t src1, DmElementHandle_t src2 );
+
+void CreateTestScene( CUtlVector< DmElementHandle_t >& handles, DmFileId_t fileid );
+
+bool AssertEqualElementHierarchies( bool quiet, CDmElement *src1, CDmElement *src2 )
+{
+ DmElementHandle_t h1 = src1->GetHandle();
+ DmElementHandle_t h2 = src2->GetHandle();
+
+ return AssertEqualElementHierarchies( quiet, h1, h2 );
+}
+
+bool AssertUnEqualElementHierarchies( CDmElement *src1, CDmElement *src2 )
+{
+ return AssertUnEqualElementHierarchies( src1->GetHandle(), src2->GetHandle() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void DescribeUndo()
+{
+ CUtlVector< UndoInfo_t > list;
+ g_pDataModel->GetUndoInfo( list );
+
+ for ( int i = list.Count() - 1; i >= 0; --i )
+ {
+ UndoInfo_t& entry = list[ i ];
+ if ( entry.terminator )
+ {
+ Msg( "[ '%s' ] -- %i operations\n", entry.undo, entry.numoperations );
+ }
+
+ Msg( " +[ '%s' ]\n", entry.desc );
+ }
+}
+
+template< class T >
+void RunSingleSetAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ pElement->SetValue( attribute, newVal );
+ }
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+void RunSingleSetAttributeUndoTestBinary( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const void *data, size_t size )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ pElement->SetValue( attribute, data, size );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleSetAttributeUndoTestArray( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.Set( slot, newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template<> void RunSingleSetAttributeUndoTestArray<DmElementHandle_t>( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const DmElementHandle_t& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrElementArray<> array( pElement, attribute );
+ array.SetHandle( slot, newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayAddToHead( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.InsertBefore( 0, newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayAddToTail( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.AddToTail( newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayInsertBefore( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.InsertBefore( slot, newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+
+template< class T >
+void RunSingleAttributeUndoTestArrayFastRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, T typeDeducer )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.FastRemove( slot );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+
+void RunSingleAttributeUndoTestElementArrayRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrElementArray<> array( pElement, attribute );
+ array.Remove( slot );
+ }
+
+ DescribeUndo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, T typeDeducer )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray<T> array( pElement, attribute );
+ array.Remove( slot );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayRemoveMultiple( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, int count, T typeDeducer )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ array.RemoveMultiple( slot, count );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayRemoveAll( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, bool purge )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray< T > array( pElement, attribute );
+ if( purge )
+ {
+ array.Purge();
+ }
+ else
+ {
+ array.RemoveAll();
+ }
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAttributeUndoTestArrayCopyFrom( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const CUtlVector<T>& list )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CDmrArray<T> array( pElement, attribute );
+ array.CopyArray( list.Base(), list.Count() );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+
+void RunSingleSetAttributeUndoTestArrayBinary( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const void *data, size_t size )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ CUtlBinaryBlock block( (const void *)data, size );
+ CDmrArray< CUtlBinaryBlock > array( pElement, attribute );
+ array.Set( slot, block );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleSetAttributeUndoTestArrayWhole( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, CUtlVector< T >& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ IDmAttributeArray< T > *pArray = static_cast< IDmAttributeArray< T > * >( pElement->GetAttribute( "attribute" ) );
+ pArray->SetValue( newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+template< class T >
+void RunSingleAddAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal )
+{
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ pElement->SetValue( attribute, newVal );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+void RunSingleRemoveAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute )
+{
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ {
+ CUndoScopeGuard guard( attribute, attribute );
+ pElement->RemoveAttribute( attribute );
+ }
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+
+ AssertUnEqualElementHierarchies( pTest, pOriginal );
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+}
+
+
+void RunUndoTestsArray( CUtlVector< DmElementHandle_t >& handles, CDmElement *pTest, CDmElement *pOriginal )
+{
+ DmObjectId_t id;
+ CreateUniqueId( &id );
+
+ VMatrix mat, mat2;
+ MatrixBuildRotateZ( mat, 55 );
+ MatrixBuildRotateZ( mat2, -55 );
+ int i;
+ unsigned char buf[256];
+ unsigned char buf2[256];
+ for ( i = 0; i < 256; ++i )
+ {
+ buf[i] = i+55500;
+ buf2[i] = 55000 + 255 - i;
+ }
+
+ Assert( handles.Count() == 7 );
+
+ //CDmElement *pElement = pTest;
+ CDmElement *pElement2 = GetElement< CDmElement >( handles[ 1 ] );
+ CDmElement *pElement3 = GetElement< CDmElement >( handles[ 2 ] );
+ CDmElement *pElement4 = GetElement< CDmElement >( handles[ 3 ] );
+ CDmElement *pElement5 = GetElement< CDmElement >( handles[ 4 ] );
+ CDmElement *pElement6 = GetElement< CDmElement >( handles[ 5 ] );
+ CDmElement *pElement7 = GetElement< CDmElement >( handles[ 6 ] );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "int_array_test", 0, 55 );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "float_array_test", 0, 55.0f );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "bool_array_test", 0, true );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "color_array_test", 0, Color( 55, 55, 55, 55 ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector2d_array_test", 0, Vector2D( 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector3d_array_test", 0, Vector( 55.0f, -55.0f, 55.0f ) );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "vector4d_array_test", 0, Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "qangle_array_test", 0, QAngle( 55.0f, 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "quat_array_test", 0, Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "vmatrix_array_test", 0, mat );
+ CUtlString newString( "55test" );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "string_array_test", 0, newString );
+ RunSingleSetAttributeUndoTestArrayBinary( pTest, pOriginal, pElement5, "binary_array_test", 0, buf, 256 );
+
+// RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "elementid_array_test", 0, id );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "element_array_test", 0, pElement2->GetHandle() );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement7, "element_array_test", 0, pElement5->GetHandle() );
+
+ {
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "int_array_test", 0, 55 );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "float_array_test", 0, 55.0f );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "bool_array_test", 0, true );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "color_array_test", 0, Color( 55, 55, 55, 55 ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector2d_array_test", 0, Vector2D( 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector3d_array_test", 0, Vector( 55.0f, -55.0f, 55.0f ) );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "vector4d_array_test", 0, Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "qangle_array_test", 0, QAngle( 55.0f, 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "quat_array_test", 0, Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "vmatrix_array_test", 0, mat );
+ CUtlString newString( "55test" );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "string_array_test", 0, newString );
+ RunSingleSetAttributeUndoTestArrayBinary( pTest, pOriginal, pElement5, "binary_array_test", 0, buf, 256 );
+
+// RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "elementid_array_test", 0, id );
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "element_array_test", 0, pElement2->GetHandle() );
+
+ RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement7, "element_array_test", 0, pElement5->GetHandle() );
+ }
+
+ RunSingleAttributeUndoTestArrayAddToHead( pTest, pOriginal, pElement2, "int_array_test", 55 );
+ RunSingleAttributeUndoTestArrayAddToTail( pTest, pOriginal, pElement2, "int_array_test", 55 );
+ RunSingleAttributeUndoTestArrayInsertBefore( pTest, pOriginal, pElement2, "int_array_test", 0, 55 );
+ RunSingleAttributeUndoTestArrayFastRemove( pTest, pOriginal, pElement2, "int_array_test", 0, (int)0 );
+ RunSingleAttributeUndoTestArrayRemove( pTest, pOriginal, pElement2, "int_array_test", 0, (int)0 );
+ RunSingleAttributeUndoTestArrayRemoveMultiple( pTest, pOriginal, pElement2, "int_array_test", 0, 2, (int)0 );
+ RunSingleAttributeUndoTestElementArrayRemove( pTest, pOriginal, pElement7, "children", 0 );
+
+ // RemoveAll
+ RunSingleAttributeUndoTestArrayRemoveAll<int>( pTest, pOriginal, pElement2, "int_array_test", false );
+ // Purge
+ RunSingleAttributeUndoTestArrayRemoveAll<int>( pTest, pOriginal, pElement2, "int_array_test", true );
+
+ CUtlVector< int > foo;
+ foo.AddToTail( 55 );
+ foo.AddToTail( 56 );
+ foo.AddToTail( 57 );
+ foo.AddToTail( 58 );
+ foo.AddToTail( 59 );
+
+ RunSingleAttributeUndoTestArrayCopyFrom( pTest, pOriginal, pElement2, "int_array_test", foo );
+}
+
+void RunUndoTests( CUtlVector< DmElementHandle_t >& handles, CDmElement *pTest, CDmElement *pOriginal )
+{
+ DmObjectId_t id;
+ CreateUniqueId( &id );
+
+ VMatrix mat, mat2;
+ MatrixBuildRotateZ( mat, 55 );
+ MatrixBuildRotateZ( mat2, -55 );
+ int i;
+ unsigned char buf[256];
+ unsigned char buf2[256];
+ for ( i = 0; i < 256; ++i )
+ {
+ buf[i] = i+100;
+ buf2[i] = 127 + 255 - i;
+ }
+
+ Assert( handles.Count() == 7 );
+ CDmElement *pElement = pTest;
+ CDmElement *pElement2 = GetElement<CDmElement>( handles[ 1 ] );
+
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "element_test", (CDmElement *)NULL );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "int_test", 55 );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "float_test", 55.0f );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "bool_test", false );
+// RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "id_test", id );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "string_test", "55test" );
+ RunSingleSetAttributeUndoTestBinary( pTest, pOriginal, pElement, "binary_test", buf, 256 );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "color_test", Color( 55, 55, 55, 55 ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector2d_test", Vector2D( 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) );
+ RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vmatrix_test", mat );
+
+ // Now run a single test with a bunch of operations occurring at the same time
+ {
+ {
+ CUndoScopeGuard guard( "biggish", "smallish" );
+ pElement->SetValue( "bool_test", false );
+ pElement->SetValue( "int_test", 55 );
+ pElement->SetValue( "float_test", 55.0f );
+ pElement->SetValue( "color_test", Color( 55, 55, 55, 55 ) );
+ pElement->SetValue( "vector2d_test", Vector2D( 55.0f, -55.0f ) );
+ pElement->SetValue( "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) );
+ pElement->SetValue( "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) );
+ pElement->SetValue( "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) );
+ pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) );
+ pElement->SetValue( "vmatrix_test", mat );
+ pElement->SetValue( "string_test", "55test" );
+ pElement->SetValue( "binary_test", buf, 256 );
+ }
+
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+ }
+
+ // Now run a test with multiple items in the stack
+ {
+ // Push 1
+ {
+ CUndoScopeGuard guard( "biggish1", "smallish1" );
+ pElement->SetValue( "bool_test", false );
+ pElement->SetValue( "int_test", 55 );
+ pElement->SetValue( "float_test", 55.0f );
+ pElement->SetValue( "color_test", Color( 55, 55, 55, 55 ) );
+ }
+ // Push 2
+ {
+ CUndoScopeGuard guard( "biggish2", "smallish2" );
+ pElement->SetValue( "vector2d_test", Vector2D( 55.0f, -55.0f ) );
+ pElement->SetValue( "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) );
+ pElement->SetValue( "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) );
+ pElement->SetValue( "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) );
+ pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) );
+ }
+ // Push 3
+ {
+ CUndoScopeGuard guard( "biggish3", "smallish3" );
+ pElement->SetValue( "vmatrix_test", mat );
+ pElement->SetValue( "string_test", "55test" );
+ pElement->SetValue( "binary_test", buf, 256 );
+ }
+ // Tests
+ {
+ g_pDataModel->Undo();
+ g_pDataModel->Undo();
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ g_pDataModel->Redo();
+ g_pDataModel->Redo();
+ g_pDataModel->Redo();
+
+ g_pDataModel->Undo();
+ g_pDataModel->Undo();
+ g_pDataModel->Undo();
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+ }
+ }
+
+ // Now run "add" tests on a subset -- add adds an attribute where one didn't already exist
+ RunSingleAddAttributeUndoTest( pTest, pOriginal, pElement2, "int_test_added", 55 );
+
+ // Now run "remove" tests on a subset of attributes -- removes an existing attribute by name
+ RunSingleRemoveAttributeUndoTest( pTest, pOriginal, pTest, "int_test" );
+}
+
+#include "datamodel/dmehandle.h"
+
+void RunUndoTestCreateElement()
+{
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunUndoTestCreateElement>" );
+
+ CDmElement *element = NULL;
+ {
+ CUndoScopeGuard guard( "create" );
+ element = CreateElement< CDmElement >( "test", fileid );
+ }
+
+ CDmeHandle< CDmElement > hChannel;
+
+ hChannel = element;
+
+ Assert( hChannel.Get() );
+
+ g_pDataModel->Undo();
+
+ Assert( !hChannel.Get() );
+
+ g_pDataModel->Redo();
+
+ Assert( hChannel.Get() );
+
+ g_pDataModel->Undo(); // It's on the redo stack, but marked as kill and all ptrs are NULL to it
+
+ Assert( !hChannel.Get() );
+
+ g_pDataModel->ClearUndo();
+ g_pDataModel->ClearRedo();
+
+ g_pDataModel->RemoveFileId( fileid );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxUndoRedoTest )
+{
+ Msg( "Running undo tests...\n" );
+
+#ifdef _DEBUG
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+#endif
+
+ CUndoScopeGuard sg( "Create Test Scenes" );
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxUndoRedoTest>" );
+ CUtlVector< DmElementHandle_t > handles;
+ CreateTestScene( handles, fileid );
+ DmElementHandle_t hOriginal = handles[ 0 ];
+ handles.RemoveAll();
+ CreateTestScene( handles, fileid );
+ DmElementHandle_t hTest = handles[ 0 ];
+
+ sg.Release();
+
+ CDmElement *pOriginal = static_cast<CDmElement*>( g_pDataModel->GetElement( hOriginal ) );
+ CDmElement *pTest = static_cast<CDmElement*>(g_pDataModel->GetElement( hTest ) );
+
+ AssertEqualElementHierarchies( false, pTest, pOriginal );
+
+ RunUndoTests( handles, pTest, pOriginal );
+ RunUndoTestsArray( handles, pTest, pOriginal );
+ RunUndoTestCreateElement();
+
+ g_pDataModel->ClearUndo();
+
+ g_pDataModel->RemoveFileId( fileid );
+
+#ifdef _DEBUG
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEquals( nEndingCount, nStartingCount );
+ if ( nEndingCount != nStartingCount )
+ {
+ for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ;
+ hElement != DMELEMENT_HANDLE_INVALID;
+ hElement = g_pDataModel->NextAllocatedElement( hElement ) )
+ {
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return;
+
+ Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() );
+ }
+ }
+#endif
+}
+
+#include "datamodel/dmelementfactoryhelper.h"
+
+//-----------------------------------------------------------------------------
+// CDmeExternal - element class used for testing external attributes
+//-----------------------------------------------------------------------------
+
+class CDmeExternal : public CDmElement
+{
+ DEFINE_ELEMENT( CDmeExternal, CDmElement );
+
+public:
+ CDmaVar< float > m_External;
+};
+
+IMPLEMENT_ELEMENT_FACTORY( DmeExternal, CDmeExternal );
+
+void CDmeExternal::OnConstruction()
+{
+ m_External .InitAndSet( this, "external", 99.9f );
+}
+
+void CDmeExternal::OnDestruction()
+{
+}
+
+USING_ELEMENT_FACTORY( DmeExternal );
+
+
+DEFINE_TESTCASE_NOSUITE( DmxExternalAttributeTest )
+{
+ Msg( "Running external attribute test...\n" );
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxExternalAttributeTest>" );
+
+ CDmeExternal *pOriginal = 0;
+
+#ifdef _DEBUG
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+#endif
+
+ {
+ CUndoScopeGuard guard( "External" );
+ pOriginal = CreateElement<CDmeExternal>( "foo", fileid );
+
+ for ( int m = 0; m < 1; ++m )
+ {
+ CDmeExternal *discard = CreateElement<CDmeExternal>( "foo", fileid );
+ g_pDataModel->DestroyElement( discard->GetHandle() );
+ }
+ }
+
+ // Now mess with vars
+
+ g_pDataModel->Undo();
+ g_pDataModel->Redo();
+
+ {
+ CUndoScopeGuard guard( "External2" );
+ pOriginal->m_External.Set( 100.0f );
+ }
+
+ g_pDataModel->ClearUndo();
+
+ g_pDataModel->RemoveFileId( fileid );
+
+#ifdef _DEBUG
+ int nEndingCount = g_pDataModel->GetAllocatedElementCount();
+ AssertEquals( nEndingCount, nStartingCount );
+ if ( nEndingCount != nStartingCount )
+ {
+ for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ;
+ hElement != DMELEMENT_HANDLE_INVALID;
+ hElement = g_pDataModel->NextAllocatedElement( hElement ) )
+ {
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return;
+
+ Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() );
+ }
+ }
+#endif
+}
+
+void Assert_InvalidAndNULL( CDmeHandle< CDmElement > &hElement )
+{
+ Shipping_Assert( !hElement.Get() );
+ if ( !hElement.Get() )
+ {
+ g_pDataModel->MarkHandleValid( hElement.GetHandle() );
+ Shipping_Assert( !hElement.Get() );
+ if ( hElement.Get() )
+ {
+ g_pDataModel->MarkHandleInvalid( hElement.GetHandle() );
+ }
+ }
+}
+
+void Assert_InvalidButNonNULL( CDmeHandle< CDmElement > &hElement )
+{
+ Shipping_Assert( !hElement.Get() );
+ if ( !hElement.Get() )
+ {
+ g_pDataModel->MarkHandleValid( hElement.GetHandle() );
+ Shipping_Assert( hElement.Get() );
+ if ( hElement.Get() )
+ {
+ g_pDataModel->MarkHandleInvalid( hElement.GetHandle() );
+ }
+ }
+}
+
+void Assert_ValidAndNonNULL( CDmeHandle< CDmElement > &hElement )
+{
+ Shipping_Assert( hElement.Get() );
+}
+
+DEFINE_TESTCASE_NOSUITE( DmxReferenceCountingTest )
+{
+ Msg( "Running external attribute test...\n" );
+
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxReferenceCountingTest>" );
+
+ CDmeHandle< CDmElement > hRoot;
+ CDmeHandle< CDmElement > hChild1, hChild2, hChild3, hChild4, hChild5;
+ CDmeHandle< CDmElement > hGrandChild1, hGrandChild2, hGrandChild3, hGrandChild4, hGrandChild5, hGrandChild6, hGrandChild7;
+ CDmeCountedHandle hStrong6, hStrong7;
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nStartingCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nStartingCount == 0 );
+
+ {
+ CUndoScopeGuard guard( "Create RefCountTest Elements" );
+
+ hRoot = CreateElement<CDmElement>( "root", fileid );
+ CDmrElementArray<> arrayRoot( hRoot, "children", true );
+ g_pDataModel->SetFileRoot( fileid, hRoot->GetHandle() );
+
+ hChild1 = CreateElement<CDmElement>( "child1", fileid );
+ CDmrElementArray<> arrayChild1( hChild1, "children", true );
+ arrayRoot.AddToTail( hChild1.Get() );
+
+ hChild2 = CreateElement<CDmElement>( "child2", fileid );
+ hChild2->AddAttributeElement<CDmElement>( "child" );
+ arrayRoot.AddToTail( hChild2.Get() );
+
+ hChild3 = CreateElement<CDmElement>( "child3", fileid );
+ hChild3->AddAttributeElement<CDmElement>( "child" );
+ arrayRoot.AddToTail( hChild3.Get() );
+
+ hChild4 = CreateElement<CDmElement>( "child4", fileid );
+ hChild4->AddAttributeElement<CDmElement>( "child" );
+ arrayRoot.AddToTail( hChild4.Get() );
+
+ hChild5 = CreateElement<CDmElement>( "child5", fileid );
+ CDmrElementArray<> arrayChild5( hChild5, "children", true );
+ arrayRoot.AddToTail( hChild5.Get() );
+
+ hGrandChild1 = CreateElement<CDmElement>( "grandchild1", fileid );
+ arrayChild1.AddToTail( hGrandChild1.Get() );
+
+ hGrandChild2 = CreateElement<CDmElement>( "grandchild2", fileid );
+ arrayChild1.AddToTail( hGrandChild2.Get() );
+ hChild2->SetValue( "child", hGrandChild2.GetHandle() );
+
+ hGrandChild3 = CreateElement<CDmElement>( "grandchild3", fileid );
+ arrayChild1.AddToTail( hGrandChild3.Get() );
+ hChild3->SetValue( "child", hGrandChild3.GetHandle() );
+
+ hGrandChild4 = CreateElement<CDmElement>( "grandchild4", fileid );
+ arrayChild1.AddToTail( hGrandChild4.Get() );
+ hChild4->SetValue( "child", hGrandChild4.GetHandle() );
+
+ hGrandChild5 = CreateElement<CDmElement>( "grandchild5", fileid );
+ arrayChild1.AddToTail( hGrandChild5.Get() );
+ arrayChild5.AddToTail( hGrandChild5.Get() );
+
+ hGrandChild6 = CreateElement<CDmElement>( "grandchild6", fileid );
+ arrayChild1.AddToTail( hGrandChild6.Get() );
+ hStrong6 = hGrandChild6;
+
+ hGrandChild7 = CreateElement<CDmElement>( "grandchild7", fileid );
+ arrayChild1.AddToTail( hGrandChild7.Get() );
+ hStrong7 = hGrandChild7;
+ }
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nCreateCount = g_pDataModel->GetAllocatedElementCount();
+ Assert( nCreateCount == nStartingCount + 13 );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_ValidAndNonNULL ( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild1 );
+ Assert_ValidAndNonNULL ( hGrandChild2 );
+ Assert_ValidAndNonNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_ValidAndNonNULL ( hGrandChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ g_pDataModel->ClearUndo();
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nPostCreateCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nPostCreateCount == nCreateCount );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_ValidAndNonNULL ( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild1 );
+ Assert_ValidAndNonNULL ( hGrandChild2 );
+ Assert_ValidAndNonNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_ValidAndNonNULL ( hGrandChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ {
+ CUndoScopeGuard guard( "Edit RefCountTest Elements" );
+
+ CDmrGenericArray children( hChild1, "children" );
+ children.RemoveAll(); // grandchild1 unref'ed to 0, grandchild2..6 unref'ed to 1
+
+ hChild2->SetValue( "child", DMELEMENT_HANDLE_INVALID ); // grandchild2 unref'ed to 0
+
+ g_pDataModel->DestroyElement( hChild3 ); // grandchild3 is unref'ed to 0
+
+ CDmrElementArray<> children5( hChild5, "children" );
+ children5.Set( 0, NULL ); // grandchild5 is unref'ed to 0
+
+ hStrong6 = DMELEMENT_HANDLE_INVALID; // grandchild6 is unref'ed to 0
+ }
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nEditCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nEditCount == nCreateCount - 1 );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_InvalidButNonNULL( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild1 );
+ Assert_ValidAndNonNULL ( hGrandChild2 );
+ Assert_ValidAndNonNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_ValidAndNonNULL ( hGrandChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ g_pDataModel->Undo();
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nUndoCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nUndoCount == nCreateCount );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_ValidAndNonNULL ( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild1 );
+ Assert_ValidAndNonNULL ( hGrandChild2 );
+ Assert_ValidAndNonNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_ValidAndNonNULL ( hGrandChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ g_pDataModel->Redo();
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nRedoCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nRedoCount == nEditCount );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_InvalidButNonNULL( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild1 );
+ Assert_ValidAndNonNULL ( hGrandChild2 );
+ Assert_ValidAndNonNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_ValidAndNonNULL ( hGrandChild5 );
+ Assert_ValidAndNonNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ g_pDataModel->ClearUndo();
+
+ g_pDmElementFramework->BeginEdit();
+
+ int nClearUndoCount = g_pDataModel->GetAllocatedElementCount();
+ Shipping_Assert( nClearUndoCount == nCreateCount - 6 );
+
+ Assert_ValidAndNonNULL ( hRoot );
+ Assert_ValidAndNonNULL ( hChild1 );
+ Assert_ValidAndNonNULL ( hChild2 );
+ Assert_InvalidAndNULL ( hChild3 );
+ Assert_ValidAndNonNULL ( hChild4 );
+ Assert_ValidAndNonNULL ( hChild5 );
+ Assert_InvalidAndNULL ( hGrandChild1 );
+ Assert_InvalidAndNULL ( hGrandChild2 );
+ Assert_InvalidAndNULL ( hGrandChild3 );
+ Assert_ValidAndNonNULL ( hGrandChild4 );
+ Assert_InvalidAndNULL ( hGrandChild5 );
+ Assert_InvalidAndNULL ( hGrandChild6 );
+ Assert_ValidAndNonNULL ( hGrandChild7 );
+
+ g_pDataModel->RemoveFileId( fileid );
+}
diff --git a/unittests/dmxtest/dmxtestvalue.cpp b/unittests/dmxtest/dmxtestvalue.cpp
new file mode 100644
index 0000000..df20e10
--- /dev/null
+++ b/unittests/dmxtest/dmxtestvalue.cpp
@@ -0,0 +1,104 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for DMX testing (testing the single-value operations)
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/idatamodel.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "datamodel/dmehandle.h"
+#include "tier2/tier2.h"
+#include "movieobjects/dmeshape.h"
+
+
+DEFINE_TESTCASE_NOSUITE( DmxValueTest )
+{
+ Msg( "Running dmx single value tests...\n" );
+
+ CDisableUndoScopeGuard sg;
+ DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunValueTests>" );
+
+ CDmElement *pElement = CreateElement< CDmElement >( "root", fileid );
+
+ CDmElement *pElement2 = CreateElement<CDmElement>( "element1", fileid );
+ Assert( pElement2 );
+ CDmElement *pElement3 = CreateElement<CDmElement>( "element2", fileid );
+ Assert( pElement3 );
+ CDmeShape *pElement4 = CreateElement<CDmeShape>( "shape", fileid );
+ Assert( pElement4 );
+
+ CDmAttribute *pIntAttribute = pElement->SetValue( "int_test", 5 );
+ CDmAttribute *pFloatAttribute = pElement->SetValue( "float_test", 4.5f );
+ CDmAttribute *pBoolAttribute = pElement->SetValue( "bool_test", true );
+
+ CDmAttribute *pAttribute = pElement->AddAttribute( "int_convert_test", AT_INT );
+
+ // Type conversion set test
+ pAttribute->SetValue( 5 );
+ Shipping_Assert( pAttribute->GetValue<int>() == 5 );
+ pAttribute->SetValue( 4.5f );
+ Shipping_Assert( pAttribute->GetValue<int>() == 4 );
+ pAttribute->SetValue( true );
+ Shipping_Assert( pAttribute->GetValue<int>() == 1 );
+ pAttribute->SetValue( pIntAttribute );
+ Shipping_Assert( pAttribute->GetValue<int>() == 5 );
+ pAttribute->SetValue( pFloatAttribute );
+ Shipping_Assert( pAttribute->GetValue<int>() == 4 );
+ pAttribute->SetValue( pBoolAttribute );
+ Shipping_Assert( pAttribute->GetValue<int>() == 1 );
+
+ pAttribute = pElement->AddAttribute( "bool_convert_test", AT_BOOL );
+
+ // Type conversion set test
+ pAttribute->SetValue( 5 );
+ Shipping_Assert( pAttribute->GetValue<bool>() == true );
+ pAttribute->SetValue( 4.5f );
+ Shipping_Assert( pAttribute->GetValue<bool>() == true );
+ pAttribute->SetValue( false );
+ Shipping_Assert( pAttribute->GetValue<bool>() == false );
+ pAttribute->SetValue( pIntAttribute );
+ Shipping_Assert( pAttribute->GetValue<bool>() == true );
+ pAttribute->SetValue( pFloatAttribute );
+ Shipping_Assert( pAttribute->GetValue<bool>() == true );
+ pAttribute->SetValue( pBoolAttribute );
+ Shipping_Assert( pAttribute->GetValue<bool>() == true );
+
+ pAttribute = pElement->AddAttribute( "float_convert_test", AT_FLOAT );
+
+ // Type conversion set test
+ pAttribute->SetValue( 5 );
+ Shipping_Assert( pAttribute->GetValue<float>() == 5.0f );
+ pAttribute->SetValue( 4.5f );
+ Shipping_Assert( pAttribute->GetValue<float>() == 4.5f );
+ pAttribute->SetValue( true );
+ Shipping_Assert( pAttribute->GetValue<float>() == 1.0f );
+ pAttribute->SetValue( pIntAttribute );
+ Shipping_Assert( pAttribute->GetValue<float>() == 5.0f );
+ pAttribute->SetValue( pFloatAttribute );
+ Shipping_Assert( pAttribute->GetValue<float>() == 4.5f );
+ pAttribute->SetValue( pBoolAttribute );
+ Shipping_Assert( pAttribute->GetValue<float>() == 1.0f );
+
+ // Type conversion set test
+ QAngle angles( 90, 0, 0 );
+ Quaternion quat;
+ AngleQuaternion( angles, quat );
+
+ pAttribute = pElement->AddAttribute( "qangle_convert_test", AT_QANGLE );
+ pAttribute->SetValue( angles );
+ Shipping_Assert( pAttribute->GetValue<QAngle>() == angles );
+ pAttribute->SetValue( quat );
+ Shipping_Assert( pAttribute->GetValue<QAngle>() == angles );
+
+ pAttribute = pElement->AddAttribute( "quat_convert_test", AT_QUATERNION );
+ pAttribute->SetValue( angles );
+ Shipping_Assert( pAttribute->GetValue<Quaternion>() == quat );
+ pAttribute->SetValue( quat );
+ Shipping_Assert( pAttribute->GetValue<Quaternion>() == quat );
+
+ g_pDataModel->RemoveFileId( fileid );
+}
diff --git a/unittests/ihvtest1/ihvtest1.cpp b/unittests/ihvtest1/ihvtest1.cpp
new file mode 100644
index 0000000..b1253dc
--- /dev/null
+++ b/unittests/ihvtest1/ihvtest1.cpp
@@ -0,0 +1,1508 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+//=============================================================================
+
+#define PROTECTED_THINGS_DISABLE
+#if !defined( _X360 )
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+#include <time.h>
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/IMaterialSystemHardwareConfig.h"
+#include "materialsystem/imaterialproxyfactory.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "appframework/appframework.h"
+#include "datacache\idatacache.h"
+#include "datacache\imdlcache.h"
+#include "vphysics_interface.h"
+#include "filesystem.h"
+#include "IStudioRender.h"
+#include "studio.h"
+#include "clientstats.h"
+#include "bone_setup.h"
+#include "tier0/icommandline.h"
+#include "vstdlib/cvar.h"
+#include "tier0/vprof.h"
+#include "tier1/tier1.h"
+#include "optimize.h"
+#if defined( _X360 )
+#include "xbox\xbox_console.h"
+#include "xbox\xbox_win32stubs.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Main system interfaces
+//-----------------------------------------------------------------------------
+IMaterialSystem *g_pMaterialSystem = NULL;
+IStudioRender *g_pStudioRender = NULL;
+IFileSystem *g_pFileSystem = NULL;
+IMDLCache *g_pMDLCache = NULL;
+
+
+//-----------------------------------------------------------------------------
+// App control defines
+//-----------------------------------------------------------------------------
+//#define MATERIAL_OVERRIDE
+//#define USE_VTUNE
+//#define USE_VPROF
+
+#if USE_VTUNE
+#include "vtuneapi.h"
+#endif
+
+static bool g_WindowMode = false;
+static bool g_bUseEmptyShader = false;
+static bool g_BenchFinished = false;
+static bool g_BenchMode = false;
+static bool g_SoftwareTL = false;
+
+static int g_RenderWidth = 640;
+static int g_RenderHeight = 480;
+static int g_RefreshRate = 60;
+static int g_LOD = 0;
+static int g_BodyGroup = 0;
+
+static int g_NumRows = 10;
+static int g_NumCols = 10;
+
+static int g_dxLevel = 0;
+static int g_LightingCombination = -1;
+
+static FILE *g_IHVTestFP = NULL;
+static IMaterial *g_pForceMaterial = NULL;
+
+static bool g_bInError = false;
+
+#define MAX_LIGHTS 2
+#define NUM_LIGHT_TYPES 4
+#define LIGHTING_COMBINATION_COUNT 5
+
+static const char *g_LightCombinationNames[] =
+{
+ "DISABLE ",
+// "SPOT ",
+ "POINT ",
+ "DIRECTIONAL ",
+ "SPOT_SPOT ",
+// "SPOT_POINT ",
+// "SPOT_DIRECTIONAL ",
+// "POINT_POINT ",
+ "POINT_DIRECTIONAL ",
+ "DIRECTIONAL_DIRECTIONAL"
+};
+
+//-----------------------------------------------------------------------------
+// Test Model class
+//-----------------------------------------------------------------------------
+struct IHVTestModel
+{
+ MDLHandle_t hMdl;
+ studiohdr_t *pStudioHdr;
+ studiohwdata_t *pHardwareData;
+};
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CIHVTestApp : public CDefaultAppSystemGroup< CSteamAppSystemGroup >
+{
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+
+private:
+ bool CreateAppWindow( char const *pTitle, int w, int h );
+ void AppPumpMessages( void );
+ void RenderFrame( void );
+ void RenderScene( void );
+ bool SetupMaterialSystem();
+ bool SetupStudioRender();
+ bool LoadModels( void );
+ bool LoadModel( const char *pModelName, IHVTestModel *pModel );
+ bool CreateMainWindow( int width, int height, bool fullscreen );
+ matrix3x4_t* SetUpBones( studiohdr_t *pStudioHdr, const matrix3x4_t &shapeToWorld, int iRun, int model, int boneMask );
+
+ // Windproc
+ static LONG WINAPI WinAppWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ LONG WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ HWND m_hWnd;
+ bool m_bExitMainLoop;
+
+ IHVTestModel *m_pIHVTestModel;
+};
+
+static CIHVTestApp s_IHVTestApp;
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT_GLOBALVAR( CIHVTestApp, s_IHVTestApp );
+
+//-----------------------------------------------------------------------------
+// Create the application window
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::CreateAppWindow( const char* pAppName, int width, int height )
+{
+ // Register the window class
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = WinAppWindowProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = pAppName;
+ wc.hIcon = NULL;
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Create the application's window
+ m_hWnd = CreateWindow( pAppName, pAppName,
+ WS_OVERLAPPEDWINDOW,
+ 0, 0, width, height,
+ GetDesktopWindow(), NULL, wc.hInstance, NULL );
+
+ ShowWindow (m_hWnd, SW_SHOWDEFAULT);
+
+ return (m_hWnd != 0);
+}
+
+//#define TREES
+
+// The maximum number of distinctive models each test may specify.
+#ifdef TREES
+const int g_nMaxModels = 1;
+#else
+const int g_nMaxModels = 9;
+#endif
+
+//-----------------------------------------------------------------------------
+// Benchmarking
+//-----------------------------------------------------------------------------
+struct BenchRunInfo
+{
+ const char *pModelName[g_nMaxModels];
+ int numFrames;
+ int rows;
+ int cols;
+ float modelSize;
+ int sequence1[g_nMaxModels];
+ int sequence2;
+};
+
+struct BenchResults
+{
+ BenchResults() : totalTime( 0.0f ), totalTris( 0 ) {}
+ float totalTime;
+ int totalTris;
+};
+
+
+#define NUM_BENCH_RUNS 1
+static BenchResults g_BenchResults[NUM_BENCH_RUNS][LIGHTING_COMBINATION_COUNT];
+
+#ifdef TREES
+
+#define MODEL_ROWS 13
+#define MODEL_COLUMNS 13
+static BenchRunInfo g_BenchRuns[NUM_BENCH_RUNS] =
+{
+ { { "models/props_foliage/tree_dead01.mdl"
+ }, 100, MODEL_ROWS, MODEL_COLUMNS, 1000.0f, { 0 }, -1 },
+};
+
+#else
+
+#define MODEL_ROWS 3
+#define MODEL_COLUMNS 3
+static BenchRunInfo g_BenchRuns[NUM_BENCH_RUNS] =
+{
+ { { "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ "models/alyx.mdl",
+ }, 100, MODEL_ROWS, MODEL_COLUMNS, 75.0f, { 1, 4, 20, 23, 25, 30, 34, 38, 1 }, -1 },
+};
+
+#endif
+
+// this is used in "-bench" mode
+static IHVTestModel g_BenchModels[NUM_BENCH_RUNS][g_nMaxModels];
+
+static void WriteBenchResults( void )
+{
+ if( !g_BenchMode )
+ {
+ return;
+ }
+
+ FILE *fp = fopen( "ihvtest1.csv", "a+" );
+ Assert( fp );
+ if( !fp )
+ {
+ return;
+ }
+
+ fprintf( fp, "------------------------------------------------------------------\n" );
+
+ time_t ltime;
+ time( &ltime );
+
+ fprintf( fp, "%s\n", GetCommandLine() );
+ fprintf( fp, "Run at: %s", ctime( &ltime ) );
+
+ int i;
+ for( i = 0; i < NUM_BENCH_RUNS; i++ )
+ {
+ int j;
+ fprintf( fp, "model,light combo,total tris,total time,tris/sec\n" );
+ for( j = 0; j < LIGHTING_COMBINATION_COUNT; j++ )
+ {
+ int k;
+ for( k = 0; k < g_nMaxModels; k++ )
+ {
+ if( g_BenchRuns[i].pModelName[k] )
+ {
+ fprintf( fp, "%s%s", k ? ", " : "", g_BenchRuns[i].pModelName[k] );
+ }
+ }
+ fprintf( fp, "," );
+ fprintf( fp, "%s,", g_LightCombinationNames[j] );
+ fprintf( fp, "%d,", g_BenchResults[i][j].totalTris );
+ fprintf( fp, "%0.5f,", ( float )g_BenchResults[i][j].totalTime );
+ fprintf( fp, "%0.0lf\n", ( double )g_BenchResults[i][j].totalTris /
+ ( double )g_BenchResults[i][j].totalTime );
+ Warning( "%f %d\n", ( float )g_BenchResults[i][j].totalTime, g_BenchResults[i][j].totalTris );
+ }
+ }
+
+ fclose( fp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Destroy app
+//-----------------------------------------------------------------------------
+void CIHVTestApp::Destroy()
+{
+ // Close the window
+ if (m_hWnd)
+ DestroyWindow( m_hWnd );
+
+ WriteBenchResults();
+}
+
+
+//-----------------------------------------------------------------------------
+// Window size helper
+//-----------------------------------------------------------------------------
+static void CalcWindowSize( int desiredRenderingWidth, int desiredRenderingHeight,
+ int *windowWidth, int *windowHeight )
+{
+ int borderX, borderY;
+ borderX = (GetSystemMetrics(SM_CXFIXEDFRAME) + 1) * 2;
+ borderY = (GetSystemMetrics(SM_CYFIXEDFRAME) + 1) * 2 + GetSystemMetrics(SM_CYSIZE) + 1;
+ *windowWidth = desiredRenderingWidth + borderX;
+ *windowHeight = desiredRenderingHeight + borderY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Spew function!
+//-----------------------------------------------------------------------------
+SpewRetval_t IHVTestSpewFunc( SpewType_t spewType, char const *pMsg )
+{
+ g_bInError = true;
+
+ OutputDebugString( pMsg );
+ switch( spewType )
+ {
+ case SPEW_MESSAGE:
+ case SPEW_WARNING:
+ case SPEW_LOG:
+ OutputDebugString( pMsg );
+ g_bInError = false;
+ return SPEW_CONTINUE;
+
+ case SPEW_ASSERT:
+ case SPEW_ERROR:
+ default:
+ ::MessageBox( NULL, pMsg, "Error!", MB_OK );
+ g_bInError = false;
+ return SPEW_DEBUGGER;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Spew function to write to ihvtest_vprof.txt
+//-----------------------------------------------------------------------------
+SpewRetval_t IHVTestVProfSpewFunc( SpewType_t spewType, char const *pMsg )
+{
+ g_bInError = true;
+
+ switch( spewType )
+ {
+ case SPEW_MESSAGE:
+ case SPEW_WARNING:
+ case SPEW_LOG:
+ fprintf( g_IHVTestFP, "%s", pMsg );
+ g_bInError = false;
+ return SPEW_CONTINUE;
+
+ case SPEW_ASSERT:
+ case SPEW_ERROR:
+ default:
+ ::MessageBox( NULL, pMsg, "Error!", MB_OK );
+ g_bInError = false;
+ return SPEW_DEBUGGER;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Warnings and Errors...
+//-----------------------------------------------------------------------------
+#define MAXPRINTMSG 4096
+void DisplayError( const char* pError, ... )
+{
+ va_list argptr;
+ char msg[1024];
+
+ g_bInError = true;
+
+ va_start( argptr, pError );
+ Q_vsnprintf( msg, sizeof( msg ), pError, argptr );
+ va_end( argptr );
+
+ MessageBox( 0, msg, 0, MB_OK );
+
+ exit( -1 );
+}
+
+static void MaterialSystem_Warning( const char *fmt, ... )
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ va_start( argptr, fmt );
+ Q_vsnprintf( msg, sizeof ( msg ), fmt, argptr );
+ va_end( argptr );
+
+ OutputDebugString( msg );
+}
+
+// garymcthack
+static void MaterialSystem_Warning( char *fmt, ... )
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ va_start( argptr, fmt );
+ Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
+ va_end( argptr );
+
+ OutputDebugString( msg );
+}
+
+static void MaterialSystem_Error( char *fmt, ... )
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ g_bInError = true;
+
+ va_start( argptr, fmt );
+ Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
+ va_end( argptr );
+
+ MessageBox( NULL, (LPCTSTR)msg, "MaterialSystem Fatal Error", MB_OK | MB_ICONINFORMATION );
+
+#ifdef _DEBUG
+ Assert( 0 );
+#endif
+ exit( -1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Engine Stats
+//-----------------------------------------------------------------------------
+// itty bitty interface for stat time
+class CStatTime : public IClientStatsTime
+{
+public:
+ float GetTime()
+ {
+ return Sys_FloatTime();
+ }
+};
+CStatTime g_StatTime;
+
+class CEngineStats
+{
+public:
+ CEngineStats() : m_InFrame( false ) {};
+ //
+ // stats input
+ //
+
+ void BeginRun( void );
+ void BeginFrame( void );
+
+ void EndFrame( void );
+ void EndRun( void );
+
+ //
+ // stats output
+ // call these outside of a BeginFrame/EndFrame pair
+ //
+
+ // return the frame time in seconds for the whole system (not just graphics)
+ double GetCurrentSystemFrameTime( void );
+ double GetRunTime( void );
+private:
+ // How many frames worth of data have we logged?
+ int m_totalNumFrames;
+
+ // frame timing data
+ double m_frameStartTime;
+ double m_frameEndTime;
+ double m_minFrameTime;
+ double m_maxFrameTime;
+
+ // run timing data
+ double m_runStartTime;
+ double m_runEndTime;
+
+ bool m_InFrame;
+};
+
+void CEngineStats::BeginRun( void )
+{
+ m_totalNumFrames = 0;
+ // frame timing data
+ m_runStartTime = Sys_FloatTime();
+}
+
+void CEngineStats::EndRun( void )
+{
+ m_runEndTime = Sys_FloatTime();
+}
+
+void CEngineStats::BeginFrame( void )
+{
+ m_InFrame = true;
+ m_frameStartTime = Sys_FloatTime();
+}
+
+void CEngineStats::EndFrame( void )
+{
+ double deltaTime;
+
+ m_frameEndTime = Sys_FloatTime();
+ deltaTime = GetCurrentSystemFrameTime();
+
+ m_InFrame = false;
+}
+
+double CEngineStats::GetRunTime( void )
+{
+ return m_runEndTime - m_runStartTime;
+}
+
+double CEngineStats::GetCurrentSystemFrameTime( void )
+{
+ return m_frameEndTime - m_frameStartTime;
+}
+static CEngineStats g_EngineStats;
+
+
+//-----------------------------------------------------------------------------
+// Lighting
+//-----------------------------------------------------------------------------
+// If you change the number of lighting combinations, change LIGHTING_COMBINATION_COUNT
+static LightType_t g_LightCombinations[][MAX_LIGHTS] =
+{
+ { MATERIAL_LIGHT_DISABLE, MATERIAL_LIGHT_DISABLE }, // 0
+// { MATERIAL_LIGHT_SPOT, MATERIAL_LIGHT_DISABLE },
+// { MATERIAL_LIGHT_POINT, MATERIAL_LIGHT_DISABLE },
+ { MATERIAL_LIGHT_DIRECTIONAL, MATERIAL_LIGHT_DISABLE },
+ { MATERIAL_LIGHT_SPOT, MATERIAL_LIGHT_SPOT },
+
+// { MATERIAL_LIGHT_SPOT, MATERIAL_LIGHT_POINT }, // 5
+// { MATERIAL_LIGHT_SPOT, MATERIAL_LIGHT_DIRECTIONAL },
+// { MATERIAL_LIGHT_POINT, MATERIAL_LIGHT_POINT },
+ { MATERIAL_LIGHT_POINT, MATERIAL_LIGHT_DIRECTIONAL },
+ { MATERIAL_LIGHT_DIRECTIONAL, MATERIAL_LIGHT_DIRECTIONAL }, // 9
+};
+
+LightDesc_t g_TestLights[NUM_LIGHT_TYPES][MAX_LIGHTS];
+
+static void FixLight( LightDesc_t *pLight )
+{
+ pLight->m_Range = 0.0f;
+ pLight->m_Falloff = 1.0f;
+ pLight->m_ThetaDot = cos( pLight->m_Theta * 0.5f );
+ pLight->m_PhiDot = cos( pLight->m_Phi * 0.5f );
+ pLight->m_Flags = 0;
+ if( pLight->m_Attenuation0 != 0.0f )
+ {
+ pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
+ }
+ if( pLight->m_Attenuation1 != 0.0f )
+ {
+ pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
+ }
+ if( pLight->m_Attenuation2 != 0.0f )
+ {
+ pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
+ }
+ VectorNormalize( pLight->m_Direction );
+}
+
+
+static void InitTestLights( void )
+{
+ LightDesc_t *pLight;
+ int i;
+ for( i = 0; i < MAX_LIGHTS; i++ )
+ {
+ // MATERIAL_LIGHT_DISABLE
+ pLight = &g_TestLights[MATERIAL_LIGHT_DISABLE][i];
+ pLight->m_Type = MATERIAL_LIGHT_DISABLE;
+ }
+
+ // x - right
+ // y - up
+ // z - front of model
+ // MATERIAL_LIGHT_SPOT 0
+ pLight = &g_TestLights[MATERIAL_LIGHT_SPOT][0];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_SPOT;
+ pLight->m_Color.Init( 5000.0f, 3500.0f, 3500.0f );
+ pLight->m_Position.Init( 0.0f, 0.0f, 50.0f );
+ pLight->m_Direction.Init( 0.0f, 0.5f, -1.0f );
+ pLight->m_Attenuation0 = 0.0f;
+ pLight->m_Attenuation1 = 0.0f;
+ pLight->m_Attenuation2 = 1.0f / 10;
+ pLight->m_Theta = DEG2RAD( 20.0f );
+ pLight->m_Phi = DEG2RAD( 30.0f );
+
+ // MATERIAL_LIGHT_SPOT 1
+ pLight = &g_TestLights[MATERIAL_LIGHT_SPOT][1];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_SPOT;
+ pLight->m_Color.Init( 3500.0f, 5000.0f, 3500.0f );
+ pLight->m_Position.Init( 0.0f, 0.0f, 150.0f );
+ pLight->m_Direction.Init( 0.0f, 0.5f, -1.0f );
+ pLight->m_Attenuation0 = 0.0f;
+ pLight->m_Attenuation1 = 0.0f;
+ pLight->m_Attenuation2 = 1.0f / 10;
+ pLight->m_Theta = DEG2RAD( 20.0f );
+ pLight->m_Phi = DEG2RAD( 30.0f );
+
+ // MATERIAL_LIGHT_POINT 0
+ pLight = &g_TestLights[MATERIAL_LIGHT_POINT][0];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_POINT;
+ pLight->m_Color.Init( 1500.0f, 750.0f, 750.0f );
+ pLight->m_Position.Init( 200.0f, 200.0f, 200.0f );
+ pLight->m_Attenuation0 = 0.0f;
+ pLight->m_Attenuation1 = 1.0f;
+ pLight->m_Attenuation2 = 0.0f;
+
+ // MATERIAL_LIGHT_POINT 1
+ pLight = &g_TestLights[MATERIAL_LIGHT_POINT][1];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_POINT;
+ pLight->m_Color.Init( 750.0f, 750.0f, 1500.0f );
+ pLight->m_Position.Init( -200.0f, 200.0f, 200.0f );
+ pLight->m_Attenuation0 = 0.0f;
+ pLight->m_Attenuation1 = 1.0f;
+ pLight->m_Attenuation2 = 0.0f;
+
+ // MATERIAL_LIGHT_DIRECTIONAL 0
+ pLight = &g_TestLights[MATERIAL_LIGHT_DIRECTIONAL][0];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ pLight->m_Color.Init( 2.0f, 2.0f, 1.0f );
+ pLight->m_Direction.Init( -1.0f, 0.0f, 0.0f );
+ pLight->m_Attenuation0 = 1.0f;
+ pLight->m_Attenuation1 = 0.0f;
+ pLight->m_Attenuation2 = 0.0f;
+
+ // MATERIAL_LIGHT_DIRECTIONAL 1
+ pLight = &g_TestLights[MATERIAL_LIGHT_DIRECTIONAL][1];
+ memset( pLight, 0, sizeof( LightDesc_t ) );
+ pLight->m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ pLight->m_Color.Init( 1.0f, 1.0f, 2.0f );
+ pLight->m_Direction.Init( 1.0f, 0.0f, 0.0f );
+ pLight->m_Attenuation0 = 1.0f;
+ pLight->m_Attenuation1 = 0.0f;
+ pLight->m_Attenuation2 = 0.0f;
+
+ for( i = 0; i < MAX_LIGHTS; i++ )
+ {
+ int j;
+ for( j = 0; j < NUM_LIGHT_TYPES; j++ )
+ {
+ FixLight( &g_TestLights[j][i] );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Setup lighting
+//-----------------------------------------------------------------------------
+static void SetupLighting( int lightingCombination, Vector &lightOffset )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ if( lightingCombination == 0 )
+ {
+ g_pStudioRender->SetLocalLights( 0, NULL );
+ pRenderContext->SetAmbientLight( 1.0, 1.0, 1.0 );
+
+ static Vector white[6] =
+ {
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 ),
+ Vector( 1.0, 1.0, 1.0 ),
+ };
+ g_pStudioRender->SetAmbientLightColors( white );
+ }
+ else
+ {
+ pRenderContext->SetAmbientLight( 0.0f, 0.0f, 0.0f );
+
+ static Vector black[6] =
+ {
+ Vector( 0.0, 0.0, 0.0 ),
+ Vector( 0.0, 0.0, 0.0 ),
+ Vector( 0.0, 0.0, 0.0 ),
+ Vector( 0.0, 0.0, 0.0 ),
+ Vector( 0.0, 0.0, 0.0 ),
+ Vector( 0.0, 0.0, 0.0 ),
+ };
+ g_pStudioRender->SetAmbientLightColors( black );
+
+ int lightID;
+ LightDesc_t lightDescs[MAX_LIGHTS];
+ for( lightID = 0; lightID < MAX_LIGHTS; lightID++ )
+ {
+ int lightType = g_LightCombinations[lightingCombination][lightID];
+ lightDescs[lightID] = g_TestLights[lightType][lightID];
+ lightDescs[lightID].m_Position += lightOffset;
+ }
+
+ // Feed disabled lights through?
+ if( g_LightCombinations[lightingCombination][1] == MATERIAL_LIGHT_DISABLE )
+ {
+ g_pStudioRender->SetLocalLights( 1, lightDescs );
+ }
+ else
+ {
+ g_pStudioRender->SetLocalLights( MAX_LIGHTS, lightDescs );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Models
+//-----------------------------------------------------------------------------
+static float s_PoseParameter[32];
+static float s_Cycle[9] = { 0.0f };
+
+virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
+{
+ if ( numincludemodels == 0 )
+ return NULL;
+ return g_pMDLCache->GetVirtualModelFast( this, (MDLHandle_t)virtualModel );
+}
+
+byte *studiohdr_t::GetAnimBlock( int i ) const
+{
+ return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i );
+}
+
+int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
+{
+ return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut );
+}
+
+const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set up the bones for a frame
+//-----------------------------------------------------------------------------
+matrix3x4_t* CIHVTestApp::SetUpBones( studiohdr_t *pStudioHdr, const matrix3x4_t &shapeToWorld, int iRun, int model, int boneMask )
+{
+ CStudioHdr studioHdr( pStudioHdr, g_pMDLCache );
+
+ // Default to middle of the pose parameter range
+ float flPoseParameter[MAXSTUDIOPOSEPARAM];
+ Studio_CalcDefaultPoseParameters( &studioHdr, flPoseParameter, MAXSTUDIOPOSEPARAM );
+
+ int nFrameCount = Studio_MaxFrame( &studioHdr, g_BenchRuns[iRun].sequence1[model], flPoseParameter );
+ if ( nFrameCount == 0 )
+ {
+ nFrameCount = 1;
+ }
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ IBoneSetup boneSetup( &studioHdr, boneMask, flPoseParameter );
+ boneSetup.InitPose( pos, q );
+ boneSetup.AccumulatePose( pos, q, g_BenchRuns[iRun].sequence1[model], s_Cycle[model], 1.0f, 0.0, NULL );
+
+ // FIXME: Try enabling this?
+// CalcAutoplaySequences( pStudioHdr, NULL, pos, q, flPoseParameter, BoneMask( ), flTime );
+
+ // Root transform
+ matrix3x4_t rootToWorld, temp;
+
+ MatrixCopy( shapeToWorld, rootToWorld );
+
+ matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( studioHdr.numbones() );
+ for ( int i = 0; i < studioHdr.numbones(); i++ )
+ {
+ // If it's not being used, fill with NAN for errors
+ if ( !(studioHdr.pBone( i )->flags & boneMask) )
+ {
+ int j, k;
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ pBoneToWorld[i][j][k] = VEC_T_NAN;
+ }
+ }
+ continue;
+ }
+
+ matrix3x4_t boneMatrix;
+ QuaternionMatrix( q[i], boneMatrix );
+ MatrixSetColumn( pos[i], 3, boneMatrix );
+
+ if (studioHdr.pBone(i)->parent == -1)
+ {
+ ConcatTransforms (rootToWorld, boneMatrix, pBoneToWorld[i]);
+ }
+ else
+ {
+ ConcatTransforms (pBoneToWorld[ studioHdr.pBone(i)->parent ], boneMatrix, pBoneToWorld[i] );
+ }
+ }
+ g_pStudioRender->UnlockBoneMatrices();
+ return pBoneToWorld;
+}
+
+
+//-----------------------------------------------------------------------------
+// Use mdlcache to load a model
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::LoadModel( const char* pModelName, IHVTestModel *pModel )
+{
+ pModel->hMdl = g_pMDLCache->FindMDL( pModelName );
+
+ pModel->pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->hMdl );
+
+ g_pMDLCache->GetVertexData( pModel->hMdl );
+ g_pMDLCache->FinishPendingLoads();
+
+ g_pMDLCache->GetHardwareData( pModel->hMdl );
+ g_pMDLCache->FinishPendingLoads();
+
+ pModel->pHardwareData = g_pMDLCache->GetHardwareData( pModel->hMdl );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads all models
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::LoadModels( void )
+{
+ const char *pArgVal;
+ if( CommandLine()->CheckParm( "-rowcol", &pArgVal ) )
+ {
+ g_NumRows = g_NumCols = atoi( pArgVal );
+ }
+
+ /* figure out which LOD we are going to render */
+ if( CommandLine()->CheckParm( "-lod", &pArgVal ) )
+ {
+ g_LOD = atoi( pArgVal );
+ }
+
+ if( CommandLine()->CheckParm( "-body", &pArgVal ) )
+ {
+ g_BodyGroup = atoi( pArgVal );
+ }
+
+ // figure out g_RefreshRate
+ if( CommandLine()->CheckParm( "-refresh", &pArgVal ) )
+ {
+ g_RefreshRate = atoi( pArgVal );
+ }
+
+ if( CommandLine()->CheckParm( "-light", &pArgVal ) )
+ {
+ g_LightingCombination = atoi( pArgVal );
+ if( g_LightingCombination < 0 )
+ {
+ g_LightingCombination = 0;
+ }
+ if( g_LightingCombination >= LIGHTING_COMBINATION_COUNT )
+ {
+ g_LightingCombination = LIGHTING_COMBINATION_COUNT - 1;
+ }
+ }
+
+ g_pForceMaterial = g_pMaterialSystem->FindMaterial( "models/alyx/thigh", TEXTURE_GROUP_OTHER );
+#ifdef MATERIAL_OVERRIDE
+ g_pStudioRender->ForcedMaterialOverride( g_pForceMaterial );
+#endif
+
+ InitTestLights();
+
+ if( g_BenchMode )
+ {
+ int i;
+ for( i = 0; i < NUM_BENCH_RUNS; i++ )
+ {
+ // Load each of the potentially alternating models:
+ int k;
+ for( k = 0; k < g_nMaxModels; k++ )
+ {
+ if( g_BenchRuns[i].pModelName[k] )
+ {
+ if( !LoadModel( g_BenchRuns[i].pModelName[k], &g_BenchModels[i][k] ) )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ CommandLine()->CheckParm( "-i", &pArgVal );
+ if( !LoadModel( pArgVal, m_pIHVTestModel ) )
+ {
+ return false;
+ }
+ }
+ g_pMaterialSystem->CacheUsedMaterials();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// App window proc
+//-----------------------------------------------------------------------------
+LONG CIHVTestApp::WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ switch( msg )
+ {
+ // abort when ESC is hit
+ case WM_CHAR:
+ switch(wParam)
+ {
+ case VK_ESCAPE:
+ SendMessage( m_hWnd, WM_CLOSE, 0, 0 );
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ m_bExitMainLoop = true;
+ return 0;
+ }
+
+ return DefWindowProc( hWnd, msg, wParam, lParam );
+}
+
+
+//-----------------------------------------------------------------------------
+// Static registered window proc
+//-----------------------------------------------------------------------------
+LONG WINAPI CIHVTestApp::WinAppWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return s_IHVTestApp.WindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+
+//-----------------------------------------------------------------------------
+// Pump messages
+//-----------------------------------------------------------------------------
+void CIHVTestApp::AppPumpMessages()
+{
+ MSG msg;
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Advance the frame
+//-----------------------------------------------------------------------------
+void AdvanceFrame( CStudioHdr *pStudioHdr, int iRun, int model, float dt )
+{
+ if (dt > 0.1)
+ dt = 0.1f;
+
+ float t = Studio_Duration( pStudioHdr, g_BenchRuns[iRun].sequence1[model], s_PoseParameter );
+
+ if (t > 0)
+ {
+ s_Cycle[model] += dt / t;
+
+ // wrap
+ s_Cycle[model] -= (int)(s_Cycle[model]);
+ }
+ else
+ {
+ s_Cycle[model] = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Render a frame
+//-----------------------------------------------------------------------------
+void CIHVTestApp::RenderFrame( void )
+{
+ VPROF( "RenderFrame" );
+ IHVTestModel *pModel = NULL;
+ static int currentRun = 0;
+ static int currentFrame = 0;
+ static int currentLightCombo = 0;
+ int modelAlternator = 0;
+
+ if (g_bInError)
+ {
+ // error context is active
+ // error may be renderer based, avoid re-entrant render to fatal crash
+ return;
+ }
+
+ if( g_BenchMode )
+ {
+ if( currentFrame > g_BenchRuns[currentRun].numFrames )
+ {
+ currentLightCombo++;
+ if( currentLightCombo >= LIGHTING_COMBINATION_COUNT )
+ {
+ currentRun++;
+ currentLightCombo = 0;
+ if( currentRun >= NUM_BENCH_RUNS )
+ {
+ g_BenchFinished = true;
+ return;
+ }
+ }
+ currentFrame = 0;
+ }
+ }
+ if( g_BenchMode )
+ {
+ pModel = &g_BenchModels[currentRun][0];
+ g_NumCols = g_BenchRuns[currentRun].cols;
+ g_NumRows = g_BenchRuns[currentRun].rows;
+ }
+ else
+ {
+ pModel = m_pIHVTestModel;
+ }
+ Assert( pModel );
+
+ g_EngineStats.BeginFrame();
+
+ g_pMaterialSystem->BeginFrame( 0 );
+ g_pStudioRender->BeginFrame();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ pRenderContext->ClearColor3ub( 0, 0, 0 );
+ pRenderContext->ClearBuffers( true, true );
+
+ pRenderContext->Viewport( 0, 0, g_RenderWidth, g_RenderHeight );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity();
+ pRenderContext->PerspectiveX( 90.0f, ( g_RenderWidth / g_RenderHeight), 1.0f, 500000.0f );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity();
+ if( g_BenchMode )
+ {
+ pRenderContext->Translate( 0.0f, 0.0f, ( float )-( g_NumCols * g_BenchRuns[currentRun].modelSize * 0.6f ) );
+ }
+ else
+ {
+ pRenderContext->Translate( 0.0f, 0.0f, ( float )-( g_NumCols * 80.0f * 0.5f ) );
+ }
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity();
+
+ QAngle angles;
+ angles[YAW] = -90.0f;
+ angles[PITCH] = -90.0f;
+ angles[ROLL] = 0.0f;
+
+ matrix3x4_t cameraMatrix;
+ AngleMatrix( angles, cameraMatrix );
+
+ int r, c;
+ int trisRendered = 0;
+ float boneSetupTime = 0.0f;
+ for( r = 0; r < g_NumRows; r++ )
+ {
+ for( c = 0; c < g_NumCols; c++ )
+ {
+ // If we are alternating models, select the next valid model.
+ if( g_BenchMode )
+ {
+ do
+ {
+ // If I pass my maximum number of models, wrap around to model 0, which must always be valid.
+ if( ++modelAlternator >= g_nMaxModels )
+ {
+ modelAlternator = 0;
+ break;
+ }
+ }
+ while( !g_BenchRuns[currentRun].pModelName[modelAlternator] );
+
+ pModel = &g_BenchModels[currentRun][modelAlternator];
+ Assert( pModel );
+ }
+
+ if( g_BenchMode )
+ {
+ cameraMatrix[0][3] = ( ( c + 0.5f ) - ( g_NumCols * .5f ) ) * g_BenchRuns[currentRun].modelSize;
+ cameraMatrix[1][3] = ( ( float )r - ( g_NumCols * .5f ) ) * g_BenchRuns[currentRun].modelSize;
+ }
+ else
+ {
+ cameraMatrix[0][3] = ( ( c + 0.5f ) - ( g_NumCols * .5f ) ) * 75.0f;
+ cameraMatrix[1][3] = ( ( float )r - ( g_NumCols * .5f ) ) * 75.0f;
+ }
+
+ Vector modelOrigin( cameraMatrix[0][3], cameraMatrix[1][3], 0.0f );
+ Vector lightOffset( cameraMatrix[0][3], cameraMatrix[1][3], 0.0f );
+
+ if (g_LightingCombination < 0)
+ {
+ SetupLighting( g_BenchMode ? currentLightCombo : 0, lightOffset );
+ }
+ else
+ {
+ SetupLighting( g_LightingCombination, lightOffset );
+ }
+
+ float startBoneSetupTime = Sys_FloatTime();
+ int lod = g_LOD;
+ lod = clamp( lod, pModel->pHardwareData->m_RootLOD, pModel->pHardwareData->m_NumLODs-1 );
+
+ int boneMask = BONE_USED_BY_VERTEX_AT_LOD( lod );
+ matrix3x4_t *pBoneToWorld = SetUpBones( pModel->pStudioHdr, cameraMatrix, currentRun, modelAlternator, boneMask );
+ boneSetupTime += Sys_FloatTime() - startBoneSetupTime;
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+
+ DrawModelInfo_t modelInfo;
+ memset( &modelInfo, 0, sizeof( modelInfo ) );
+ modelInfo.m_pStudioHdr = pModel->pStudioHdr;
+ modelInfo.m_pHardwareData = pModel->pHardwareData;
+ modelInfo.m_Decals = STUDIORENDER_DECAL_INVALID;
+ modelInfo.m_Skin = 0;
+ modelInfo.m_Body = g_BodyGroup;
+ modelInfo.m_HitboxSet = 0;
+ modelInfo.m_pClientEntity = NULL;
+ modelInfo.m_Lod = lod;
+ modelInfo.m_pColorMeshes = NULL;
+ g_pStudioRender->DrawModel( NULL, modelInfo, pBoneToWorld, NULL, NULL, modelOrigin );
+
+ DrawModelResults_t results;
+ g_pStudioRender->GetPerfStats( &results, modelInfo, NULL );
+ trisRendered += results.m_ActualTriCount;
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+ }
+ }
+
+ pRenderContext->Flush( true );
+ g_EngineStats.EndFrame();
+
+ g_pStudioRender->EndFrame();
+ g_pMaterialSystem->EndFrame();
+
+ // hack - don't count the first frame in case there are any state
+ // transitions computed on that frame.
+ if( currentFrame != 0 )
+ {
+ g_BenchResults[currentRun][currentLightCombo].totalTime += g_EngineStats.GetCurrentSystemFrameTime();
+ g_BenchResults[currentRun][currentLightCombo].totalTris += trisRendered;
+ }
+
+ for ( int model = 0; model < g_nMaxModels; ++model )
+ {
+ CStudioHdr studioHdr( g_BenchModels[currentRun][model].pStudioHdr, g_pMDLCache );
+ AdvanceFrame( &studioHdr, currentRun, model, g_EngineStats.GetCurrentSystemFrameTime() );
+ }
+
+ g_pMaterialSystem->SwapBuffers();
+
+#ifdef USE_VPROF
+ g_VProfCurrentProfile.MarkFrame();
+ static bool bBeenHere = false;
+ if( !bBeenHere )
+ {
+ bBeenHere = true;
+ g_VProfCurrentProfile.Reset();
+ }
+#endif
+ currentFrame++;
+}
+
+
+//-----------------------------------------------------------------------------
+// Create the application object
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::Create()
+{
+ AppSystemInfo_t appSystems[] =
+ {
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+ { "datacache.dll", DATACACHE_INTERFACE_VERSION },
+ { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
+ { "datacache.dll", MDLCACHE_INTERFACE_VERSION },
+ { "vphysics.dll", VPHYSICS_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ g_pFileSystem = ( IFileSystem * )FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ g_pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+ g_pStudioRender = (IStudioRender*)FindSystem( STUDIO_RENDER_INTERFACE_VERSION );
+ g_pMDLCache = (IMDLCache*)FindSystem( MDLCACHE_INTERFACE_VERSION );
+
+ if ( !g_pFileSystem || !g_pMaterialSystem || !g_pStudioRender || !g_pMDLCache )
+ {
+ DisplayError( "Unable to load required library interfaces!" );
+ return false;
+ }
+
+#if defined( _X360 )
+ // vxconsole - true will block (legacy behavior)
+ XBX_InitConsoleMonitor( false );
+#endif
+
+ const char* pDLLName;
+ if ( CommandLine()->CheckParm( "-null" ) )
+ {
+ g_bUseEmptyShader = true;
+ pDLLName = "shaderapiempty.dll";
+ }
+ else
+ {
+ pDLLName = "shaderapidx9.dll";
+ }
+
+#if defined( _X360 )
+ g_pFileSystem->LoadModule( pDLLName );
+#endif
+ g_pMaterialSystem->SetShaderAPI( pDLLName );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// StudioRender...
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::SetupStudioRender( void )
+{
+ StudioRenderConfig_t config;
+ memset( &config, 0, sizeof(config) );
+
+ config.bEyeMove = true;
+ config.bTeeth = true;
+ config.bEyes = true;
+ config.bFlex = true;
+
+ config.fEyeShiftX = 0.0f;
+ config.fEyeShiftY = 0.0f;
+ config.fEyeShiftZ = 0.0f;
+ config.fEyeSize = 0.0f;
+
+ config.bNoHardware = false;
+ config.bNoSoftware = false;
+
+ config.bSoftwareSkin = false;
+ config.bSoftwareLighting = false;
+
+ config.drawEntities = true;
+ config.bWireframe = false;
+ config.bDrawNormals = false;
+ config.bDrawTangentFrame = false;
+ config.skin = 0;
+
+ config.fullbright = 0;
+
+ config.bShowEnvCubemapOnly = false;
+
+ g_pStudioRender->UpdateConfig( config );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Material system
+//-----------------------------------------------------------------------------
+bool InitMaterialSystem( HWND mainWindow )
+{
+ MaterialSystem_Config_t config;
+ if( g_WindowMode )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ }
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, IsX360() ? 0 : true );
+
+ config.m_VideoMode.m_Width = 0;
+ config.m_VideoMode.m_Height = 0;
+ config.m_VideoMode.m_Format = IMAGE_FORMAT_BGRX8888;
+ config.m_VideoMode.m_RefreshRate = g_RefreshRate;
+ config.dxSupportLevel = IsX360() ? 98 : 0;
+
+ bool modeSet = g_pMaterialSystem->SetMode( (void*)mainWindow, config );
+ if (!modeSet)
+ {
+ DisplayError( "Unable to set mode\n" );
+ return false;
+ }
+
+ g_pMaterialSystem->OverrideConfig( config, false );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit
+//-----------------------------------------------------------------------------
+bool CIHVTestApp::PreInit( void )
+{
+ CreateInterfaceFn factory = GetFactory();
+ ConnectTier1Libraries( &factory, 1 );
+
+ // Add paths...
+ if ( !SetupSearchPaths( NULL, false, true ) )
+ {
+ Error( "Failed to setup search paths\n" );
+ return false;
+ }
+
+ const char *pArgVal;
+ if ( CommandLine()->CheckParm( "-bench" ) )
+ {
+ g_BenchMode = true;
+ }
+
+ if( !g_BenchMode && !CommandLine()->CheckParm( "-i" ) )
+ {
+ // Set some default parameters for running as a unittest
+ g_BenchMode = true;
+ g_WindowMode = IsPC() ? true : false;
+ }
+
+ if( g_BenchMode )
+ {
+ if ( CommandLine()->CheckParm( "-i", &pArgVal ) )
+ {
+ g_BenchRuns[0].pModelName[0] = pArgVal;
+ }
+ }
+
+ if( CommandLine()->CheckParm( "-softwaretl" ) )
+ {
+ g_SoftwareTL = true;
+ }
+
+ // Explicitly in window/fullscreen mode?
+ if ( CommandLine()->CheckParm( "-window") )
+ {
+ g_WindowMode = true;
+ }
+ else if ( CommandLine()->CheckParm( "-fullscreen" ) )
+ {
+ g_WindowMode = false;
+ }
+
+ /* figure out g_Renderwidth and g_RenderHeight */
+ g_RenderWidth = -1;
+ g_RenderHeight = -1;
+
+ if( CommandLine()->CheckParm( "-width", &pArgVal ) )
+ {
+ g_RenderWidth = atoi( pArgVal );
+ }
+ if( CommandLine()->CheckParm( "-height", &pArgVal ) )
+ {
+ g_RenderHeight = atoi( pArgVal );
+ }
+
+ if( g_RenderWidth == -1 && g_RenderHeight == -1 )
+ {
+ g_RenderWidth = 640;
+ g_RenderHeight = 480;
+ }
+ else if( g_RenderWidth != -1 && g_RenderHeight == -1 )
+ {
+ switch( g_RenderWidth )
+ {
+ case 320:
+ g_RenderHeight = 240;
+ break;
+ case 512:
+ g_RenderHeight = 384;
+ break;
+ case 640:
+ g_RenderHeight = 480;
+ break;
+ case 800:
+ g_RenderHeight = 600;
+ break;
+ case 1024:
+ g_RenderHeight = 768;
+ break;
+ case 1280:
+ g_RenderHeight = 1024;
+ break;
+ case 1600:
+ g_RenderHeight = 1200;
+ break;
+ default:
+ DisplayError( "Can't figure out window dimensions!!" );
+ exit( -1 );
+ break;
+ }
+ }
+
+ if( g_RenderWidth == -1 || g_RenderHeight == -1 )
+ {
+ DisplayError( "Can't figure out window dimensions!!" );
+ exit( -1 );
+ }
+
+ int windowWidth, windowHeight;
+ CalcWindowSize( g_RenderWidth, g_RenderHeight, &windowWidth, &windowHeight );
+
+ if( !CreateAppWindow( "ihvtest1", windowWidth, windowHeight ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+void CIHVTestApp::PostShutdown()
+{
+ DisconnectTier1Libraries();
+}
+
+
+//-----------------------------------------------------------------------------
+// The application main loop
+//-----------------------------------------------------------------------------
+int CIHVTestApp::Main()
+{
+ SpewOutputFunc( IHVTestSpewFunc );
+
+ if ( !SetupStudioRender() )
+ {
+ return 0;
+ }
+
+ if ( !InitMaterialSystem( m_hWnd ) )
+ {
+ return 0;
+ }
+
+#if !defined( _X360 ) // X360TBD:
+extern void Sys_InitFloatTime( void ); //garymcthack
+ Sys_InitFloatTime();
+#endif
+
+ LoadModels();
+
+#if USE_VTUNE
+ VTResume();
+#endif
+#ifdef USE_VPROF
+ g_VProfCurrentProfile.Start();
+#endif
+
+ bool m_bExitMainLoop = false;
+ while (!m_bExitMainLoop && !g_BenchFinished)
+ {
+ AppPumpMessages();
+ RenderFrame();
+ }
+
+#ifdef USE_VPROF
+ g_VProfCurrentProfile.Stop();
+#endif
+ g_IHVTestFP = fopen( "ihvtest_vprof.txt", "w" );
+#ifdef USE_VPROF
+ SpewOutputFunc( IHVTestVProfSpewFunc );
+ g_VProfCurrentProfile.OutputReport( VPRT_SUMMARY );
+ g_VProfCurrentProfile.OutputReport( VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY );
+ fclose( g_IHVTestFP );
+ SpewOutputFunc( IHVTestSpewFunc );
+#endif
+#if USE_VTUNE
+ VTPause();
+#endif
+
+ return 0;
+}
+
diff --git a/unittests/ihvtest1/ihvtest1.vpc b/unittests/ihvtest1/ihvtest1.vpc
new file mode 100644
index 0000000..80731ca
--- /dev/null
+++ b/unittests/ihvtest1/ihvtest1.vpc
@@ -0,0 +1,106 @@
+//-----------------------------------------------------------------------------
+// IHVTEST1.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\game\shared"
+ $PreprocessorDefinitions "$BASE;IHVTEST"
+ }
+}
+
+$Project "ihvtest1"
+{
+ $Folder "Source Files"
+ {
+ $File "$SRCDIR\public\bone_setup.cpp"
+ $File "$SRCDIR\public\collisionutils.cpp"
+ $File "ihvtest1.cpp"
+ $File "$SRCDIR\public\studio.cpp"
+ $File "sys_clock.cpp" [$WIN32]
+ }
+
+ $Folder "Public Header Files"
+ {
+ $File "$SRCDIR\public\mathlib\amd3dx.h"
+ $File "$SRCDIR\public\basehandle.h"
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\public\bitvec.h"
+ $File "$SRCDIR\public\bone_accessor.h"
+ $File "$SRCDIR\public\bone_setup.h"
+ $File "$SRCDIR\public\bspflags.h"
+ $File "$SRCDIR\public\clientstats.h"
+ $File "$SRCDIR\public\cmodel.h"
+ $File "$SRCDIR\public\CollisionUtils.h"
+ $File "$SRCDIR\public\tier0\commonmacros.h"
+ $File "$SRCDIR\public\mathlib\compressed_vector.h"
+ $File "$SRCDIR\public\const.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "$SRCDIR\public\tier0\fasttimer.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\gametrace.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\tier0\icommandline.h"
+ $File "$SRCDIR\public\ihandleentity.h"
+ $File "$SRCDIR\public\materialsystem\imaterialproxyfactory.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystem.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystemhardwareconfig.h"
+ $File "$SRCDIR\public\tier1\interface.h"
+ $File "$SRCDIR\public\istudiorender.h"
+ $File "$SRCDIR\public\materialsystem\materialsystem_config.h"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "$SRCDIR\public\tier0\mem.h"
+ $File "$SRCDIR\public\tier0\memalloc.h"
+ $File "$SRCDIR\public\tier0\memdbgon.h"
+ $File "$SRCDIR\public\tier0\platform.h"
+ $File "$SRCDIR\public\tier0\protected_things.h"
+ $File "$SRCDIR\public\vstdlib\random.h"
+ $File "$SRCDIR\public\string_t.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlrbtree.h"
+ $File "$SRCDIR\public\tier1\utlsymbol.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\vcollide.h"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\mathlib\vector4d.h"
+ $File "$SRCDIR\public\tier0\vprof.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ }
+
+ $Folder "Build Bat Files" [$0]
+ {
+ $File "copybin.bat"
+ $File "copycommonsrc.bat"
+ $File "copydx8.bat"
+ $File "copygamesharedsrc.bat"
+ $File "copyihvtestsrc.bat"
+ $File "copylib.bat"
+ $File "copymatsyssrc.bat"
+ $File "copypublicsrc.bat"
+ $File "copyshaderdx8src.bat"
+ $File "copysrc.bat"
+ $File "copystudiorendersrc.bat"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib mathlib
+ $Lib $LIBCOMMON\vtuneapi [$WIN32&&!$VS2015]
+ $Lib $SRCDIR\lib\common\vtuneapi [$WIN32&&$VS2015]
+ }
+}
diff --git a/unittests/ihvtest1/sys_clock.cpp b/unittests/ihvtest1/sys_clock.cpp
new file mode 100644
index 0000000..4412ed8
--- /dev/null
+++ b/unittests/ihvtest1/sys_clock.cpp
@@ -0,0 +1,254 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <assert.h>
+
+#pragma optimize( "", off )
+
+#pragma pack( push, thing )
+#pragma pack( 4 )
+static long g_cw, g_single_cw, g_highchop_cw, g_full_cw, g_ceil_cw, g_pushed_cw;
+static struct
+{
+ long dummy[8];
+} g_fpenv;
+#pragma pack( pop, thing )
+
+
+void __declspec ( naked ) MaskExceptions()
+{
+ _asm
+ {
+ fnstenv ds:dword ptr[g_fpenv]
+ or ds:dword ptr[g_fpenv],03Fh
+ fldenv ds:dword ptr[g_fpenv]
+ ret
+ }
+}
+
+void __declspec ( naked ) Sys_SetFPCW()
+{
+ _asm
+ {
+ fnstcw ds:word ptr[g_cw]
+ mov eax,ds:dword ptr[g_cw]
+ and ah,0F0h
+ or ah,003h
+ mov ds:dword ptr[g_full_cw],eax
+ mov ds:dword ptr[g_highchop_cw],eax
+ and ah,0F0h
+ or ah,00Ch
+ mov ds:dword ptr[g_single_cw],eax
+ and ah,0F0h
+ or ah,008h
+ mov ds:dword ptr[g_ceil_cw],eax
+ ret
+ }
+}
+
+void __declspec ( naked ) Sys_PushFPCW_SetHigh()
+{
+ _asm
+ {
+ fnstcw ds:word ptr[g_pushed_cw]
+ fldcw ds:word ptr[g_full_cw]
+ ret
+ }
+}
+
+void __declspec ( naked ) Sys_PopFPCW()
+{
+ _asm
+ {
+ fldcw ds:word ptr[g_pushed_cw]
+ ret
+ }
+}
+
+#pragma optimize( "", on )
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements high precision clock
+// TODO: Make into an interface?
+//-----------------------------------------------------------------------------
+class CSysClock
+{
+public:
+ // Construction
+ CSysClock( void );
+
+ // Initialization
+ void Init( void );
+ void SetStartTime( void );
+
+ // Sample the clock
+ double GetTime( void );
+
+private:
+ // High performance clock frequency
+ double m_dClockFrequency;
+ // Current accumulated time
+ double m_dCurrentTime;
+ // How many bits to shift raw 64 bit sample count by
+ int m_nTimeSampleShift;
+ // Previous 32 bit sample count
+ unsigned int m_uiPreviousTime;
+
+ bool m_bInitialized;
+};
+
+static CSysClock g_Clock;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CSysClock::CSysClock( void )
+{
+ m_bInitialized = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the clock
+//-----------------------------------------------------------------------------
+void CSysClock::Init( void )
+{
+ BOOL success;
+ LARGE_INTEGER PerformanceFreq;
+ unsigned int lowpart, highpart;
+
+ MaskExceptions ();
+ Sys_SetFPCW ();
+
+ // Start clock at zero
+ m_dCurrentTime = 0.0;
+
+ success = QueryPerformanceFrequency( &PerformanceFreq );
+ assert( success );
+
+ // get 32 out of the 64 time bits such that we have around
+ // 1 microsecond resolution
+ lowpart = (unsigned int)PerformanceFreq.LowPart;
+ highpart = (unsigned int)PerformanceFreq.HighPart;
+
+ m_nTimeSampleShift = 0;
+
+ while ( highpart || ( lowpart > 2000000.0 ) )
+ {
+ m_nTimeSampleShift++;
+ lowpart >>= 1;
+ lowpart |= (highpart & 1) << 31;
+ highpart >>= 1;
+ }
+
+ m_dClockFrequency = 1.0 / (double)lowpart;
+
+ // Get initial sample
+ unsigned int temp;
+ LARGE_INTEGER PerformanceCount;
+ QueryPerformanceCounter( &PerformanceCount );
+ if ( !m_nTimeSampleShift )
+ {
+ temp = (unsigned int)PerformanceCount.LowPart;
+ }
+ else
+ {
+ // Rotate counter to right by m_nTimeSampleShift places
+ temp = ((unsigned int)PerformanceCount.LowPart >> m_nTimeSampleShift) |
+ ((unsigned int)PerformanceCount.HighPart << (32 - m_nTimeSampleShift));
+ }
+
+ // Set first time stamp
+ m_uiPreviousTime = temp;
+
+ m_bInitialized = true;
+
+ SetStartTime();
+}
+
+void CSysClock::SetStartTime( void )
+{
+ GetTime();
+
+ m_dCurrentTime = 0.0;
+
+ m_uiPreviousTime = ( unsigned int )m_dCurrentTime;
+}
+
+double CSysClock::GetTime( void )
+{
+ LARGE_INTEGER PerformanceCount;
+ unsigned int temp, t2;
+ double time;
+
+ if ( !m_bInitialized )
+ {
+ return 0.0;
+ }
+
+ Sys_PushFPCW_SetHigh();
+
+ // Get sample counter
+ QueryPerformanceCounter( &PerformanceCount );
+
+ if ( !m_nTimeSampleShift )
+ {
+ temp = (unsigned int)PerformanceCount.LowPart;
+ }
+ else
+ {
+ // Rotate counter to right by m_nTimeSampleShift places
+ temp = ((unsigned int)PerformanceCount.LowPart >> m_nTimeSampleShift) |
+ ((unsigned int)PerformanceCount.HighPart << (32 - m_nTimeSampleShift));
+ }
+
+ // check for turnover or backward time
+ if ( ( temp <= m_uiPreviousTime ) &&
+ ( ( m_uiPreviousTime - temp ) < 0x10000000) )
+ {
+ m_uiPreviousTime = temp; // so we can't get stuck
+ }
+ else
+ {
+ // gap in performance clocks
+ t2 = temp - m_uiPreviousTime;
+
+ // Convert to time using frequencey of clock
+ time = (double)t2 * m_dClockFrequency;
+
+ // Remember old time
+ m_uiPreviousTime = temp;
+
+ // Increment clock
+ m_dCurrentTime += time;
+ }
+
+ Sys_PopFPCW();
+
+ // Convert to float
+ return m_dCurrentTime;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sample the high-precision clock
+// Output : double
+//-----------------------------------------------------------------------------
+double Sys_FloatTime( void )
+{
+ return g_Clock.GetTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize high-precision clock
+//-----------------------------------------------------------------------------
+void Sys_InitFloatTime( void )
+{
+ g_Clock.Init();
+}
diff --git a/unittests/inputtest/inputtest.cpp b/unittests/inputtest/inputtest.cpp
new file mode 100644
index 0000000..5b9781b
--- /dev/null
+++ b/unittests/inputtest/inputtest.cpp
@@ -0,0 +1,250 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Material editor
+//=============================================================================
+
+#include <windows.h>
+#include "appframework/tier2app.h"
+#include "inputsystem/iinputsystem.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Warning/Msg call back through this API
+// Input : type -
+// *pMsg -
+// Output : SpewRetval_t
+//-----------------------------------------------------------------------------
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ OutputDebugString( pMsg );
+ return SPEW_CONTINUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CInputTestApp : public CTier2SteamApp
+{
+ typedef CTier2SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown( );
+ virtual void Destroy();
+ virtual const char *GetAppName() { return "InputTest"; }
+ virtual bool AppUsesReadPixels() { return false; }
+
+private:
+ // Window management
+ bool CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h );
+
+ // Sets up the game path
+ bool SetupSearchPaths();
+
+ HWND m_HWnd;
+};
+
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CInputTestApp );
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CInputTestApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ return true;
+}
+
+void CInputTestApp::Destroy()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+bool CInputTestApp::CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = DefWindowProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ m_HWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if (!m_HWnd)
+ return false;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos (m_HWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CInputTestApp::SetupSearchPaths()
+{
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ g_pFullFileSystem->AddSearchPath( GetGameInfoPath(), "SKIN", PATH_ADD_TO_HEAD );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CInputTestApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if (!g_pFullFileSystem || !g_pInputSystem )
+ return false;
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ if (!CreateAppWindow( "InputTest", bWindowed, iWidth, iHeight ))
+ return false;
+
+ g_pInputSystem->AttachToWindow( m_HWnd );
+ return true;
+}
+
+void CInputTestApp::PostShutdown( )
+{
+ g_pInputSystem->DetachFromWindow( );
+ BaseClass::PostShutdown();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CInputTestApp::Main()
+{
+ while( true )
+ {
+ g_pInputSystem->PollInputState();
+
+ int nEventCount = g_pInputSystem->GetEventCount();
+ const InputEvent_t* pEvents = g_pInputSystem->GetEventData( );
+ for ( int i = 0; i < nEventCount; ++i )
+ {
+ const InputEvent_t* pEvent = &pEvents[i];
+ switch( pEvent->m_nType )
+ {
+ case IE_ButtonPressed:
+ Msg("Button Pressed Event %d : Start tick %d\n", pEvent->m_nData, pEvent->m_nTick );
+ break;
+
+ case IE_ButtonReleased:
+ Msg("Button Released Event %d : End tick %d Start tick %d\n", pEvent->m_nData, pEvent->m_nTick, g_pInputSystem->GetButtonPressedTick( (ButtonCode_t)pEvent->m_nData ) );
+ break;
+
+ case IE_ButtonDoubleClicked:
+ Msg("Button Double clicked Event %d : Start tick %d\n", pEvent->m_nData, pEvent->m_nTick );
+ break;
+
+ case IE_AnalogValueChanged:
+ Msg("Analog Value Changed %d : Start tick %d Value %d\n", pEvent->m_nData, pEvent->m_nTick, pEvent->m_nData2 );
+ break;
+
+ case IE_Quit:
+ Msg("Quit");
+ return 1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+
diff --git a/unittests/inputtest/inputtest.vpc b/unittests/inputtest/inputtest.vpc
new file mode 100644
index 0000000..a377b82
--- /dev/null
+++ b/unittests/inputtest/inputtest.vpc
@@ -0,0 +1,24 @@
+//-----------------------------------------------------------------------------
+// INPUTTEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Project "Inputtest"
+{
+ $Folder "Source Files"
+ {
+ $File "inputtest.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib tier2
+ }
+}
diff --git a/unittests/materialsystemtest/materialsystemtest.cpp b/unittests/materialsystemtest/materialsystemtest.cpp
new file mode 100644
index 0000000..9a193bf
--- /dev/null
+++ b/unittests/materialsystemtest/materialsystemtest.cpp
@@ -0,0 +1,492 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Material editor
+//=============================================================================
+
+#include <windows.h>
+#include "appframework/tier2app.h"
+#include "materialsystem/materialsystem_config.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "vstdlib/random.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "materialsystem/imesh.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Warning/Msg call back through this API
+// Input : type -
+// *pMsg -
+// Output : SpewRetval_t
+//-----------------------------------------------------------------------------
+SpewRetval_t SpewFunc( SpewType_t type, const char *pMsg )
+{
+ if ( Plat_IsInDebugSession() )
+ {
+ OutputDebugString( pMsg );
+ if ( type == SPEW_ASSERT )
+ return SPEW_DEBUGGER;
+ }
+ return SPEW_CONTINUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CMaterialSystemTestApp : public CTier2SteamApp
+{
+ typedef CTier2SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown( );
+ virtual void Destroy();
+ virtual const char *GetAppName() { return "MaterialSystemTest"; }
+ virtual bool AppUsesReadPixels() { return false; }
+
+private:
+ // Window management
+ bool CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h );
+
+ // Sets up the game path
+ bool SetupSearchPaths();
+
+ // Waits for a keypress
+ bool WaitForKeypress();
+
+ // Sets the video mode
+ bool SetMode();
+
+ // Tests dynamic buffers
+ void TestDynamicBuffers( IMatRenderContext *pRenderContext, bool bBuffered );
+
+ // Creates, destroys a test material
+ void CreateWireframeMaterial();
+ void DestroyMaterial();
+
+ CMaterialReference m_pMaterial;
+
+ HWND m_HWnd;
+};
+
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CMaterialSystemTestApp );
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+
+ // Required to terminate the list
+ { "", "" }
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ IMaterialSystem *pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+ if ( !pMaterialSystem )
+ {
+ Warning( "CMaterialSystemTestApp::Create: Unable to connect to necessary interface!\n" );
+ return false;
+ }
+
+ bool bIsVistaOrHigher = false;
+
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if ( GetVersionEx( &info ) )
+ {
+ bIsVistaOrHigher = info.dwMajorVersion >= 6;
+ }
+
+ const char *pShaderDLL = CommandLine()->ParmValue( "-shaderdll" );
+ if ( !pShaderDLL )
+ {
+ pShaderDLL = "shaderapidx10.dll";
+ }
+
+ if ( !bIsVistaOrHigher && !Q_stricmp( pShaderDLL, "shaderapidx10.dll" ) )
+ {
+ pShaderDLL = "shaderapidx9.dll";
+ }
+
+ pMaterialSystem->SetShaderAPI( pShaderDLL );
+ return true;
+}
+
+void CMaterialSystemTestApp::Destroy()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Window callback
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MaterialSystemTestWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+ switch( message )
+ {
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ return DefWindowProc( hWnd, message, wParam, lParam );
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = MaterialSystemTestWndProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ m_HWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if (!m_HWnd)
+ return false;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos (m_HWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::SetupSearchPaths()
+{
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ g_pFullFileSystem->AddSearchPath( GetGameInfoPath(), "SKIN", PATH_ADD_TO_HEAD );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if ( !g_pFullFileSystem || !g_pMaterialSystem )
+ return false;
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ if (!CreateAppWindow( "Press a Key To Continue", bWindowed, iWidth, iHeight ))
+ return false;
+
+ // Get the adapter from the command line....
+ const char *pAdapterString;
+ int nAdapter = 0;
+ if ( CommandLine()->CheckParm( "-adapter", &pAdapterString ) )
+ {
+ nAdapter = atoi( pAdapterString );
+ }
+
+ int nAdapterFlags = 0;
+ if ( AppUsesReadPixels() )
+ {
+ nAdapterFlags |= MATERIAL_INIT_ALLOCATE_FULLSCREEN_TEXTURE;
+ }
+
+ g_pMaterialSystem->SetAdapter( nAdapter, nAdapterFlags );
+
+ return true;
+}
+
+void CMaterialSystemTestApp::PostShutdown( )
+{
+ BaseClass::PostShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Waits for a keypress
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::WaitForKeypress()
+{
+ MSG msg = {0};
+ while( WM_QUIT != msg.message )
+ {
+ if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ if ( msg.message == WM_KEYDOWN )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the video mode
+//-----------------------------------------------------------------------------
+bool CMaterialSystemTestApp::SetMode()
+{
+ MaterialSystem_Config_t config;
+ if ( CommandLine()->CheckParm( "-fullscreen" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, false );
+ }
+ else
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ }
+
+ if ( CommandLine()->CheckParm( "-resizing" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
+ }
+
+ if ( CommandLine()->CheckParm( "-mat_vsync" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, false );
+ }
+ config.m_nAASamples = CommandLine()->ParmValue( "-mat_antialias", 1 );
+ config.m_nAAQuality = CommandLine()->ParmValue( "-mat_aaquality", 0 );
+
+ config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0;
+ config.m_VideoMode.m_Format = IMAGE_FORMAT_BGRX8888;
+ config.m_VideoMode.m_RefreshRate = 0;
+
+ bool modeSet = g_pMaterialSystem->SetMode( m_HWnd, config );
+ if (!modeSet)
+ {
+ Error( "Unable to set mode\n" );
+ return false;
+ }
+
+ g_pMaterialSystem->OverrideConfig( config, false );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates, destroys a test material
+//-----------------------------------------------------------------------------
+void CMaterialSystemTestApp::CreateWireframeMaterial()
+{
+ KeyValues *pVMTKeyValues = new KeyValues( "Wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ m_pMaterial.Init( "__test", pVMTKeyValues );
+}
+
+void CMaterialSystemTestApp::DestroyMaterial()
+{
+ m_pMaterial.Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests dynamic buffers
+//-----------------------------------------------------------------------------
+void CMaterialSystemTestApp::TestDynamicBuffers( IMatRenderContext *pMatRenderContext, bool bBuffered )
+{
+ CreateWireframeMaterial();
+
+ g_pMaterialSystem->BeginFrame( 0 );
+
+ pMatRenderContext->Bind( m_pMaterial );
+ IMesh *pMesh = pMatRenderContext->GetDynamicMesh( bBuffered );
+
+ // clear (so that we can make sure that we aren't getting results from the previous quad)
+ pMatRenderContext->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) );
+ pMatRenderContext->ClearBuffers( true, true );
+
+ static unsigned char s_pColors[4][4] =
+ {
+ { 255, 0, 0, 255 },
+ { 0, 255, 0, 255 },
+ { 0, 0, 255, 255 },
+ { 255, 255, 255, 255 },
+ };
+
+ static int nCount = 0;
+
+ const int nLoopCount = 8;
+ float flWidth = 2.0f / nLoopCount;
+ for ( int i = 0; i < nLoopCount; ++i )
+ {
+ CMeshBuilder mb;
+ mb.Begin( pMesh, MATERIAL_TRIANGLES, 4, 6 );
+
+ mb.Position3f( -1.0f + i * flWidth, -1.0f, 0.5f );
+ mb.Normal3f( 0.0f, 0.0f, 1.0f );
+ mb.Color4ubv( s_pColors[nCount++ % 4] );
+ mb.AdvanceVertex();
+
+ mb.Position3f( -1.0f + i * flWidth + flWidth, -1.0f, 0.5f );
+ mb.Normal3f( 0.0f, 0.0f, 1.0f );
+ mb.Color4ubv( s_pColors[nCount++ % 4] );
+ mb.AdvanceVertex();
+
+ mb.Position3f( -1.0f + i * flWidth + flWidth, 1.0f, 0.5f );
+ mb.Normal3f( 0.0f, 0.0f, 1.0f );
+ mb.Color4ubv( s_pColors[nCount++ % 4] );
+ mb.AdvanceVertex();
+
+ mb.Position3f( -1.0f + i * flWidth, 1.0f, 0.5f );
+ mb.Normal3f( 0.0f, 0.0f, 1.0f );
+ mb.Color4ubv( s_pColors[nCount++ % 4] );
+ mb.AdvanceVertex();
+
+ ++nCount;
+
+ mb.FastIndex( 0 );
+ mb.FastIndex( 2 );
+ mb.FastIndex( 1 );
+ mb.FastIndex( 0 );
+ mb.FastIndex( 3 );
+ mb.FastIndex( 2 );
+
+ mb.End( true );
+
+ pMesh->Draw( );
+ }
+
+ ++nCount;
+
+ g_pMaterialSystem->EndFrame();
+ g_pMaterialSystem->SwapBuffers();
+
+ DestroyMaterial();
+}
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CMaterialSystemTestApp::Main()
+{
+ if ( !SetMode() )
+ return 0;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ // Sets up a full-screen viewport
+ int w, h;
+ pRenderContext->GetWindowSize( w, h );
+ pRenderContext->Viewport( 0, 0, w, h );
+ pRenderContext->DepthRange( 0.0f, 1.0f );
+
+ // Clears the screen
+ g_pMaterialSystem->BeginFrame( 0 );
+ pRenderContext->ClearColor4ub( 76, 88, 68, 255 );
+ pRenderContext->ClearBuffers( true, true );
+ g_pMaterialSystem->EndFrame();
+ g_pMaterialSystem->SwapBuffers();
+
+ SetWindowText( m_HWnd, "Buffer clearing . . hit a key" );
+ if ( !WaitForKeypress() )
+ return 1;
+
+ SetWindowText( m_HWnd, "Dynamic buffer test.. hit a key" );
+ TestDynamicBuffers( pRenderContext, false );
+ if ( !WaitForKeypress() )
+ return 1;
+
+ SetWindowText( m_HWnd, "Buffered dynamic buffer test.. hit a key" );
+ TestDynamicBuffers( pRenderContext, true );
+ if ( !WaitForKeypress() )
+ return 1;
+
+ return 1;
+} \ No newline at end of file
diff --git a/unittests/materialsystemtest/materialsystemtest.vpc b/unittests/materialsystemtest/materialsystemtest.vpc
new file mode 100644
index 0000000..5c3cbbe
--- /dev/null
+++ b/unittests/materialsystemtest/materialsystemtest.vpc
@@ -0,0 +1,24 @@
+//-----------------------------------------------------------------------------
+// MATERIALSYSTEMTEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Project "MaterialSystemTest"
+{
+ $Folder "Source Files"
+ {
+ $File "materialsystemtest.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib tier2
+ }
+}
diff --git a/unittests/mathlib_test/mathlib_test.cpp b/unittests/mathlib_test/mathlib_test.cpp
new file mode 100644
index 0000000..c3c1191
--- /dev/null
+++ b/unittests/mathlib_test/mathlib_test.cpp
@@ -0,0 +1,79 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "tier0/platform.h"
+#include "mathlib/mathlib.h"
+#include "mathlib/spherical_geometry.h"
+#include "tier2/tier2.h"
+#include "mathlib/halton.h"
+#include "bitmap/float_bm.h"
+#include "tier0/memdbgon.h"
+
+void main(int argc,char **argv)
+{
+ InitCommandLineProgram( argc, argv );
+
+ // 1/8th of the sphere
+ float a1=UnitSphereTriangleArea( Vector( 1, 0, 0 ), Vector( 0, 0, -1 ), Vector( 0, 1, 0 ) );
+ printf( "right spherical triangle projected percentage=%2.4f\n", a1 / ( 4 * M_PI ));
+
+ // a small one
+ Vector v1 = Vector( 1, 0, 0 );
+ Vector v2 = v1 + Vector( 0, 0.2, 0 );
+ Vector v3 = v1 + Vector( 0, 0, 0.2 );
+ v2.NormalizeInPlace();
+ v3.NormalizeInPlace();
+ float a2=UnitSphereTriangleArea( v1, v2, v3 );
+ printf( "small spherical triangle projected percentage=%2.5f\n", a2 / ( 4* M_PI ) );
+
+ // now, create a cubemap and sum the area of each of its cells
+ FloatCubeMap_t envMap( 10, 10 );
+ float flAreaSum = 0.;
+ for( int nFace = 0 ; nFace < 6; nFace ++ )
+ {
+ for( int nY = 0 ; nY < 9; nY++ )
+ for( int nX = 0 ; nX < 9; nX++ )
+ {
+ Vector v00 = envMap.PixelDirection( nFace, nX, nY );
+ Vector v01 = envMap.PixelDirection( nFace, nX, nY + 1 );
+ Vector v10 = envMap.PixelDirection( nFace, nX + 1, nY );
+ Vector v11 = envMap.PixelDirection( nFace, nX + 1 , nY + 1 );
+ v00.NormalizeInPlace();
+ v01.NormalizeInPlace();
+ v10.NormalizeInPlace();
+ v11.NormalizeInPlace();
+ flAreaSum += UnitSphereTriangleArea( v00, v01, v10 );
+ flAreaSum += UnitSphereTriangleArea( v10, v11, v01 );
+ }
+ }
+ printf( "sum of areas of cubemap cells = %2.2f\n", flAreaSum / ( 4.0 * M_PI ) );
+
+#if 0 // visual spherical harmonics as (confusing) point sets
+ // spherical harmonics
+ DirectionalSampler_t sampler;
+ for(int i = 0 ; i < 50000; i++ )
+ {
+ Vector dir=sampler.NextValue();
+ float SH = SphericalHarmonic( 4, 3, dir );
+ float r=0;
+ float g=1; //0.5+0.5*DotProduct( dir, Vector( 0, 0, 1 ) );
+ float b=0;
+ if ( SH < 0 )
+ {
+ SH = -SH;
+ r=g;
+ g=0;
+ }
+ r *= SH;
+ g *= SH;
+ b *= SH;
+ float rad= SH * 4.0; //4.0; //SH *= 8.0;
+ printf( "2\n" );
+ printf( "%f %f %f %f %f %f\n",
+ dir.x * rad, dir.y * rad, dir.z * rad, r, g, b );
+ rad += 0.03;
+ printf( "%f %f %f %f %f %f\n",
+ dir.x * rad, dir.y * rad, dir.z * rad, r, g, b );
+ }
+#endif
+
+}
+
diff --git a/unittests/mathlib_test/mathlib_test.vpc b/unittests/mathlib_test/mathlib_test.vpc
new file mode 100644
index 0000000..32dd5f3
--- /dev/null
+++ b/unittests/mathlib_test/mathlib_test.vpc
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------------
+// mathlib_test.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\unittests\mathlib_test"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration "Debug"
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE winmm.lib"
+ }
+}
+
+$Project "mathlib_test"
+{
+ $Folder "Source Files"
+ {
+ $File "mathlib_test.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib bitmap
+ $Lib mathlib
+ $Lib tier2
+ }
+}
diff --git a/unittests/networktest/networktest.cpp b/unittests/networktest/networktest.cpp
new file mode 100644
index 0000000..5e8c9a4
--- /dev/null
+++ b/unittests/networktest/networktest.cpp
@@ -0,0 +1,395 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Material editor
+//=============================================================================
+
+#include <windows.h>
+#include "appframework/appframework.h"
+#include "networksystem/inetworksystem.h"
+#include "networksystem/inetworkmessage.h"
+#include "bitbuf.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+#include "vstdlib/cvar.h"
+
+
+//-----------------------------------------------------------------------------
+// Singleton interfaces
+//-----------------------------------------------------------------------------
+IFileSystem *g_pFileSystem;
+INetworkSystem *g_pNetworkSystem;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Warning/Msg call back through this API
+// Input : type -
+// *pMsg -
+// Output : SpewRetval_t
+//-----------------------------------------------------------------------------
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ OutputDebugString( pMsg );
+ if ( type == SPEW_ASSERT )
+ {
+ DebuggerBreak();
+ }
+ return SPEW_CONTINUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CNetworkTestApp : public CSteamAppSystemGroup
+{
+ typedef CSteamAppSystemGroup BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown( );
+ virtual void Destroy();
+ virtual const char *GetAppName() { return "NetworkTest"; }
+ virtual bool AppUsesReadPixels() { return false; }
+
+private:
+ // Window management
+ bool CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h );
+
+ // Sets up the game path
+ bool SetupSearchPaths();
+
+ HWND m_HWnd;
+};
+
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CNetworkTestApp );
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CNetworkTestApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+
+ // Add in the cvar factory
+ AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
+ AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "networksystem.dll", NETWORKSYSTEM_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ g_pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ g_pNetworkSystem = (INetworkSystem*)FindSystem( NETWORKSYSTEM_INTERFACE_VERSION );
+
+ if (!g_pFileSystem || !g_pNetworkSystem )
+ return false;
+
+ return true;
+}
+
+void CNetworkTestApp::Destroy()
+{
+ g_pFileSystem = NULL;
+ g_pNetworkSystem = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+bool CNetworkTestApp::CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = DefWindowProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ m_HWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if (!m_HWnd)
+ return false;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos (m_HWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CNetworkTestApp::SetupSearchPaths()
+{
+ CFSSteamSetupInfo steamInfo;
+ steamInfo.m_pDirectoryName = NULL;
+ steamInfo.m_bOnlyUseDirectoryName = false;
+ steamInfo.m_bToolsMode = true;
+ steamInfo.m_bSetSteamDLLPath = true;
+ steamInfo.m_bSteam = g_pFileSystem->IsSteam();
+ if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
+ return false;
+
+ CFSMountContentInfo fsInfo;
+ fsInfo.m_pFileSystem = g_pFileSystem;
+ fsInfo.m_bToolsMode = true;
+ fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
+
+ if ( FileSystem_MountContent( fsInfo ) != FS_OK )
+ return false;
+
+ // Finally, load the search paths for the "GAME" path.
+ CFSSearchPathsInit searchPathsInit;
+ searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
+ searchPathsInit.m_pFileSystem = g_pFileSystem;
+ if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
+ return false;
+
+ g_pFileSystem->AddSearchPath( steamInfo.m_GameInfoPath, "SKIN", PATH_ADD_TO_HEAD );
+
+ char platform[MAX_PATH];
+ Q_strncpy( platform, steamInfo.m_GameInfoPath, MAX_PATH );
+ Q_StripTrailingSlash( platform );
+ Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
+
+ g_pFileSystem->AddSearchPath( platform, "PLATFORM" );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CNetworkTestApp::PreInit( )
+{
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ if (!CreateAppWindow( "NetworkTest", bWindowed, iWidth, iHeight ))
+ return false;
+
+ return true;
+}
+
+void CNetworkTestApp::PostShutdown( )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Network message ids
+//-----------------------------------------------------------------------------
+enum
+{
+ TEST_GROUP = NETWORKSYSTEM_FIRST_GROUP,
+};
+
+enum
+{
+ TEST_MESSAGE_1 = 0,
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Test network message
+//-----------------------------------------------------------------------------
+class CTestNetworkMessage : public CNetworkMessage
+{
+public:
+ CTestNetworkMessage() { SetReliable( false ); }
+ CTestNetworkMessage( int nValue ) : m_Data( nValue ) { SetReliable( false ); }
+
+ DECLARE_BASE_MESSAGE( TEST_GROUP, TEST_MESSAGE_1, "Test Message 1" )
+
+ bool Process();
+
+ int m_Data;
+};
+
+bool CTestNetworkMessage::WriteToBuffer( bf_write &buffer )
+{
+ buffer.WriteShort( m_Data );
+ return !buffer.IsOverflowed();
+}
+
+bool CTestNetworkMessage::ReadFromBuffer( bf_read &buffer )
+{
+ m_Data = buffer.ReadShort();
+ return !buffer.IsOverflowed();
+}
+
+bool CTestNetworkMessage::Process()
+{
+ Msg( "Received test message %d\n", m_Data );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CNetworkTestApp::Main()
+{
+ // Network messages must be registered before the server or client is started
+ g_pNetworkSystem->RegisterMessage( new CTestNetworkMessage() );
+
+ int nRetVal = 0;
+ if ( !g_pNetworkSystem->StartServer( ) )
+ return 0;
+
+ if ( !g_pNetworkSystem->StartClient( ) )
+ goto shutdownServer;
+
+ // Set the channel up for receiving
+ INetChannel *pChan = g_pNetworkSystem->ConnectClientToServer( "localhost", 27001 );
+ if ( !pChan )
+ goto shutdownClient;
+
+ INetChannel *pServerChan = NULL;
+
+ {
+ while( true )
+ {
+ // Helps avoid a buffer overflow
+ Sleep( 1 );
+
+ // Send a message out
+ if ( pChan->GetConnectionState() == CONNECTION_STATE_CONNECTED )
+ {
+ CTestNetworkMessage msg( 5 );
+ pChan->AddNetMsg( &msg, false );
+ msg.m_Data = 4;
+ pChan->AddNetMsg( &msg, false );
+ }
+
+ if ( pServerChan )
+ {
+ CTestNetworkMessage msg( 6 );
+ pServerChan->AddNetMsg( &msg, false );
+ msg.m_Data = 7;
+ pServerChan->AddNetMsg( &msg, false );
+ }
+
+ g_pNetworkSystem->ClientSendMessages();
+ g_pNetworkSystem->ServerReceiveMessages();
+ g_pNetworkSystem->ServerSendMessages();
+ g_pNetworkSystem->ClientReceiveMessages();
+
+ NetworkEvent_t *pEvent = g_pNetworkSystem->FirstNetworkEvent();
+ for ( ; pEvent; pEvent = g_pNetworkSystem->NextNetworkEvent( ) )
+ {
+ switch ( pEvent->m_nType )
+ {
+ case NETWORK_EVENT_CONNECTED:
+ pServerChan = ((NetworkConnectionEvent_t*)pEvent)->m_pChannel;
+ break;
+
+ case NETWORK_EVENT_DISCONNECTED:
+ if ( pServerChan == ((NetworkDisconnectionEvent_t*)pEvent)->m_pChannel )
+ {
+ pServerChan = NULL;
+ }
+ break;
+
+ case NETWORK_EVENT_MESSAGE_RECEIVED:
+ {
+ NetworkMessageReceivedEvent_t *pReceivedEvent = static_cast<NetworkMessageReceivedEvent_t*>( pEvent );
+ if ( ( pReceivedEvent->m_pNetworkMessage->GetGroup() == TEST_GROUP ) && ( pReceivedEvent->m_pNetworkMessage->GetType() == TEST_MESSAGE_1 ) )
+ {
+ static_cast<CTestNetworkMessage*>( pReceivedEvent->m_pNetworkMessage )->Process();
+ }
+ }
+ break;
+ }
+ }
+ }
+ nRetVal = 1;
+
+ g_pNetworkSystem->DisconnectClientFromServer( pChan );
+ }
+
+shutdownClient:
+ g_pNetworkSystem->ShutdownClient( );
+
+shutdownServer:
+ g_pNetworkSystem->ShutdownServer( );
+
+ return nRetVal;
+}
+
+
+
diff --git a/unittests/networktest/networktest.vpc b/unittests/networktest/networktest.vpc
new file mode 100644
index 0000000..27edf6b
--- /dev/null
+++ b/unittests/networktest/networktest.vpc
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+// NETWORKTEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_win_win32_base.vpc"
+
+$Project "Networktest"
+{
+ $Folder "Source Files"
+ {
+ $File "networktest.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $DynamicFile "$SRCDIR\lib\public\appframework.lib"
+ }
+}
diff --git a/unittests/rt_test/gwolf.tga b/unittests/rt_test/gwolf.tga
new file mode 100644
index 0000000..b7af1d9
--- /dev/null
+++ b/unittests/rt_test/gwolf.tga
Binary files differ
diff --git a/unittests/rt_test/rt_test.cpp b/unittests/rt_test/rt_test.cpp
new file mode 100644
index 0000000..c9399e8
--- /dev/null
+++ b/unittests/rt_test/rt_test.cpp
@@ -0,0 +1,87 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "tier0/platform.h"
+#include "tier0/progressbar.h"
+#include "bitmap/float_bm.h"
+#include "mathlib/mathlib.h"
+#include "tier2/tier2.h"
+#include "tier0/memdbgon.h"
+#include "raytrace.h"
+#include "bitmap/tgawriter.h"
+
+void main(int argc,char **argv)
+{
+ InitCommandLineProgram( argc, argv );
+
+ if (argc != 5)
+ {
+ printf("format is 'rt_test src_image dest_image xsize ysize'\n");
+ }
+ else
+ {
+ ReportProgress("reading src texture",0,0);
+ FloatBitMap_t src_texture(argv[1]);
+ int xsize = atoi( argv[3] );
+ int ysize = atoi( argv[4] );
+
+ // render a simple scene of a terrain, using a bitmap for color data and its alpha channel for the height
+ RayTracingEnvironment rt_Env;
+ int id = 1;
+ float flXScale = (1.0/(src_texture.Width-1) );
+ float flZScale = (1.0/(src_texture.Height-1) );
+ for( int y=0; y < src_texture.Height-1; y++ )
+ for(int x=0 ; x < src_texture.Width-1; x++ )
+ {
+ Vector vecVerts[2][2];
+ for(int iy=0 ; iy < 2; iy++)
+ for(int ix=0 ; ix < 2; ix++)
+ {
+ vecVerts[ix][iy].x = 2.0* ( ( x+ix )*flXScale-0.5 );
+ if ( ( x+ix == src_texture.Width-1 ) || ( y+iy==src_texture.Height-1 ) )
+ vecVerts[ix][iy].y = 0;
+ else
+ vecVerts[ix][iy].y = 0.3*src_texture.Pixel( x+ix, y+iy, 1 );
+ vecVerts[ix][iy].z = -2.0* ( ( y+iy )*flZScale-0.5 );
+ }
+ Vector vecColor( GammaToLinear(src_texture.Pixel(x,y,0)),
+ GammaToLinear( src_texture.Pixel( x, y, 1 )),
+ GammaToLinear( src_texture.Pixel( x, y, 2 )) );
+ rt_Env.AddTriangle( id++, vecVerts[0][0], vecVerts[1][0], vecVerts[1][1], vecColor );
+ rt_Env.AddTriangle( id++, vecVerts[0][0], vecVerts[0][1], vecVerts[1][1], vecColor );
+ }
+ rt_Env.AddTriangle( id++, Vector(0,0,-.2), Vector(.2,0,.2), Vector( -.2,0,.2), Vector( 0,0,1 ) );
+ printf("n triangles %d\n",id);
+ ReportProgress("Creating kd-tree",0,0);
+ float stime = Plat_FloatTime();
+ rt_Env.SetupAccelerationStructure();
+ printf("kd built time := %d\n", (int) ( Plat_FloatTime() - stime ) );
+ rt_Env.AddInfinitePointLight( Vector( 0,5, 0), Vector( .1,.1,.1 ));
+ // lets render a frame
+ uint32 *buf=reinterpret_cast<uint32 *> ( MemAlloc_AllocAligned( xsize * ysize * 4 , 16 ) );
+
+ Vector EyePos(0,2,0);
+ ReportProgress("Rendering",0,0);
+
+// rt_Env.RenderScene( xsize, ysize, xsize, buf, Vector( 0, 0.5, -1.0 ),
+// Vector( -1, 1, 0),
+// Vector( 1, 1, 0 ),
+// Vector( -1, -1, 0 ),
+// Vector( 1, -1, 0 ) );
+ float curtime = Plat_FloatTime();
+ for(int i=0;i<10;i++)
+ {
+ rt_Env.RenderScene( xsize, ysize, xsize, buf,
+ EyePos,
+ Vector( -1, 0,1)-EyePos,
+ Vector( 1, 0, 1 )-EyePos,
+ Vector( -1, 0, -1 )-EyePos,
+ Vector( 1, 0, -1 )-EyePos );
+ }
+ float etime=Plat_FloatTime()-curtime;
+ printf("pixels traced and lit per second := %f\n",(10*xsize*ysize)*(1.0/etime));
+ TGAWriter::WriteTGAFile( "test.tga", xsize, ysize, IMAGE_FORMAT_RGBA8888,
+ reinterpret_cast<uint8 *> (buf), 4*xsize );
+
+ MemAlloc_FreeAligned( buf );
+ }
+
+}
diff --git a/unittests/rt_test/rt_test.exe b/unittests/rt_test/rt_test.exe
new file mode 100644
index 0000000..afda31c
--- /dev/null
+++ b/unittests/rt_test/rt_test.exe
Binary files differ
diff --git a/unittests/rt_test/rt_test.pdb b/unittests/rt_test/rt_test.pdb
new file mode 100644
index 0000000..6928711
--- /dev/null
+++ b/unittests/rt_test/rt_test.pdb
Binary files differ
diff --git a/unittests/rt_test/rt_test.vpc b/unittests/rt_test/rt_test.vpc
new file mode 100644
index 0000000..07a9d6b
--- /dev/null
+++ b/unittests/rt_test/rt_test.vpc
@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------------
+// rt_test.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\unittests\rt_test"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration "Debug"
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE winmm.lib"
+ }
+}
+
+$Project "rt_test"
+{
+ $Folder "Source Files"
+ {
+ $File "rt_test.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib bitmap
+ $Lib mathlib
+ $Lib raytrace
+ $Lib tier2
+ }
+}
diff --git a/unittests/shaderapitest/shaderapitest.cpp b/unittests/shaderapitest/shaderapitest.cpp
new file mode 100644
index 0000000..2f94b2f
--- /dev/null
+++ b/unittests/shaderapitest/shaderapitest.cpp
@@ -0,0 +1,1056 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Material editor
+//=============================================================================
+
+#include <windows.h>
+#include "appframework/tier2app.h"
+#include "shaderapi/ishaderdevice.h"
+#include "shaderapi/ishaderutil.h"
+#include "shaderapi/ishaderapi.h"
+#include "materialsystem/materialsystem_config.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "vstdlib/random.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/lzmadecoder.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/shader_vcs_version.h"
+#include "../utils/bzip2/bzlib.h"
+
+
+class CShaderUtilTemp : public CBaseAppSystem< IShaderUtil >
+{
+public:
+ // Method to allow clients access to the MaterialSystem_Config
+ virtual MaterialSystem_Config_t& GetConfig()
+ {
+ static MaterialSystem_Config_t config;
+ return config;
+ }
+
+ // Allows us to convert image formats
+ virtual bool ConvertImageFormat( unsigned char *src, enum ImageFormat srcImageFormat,
+ unsigned char *dst, enum ImageFormat dstImageFormat,
+ int width, int height, int srcStride = 0, int dstStride = 0 )
+ {
+ return true;
+ }
+
+ // Figures out the amount of memory needed by a bitmap
+ virtual int GetMemRequired( int width, int height, int depth, ImageFormat format, bool mipmap )
+ {
+ return 0;
+ }
+
+ // Gets image format info
+ virtual const ImageFormatInfo_t& ImageFormatInfo( ImageFormat fmt ) const
+ {
+ static ImageFormatInfo_t info;
+ return info;
+ }
+
+ // Allows us to set the default shadow state
+ virtual void SetDefaultShadowState() { }
+
+ // Allows us to set the default shader state
+ virtual void SetDefaultState( ) { }
+
+ // Bind standard textures
+ virtual void BindStandardTexture( Sampler_t stage, StandardTextureId_t id ) { }
+ virtual void BindStandardVertexTexture( VertexTextureSampler_t stage, StandardTextureId_t id ) { }
+ virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) { *pWidth = *pHeight = 0; }
+
+ // What are the lightmap dimensions?
+ virtual void GetLightmapDimensions( int *w, int *h ) { *w = *h = 0; }
+
+ // These methods are called when the shader must eject + restore HW memory
+ virtual void ReleaseShaderObjects() {}
+ virtual void RestoreShaderObjects( CreateInterfaceFn shaderFactory, int nChangeFlags = 0 ) {}
+
+ // Used to prevent meshes from drawing.
+ virtual bool IsInStubMode() { return false; }
+ virtual bool InFlashlightMode() const { return false; }
+
+ // For the shader API to shove the current version of aniso level into the
+ // "definitive" place (g_config) when the shader API decides to change it.
+ // Eventually, we should have a better system of who owns the definitive
+ // versions of config vars.
+ virtual void NoteAnisotropicLevel( int currentLevel ) {}
+
+ // NOTE: Stuff after this is added after shipping HL2.
+
+ // Are we rendering through the editor?
+ virtual bool InEditorMode() const { return false; }
+
+ // Gets the bound morph's vertex format; returns 0 if no morph is bound
+ virtual MorphFormat_t GetBoundMorphFormat() { return 0; }
+
+ virtual ITexture *GetRenderTargetEx( int nRenderTargetID ) { return 0; }
+
+ // Tells the material system to draw a buffer clearing quad
+ virtual void DrawClearBufferQuad( unsigned char r, unsigned char g, unsigned char b, unsigned char a, bool bClearColor, bool bClearAlpha, bool bClearDepth ) OVERRIDE {}
+
+#if defined( _X360 )
+ virtual void ReadBackBuffer( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pData, ImageFormat dstFormat, int nDstStride ) {}
+#endif
+
+ // Calls from meshes to material system to handle queing/threading
+ virtual bool OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices ) { return false; }
+ virtual bool OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists ) { return false; }
+ virtual bool OnSetFlexMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) { return false; }
+ virtual bool OnSetColorMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) { return false; }
+ virtual bool OnSetPrimitiveType( IMesh *pMesh, MaterialPrimitiveType_t type ) { return false; }
+ virtual bool OnFlushBufferedPrimitives() { return false; }
+
+
+ virtual void SyncMatrices() {}
+ virtual void SyncMatrix( MaterialMatrixMode_t ) {}
+ virtual int MaxHWMorphBatchCount() const { return 0; }
+
+ virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo )
+ {
+ pInfo->m_bIsEnabled = false;
+ pInfo->m_nLookupCount = 0;
+ pInfo->m_flDefaultWeight = 0.0f;
+ }
+ virtual void OnThreadEvent( uint32 threadEvent ) {}
+
+ ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel ) { return 0; }
+
+ // Remove any materials from memory that aren't in use as determined
+ // by the IMaterial's reference count.
+ virtual void UncacheUnusedMaterials( bool bRecomputeStateSnapshots = false ) {}
+
+ virtual MaterialThreadMode_t GetThreadMode( ) { return MATERIAL_SINGLE_THREADED; }
+ virtual bool IsRenderThreadSafe( ) { return true; }
+};
+
+
+static CShaderUtilTemp g_pTemp;
+
+static IShaderDeviceMgr *g_pShaderDeviceMgr;
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderUtilTemp, IShaderUtil,
+ SHADER_UTIL_INTERFACE_VERSION, g_pTemp )
+
+//-----------------------------------------------------------------------------
+// Purpose: Warning/Msg call back through this API
+// Input : type -
+// *pMsg -
+// Output : SpewRetval_t
+//-----------------------------------------------------------------------------
+SpewRetval_t SpewFunc( SpewType_t type, const char *pMsg )
+{
+ if ( Plat_IsInDebugSession() )
+ {
+ OutputDebugString( pMsg );
+ if ( type == SPEW_ASSERT )
+ return SPEW_DEBUGGER;
+ }
+ return SPEW_CONTINUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CShaderAPITestApp : public CTier2SteamApp
+{
+ typedef CTier2SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown( );
+ virtual void Destroy();
+ virtual const char *GetAppName() { return "InputTest"; }
+ virtual bool AppUsesReadPixels() { return false; }
+
+private:
+ // Window management
+ bool CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h );
+
+ // Sets up the game path
+ bool SetupSearchPaths();
+
+ // Waits for a keypress
+ bool WaitForKeypress();
+
+ // Displays information about all adapters
+ void DisplayAdapterInfo();
+
+ // Sets the video mode
+ bool SetMode();
+
+ // Creates really simple vertex + index buffers
+ void CreateSimpleBuffers( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered );
+
+ // Destroys the buffers
+ void DestroyBuffers();
+
+ // Creates shaders
+ void CreateShaders( const char *pVShader, int nVBufLen, const char *pGShader, int nGBufLen, const char *pPShader, int nPBufLen );
+
+ // Destroys the buffers
+ void DestroyShaders();
+
+ // DrawUsingShaders
+ void TestColoredQuad( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered );
+
+ // Tests dynamic buffers
+ void TestDynamicBuffers();
+
+ bool CreateDynamicCombos_Ver5( uint8 *pComboBuffer, bool bVertexShader );
+ void LoadShaderFile( const char *pName, bool bVertexShader );
+
+ HWND m_HWnd;
+ IShaderAPI *m_pShaderAPI;
+ IShaderDevice *m_pShaderDevice;
+
+ IIndexBuffer *m_pIndexBuffer;
+ IVertexBuffer *m_pVertexBuffer;
+
+ VertexShaderHandle_t m_hVertexShader;
+ GeometryShaderHandle_t m_hGeometryShader;
+ PixelShaderHandle_t m_hPixelShader;
+};
+
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CShaderAPITestApp );
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+
+ bool bIsVistaOrHigher = false;
+
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if ( GetVersionEx( &info ) )
+ {
+ bIsVistaOrHigher = info.dwMajorVersion >= 6;
+ }
+
+ const char *pShaderDLL = CommandLine()->ParmValue( "-shaderdll" );
+ if ( !pShaderDLL )
+ {
+ pShaderDLL = "shaderapidx10.dll";
+ }
+
+ if ( !bIsVistaOrHigher && !Q_stricmp( pShaderDLL, "shaderapidx10.dll" ) )
+ {
+ pShaderDLL = "shaderapidx9.dll";
+ }
+
+ AppModule_t module = LoadModule( pShaderDLL );
+ if ( module == APP_MODULE_INVALID )
+ {
+ if ( module == APP_MODULE_INVALID )
+ {
+ pShaderDLL = "shaderapidx9.dll";
+ module = LoadModule( pShaderDLL );
+ if ( module == APP_MODULE_INVALID )
+ {
+ pShaderDLL = "shaderapiempty.dll";
+ module = LoadModule( pShaderDLL );
+ if ( module == APP_MODULE_INVALID )
+ return false;
+ }
+ }
+ }
+
+ g_pShaderDeviceMgr = (IShaderDeviceMgr*)AddSystem( module, SHADER_DEVICE_MGR_INTERFACE_VERSION );
+
+ // So that shaderapi can get ahold of our bogus IShaderUtil
+ module = LoadModule( Sys_GetFactoryThis() );
+ AddSystem( module, SHADER_UTIL_INTERFACE_VERSION );
+
+ return ( g_pShaderDeviceMgr != NULL );
+}
+
+void CShaderAPITestApp::Destroy()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Window callback
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK ShaderAPITestWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+ switch( message )
+ {
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ return DefWindowProc( hWnd, message, wParam, lParam );
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::CreateAppWindow( const char *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = ShaderAPITestWndProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ m_HWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if (!m_HWnd)
+ return false;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos (m_HWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::SetupSearchPaths()
+{
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ g_pFullFileSystem->AddSearchPath( GetGameInfoPath(), "SKIN", PATH_ADD_TO_HEAD );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if (!g_pFullFileSystem || !g_pShaderDeviceMgr )
+ return false;
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ if (!CreateAppWindow( "Press a Key To Continue", bWindowed, iWidth, iHeight ))
+ return false;
+
+ return true;
+}
+
+void CShaderAPITestApp::PostShutdown( )
+{
+ BaseClass::PostShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Waits for a keypress
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::WaitForKeypress()
+{
+ MSG msg = {0};
+ while( WM_QUIT != msg.message )
+ {
+ if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ if ( msg.message == WM_KEYDOWN )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Displays adapter information
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::DisplayAdapterInfo()
+{
+ int nAdapterCount = g_pShaderDeviceMgr->GetAdapterCount();
+ for ( int i = 0; i < nAdapterCount; ++i )
+ {
+ MaterialAdapterInfo_t info;
+ g_pShaderDeviceMgr->GetAdapterInfo( i, info );
+
+ Msg( "Adapter %d\n", i );
+ Msg( "\tName: %s\n\tVendor: 0x%X\n\tDevice: 0x%X\n\tSubSystem: 0x%X\n\tRevision: 0x%X\n\tRecommended DX Level: %d\n\tMax DX Level: %d\n",
+ info.m_pDriverName, info.m_VendorID, info.m_DeviceID, info.m_SubSysID, info.m_Revision, info.m_nDXSupportLevel, info.m_nMaxDXSupportLevel );
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ KeyValues *pConfiguration = new KeyValues( "Config" );
+ g_pShaderDeviceMgr->GetRecommendedConfigurationInfo( i, info.m_nDXSupportLevel, pConfiguration );
+ pConfiguration->RecursiveSaveToFile( buf, 1 );
+ Msg( "\tConfiguration:\n%s", ( const char * )buf.Base() );
+ Msg( "\n" );
+
+ int nModeCount = g_pShaderDeviceMgr->GetModeCount( i );
+ Msg( "\tMode Count : %d\n", nModeCount );
+ for ( int j = 0; j < nModeCount; ++j )
+ {
+ ShaderDisplayMode_t mode;
+ g_pShaderDeviceMgr->GetModeInfo( &mode, i, j );
+ Msg( "\t\tH: %5d W: %5d Format: %3d Refresh %3d/%3d\n",
+ mode.m_nWidth, mode.m_nHeight, mode.m_Format, mode.m_nRefreshRateNumerator, mode.m_nRefreshRateDenominator );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the video mode
+//-----------------------------------------------------------------------------
+bool CShaderAPITestApp::SetMode()
+{
+ int nAdapterCount = g_pShaderDeviceMgr->GetAdapterCount();
+ int nAdapter = CommandLine()->ParmValue( "-adapter", 0 );
+ if ( nAdapter >= nAdapterCount )
+ {
+ Warning( "Specified too high an adapter number on the command-line (%d/%d)!\n", nAdapter, nAdapterCount );
+ return false;
+ }
+
+ ShaderDeviceInfo_t mode;
+ mode.m_DisplayMode.m_nWidth = 1024;
+ mode.m_DisplayMode.m_nHeight = 768;
+ mode.m_DisplayMode.m_Format = IMAGE_FORMAT_BGRA8888;
+ mode.m_DisplayMode.m_nRefreshRateNumerator = 60;
+ mode.m_DisplayMode.m_nRefreshRateDenominator = 1;
+ mode.m_bWindowed = true;
+ mode.m_nBackBufferCount = 1;
+
+ CreateInterfaceFn shaderFactory = g_pShaderDeviceMgr->SetMode( m_HWnd, nAdapter, mode );
+ if ( !shaderFactory )
+ {
+ Warning( "Unable to set mode!\n" );
+ return false;
+ }
+
+ m_pShaderAPI = (IShaderAPI*)shaderFactory( SHADERAPI_INTERFACE_VERSION, NULL );
+ m_pShaderDevice = (IShaderDevice*)shaderFactory( SHADER_DEVICE_INTERFACE_VERSION, NULL );
+ if ( !m_pShaderAPI || !m_pShaderDevice )
+ {
+ Warning( "Unable to get IShaderAPI or IShaderDevice interface!\n" );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates really simple vertex + index buffers
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::CreateSimpleBuffers( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered )
+{
+ VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR;
+ if ( IsDynamicBufferType( nVBType ) )
+ {
+ m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer(
+ nVBType, VERTEX_FORMAT_UNKNOWN, 1024, "test" );
+ }
+ else
+ {
+ m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer(
+ nVBType, fmt, 4, "test" );
+ }
+
+ static unsigned char s_pColors[4][4] =
+ {
+ { 255, 0, 0, 255 },
+ { 0, 255, 0, 255 },
+ { 0, 0, 255, 255 },
+ { 255, 255, 255, 255 },
+ };
+
+ static int nCount = 0;
+
+ CVertexBuilder vb( m_pVertexBuffer, fmt );
+ vb.Lock( 4 );
+
+ vb.Position3f( -1.0f, -1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( 1.0f, -1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( 1.0f, 1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( -1.0f, 1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.SpewData( );
+ vb.Unlock( );
+
+ ++nCount;
+
+ if ( IsDynamicBufferType( nIBType ) )
+ {
+ m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer( nIBType, MATERIAL_INDEX_FORMAT_UNKNOWN, 64, "test" );
+ }
+ else
+ {
+ m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer( nIBType, MATERIAL_INDEX_FORMAT_16BIT, 6, "test" );
+ }
+ CIndexBuilder ib( m_pIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT );
+
+ ib.Lock( 6, 0 );
+ ib.FastIndex( 0 );
+ ib.FastIndex( 2 );
+ ib.FastIndex( 1 );
+ ib.FastIndex( 0 );
+ ib.FastIndex( 3 );
+ ib.FastIndex( 2 );
+ ib.SpewData();
+ ib.Unlock( );
+
+ m_pShaderAPI->BindVertexBuffer( 0, m_pVertexBuffer, vb.Offset(), 0, vb.TotalVertexCount(), fmt );
+ m_pShaderAPI->BindIndexBuffer( m_pIndexBuffer, ib.Offset() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Destroys the buffers
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::DestroyBuffers()
+{
+ if ( m_pVertexBuffer )
+ {
+ m_pShaderDevice->DestroyVertexBuffer( m_pVertexBuffer );
+ m_pVertexBuffer = NULL;
+ }
+
+ if ( m_pIndexBuffer )
+ {
+ m_pShaderDevice->DestroyIndexBuffer( m_pIndexBuffer );
+ m_pIndexBuffer = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// shader programs
+//-----------------------------------------------------------------------------
+static const char s_pSimpleVertexShader[] =
+ "struct VS_INPUT "
+ "{ "
+ " float3 vPos : POSITION0; "
+ " float4 vColor : COLOR0; "
+ "}; "
+ " "
+ "struct VS_OUTPUT "
+ "{ "
+ " float4 projPos : POSITION0; "
+ " float4 vertexColor : COLOR0; "
+ "}; "
+ " "
+ "VS_OUTPUT main( const VS_INPUT v ) "
+ "{ "
+ " VS_OUTPUT o = ( VS_OUTPUT )0; "
+ " "
+ " o.projPos.xyz = v.vPos; "
+ " o.projPos.w = 1.0f; "
+ " o.vertexColor = v.vColor; "
+ " return o; "
+ "} "
+ "";
+
+static const char s_pSimplePixelShader[] =
+ "struct PS_INPUT "
+ "{ "
+ " float4 projPos : POSITION0; "
+ " float4 vColor : COLOR0; "
+ "}; "
+ " "
+ "float4 main( const PS_INPUT i ) : COLOR "
+ "{ "
+ " return i.vColor; "
+ "} "
+ "";
+
+
+//-----------------------------------------------------------------------------
+// Create, destroy shaders
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::CreateShaders( const char *pVShader, int nVBufLen, const char *pGShader, int nGBufLen, const char *pPShader, int nPBufLen )
+{
+ const char *pVertexShaderVersion = g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 100 ? "vs_4_0" : "vs_2_0";
+ const char *pPixelShaderVersion = g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 100 ? "ps_4_0" : "ps_2_0";
+
+ // Compile shaders
+ m_hVertexShader = m_pShaderDevice->CreateVertexShader( pVShader, nVBufLen, pVertexShaderVersion );
+ Assert( m_hVertexShader != VERTEX_SHADER_HANDLE_INVALID );
+
+ m_hGeometryShader = GEOMETRY_SHADER_HANDLE_INVALID;
+ if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 100 )
+ {
+ // m_hGeometryShader = m_pShaderDevice->CreateGeometryShader( pGShader, nGBufLen, "gs_4_0" );
+ // Assert( m_hGeometryShader != GEOMETRY_SHADER_HANDLE_INVALID );
+ }
+
+ m_hPixelShader = m_pShaderDevice->CreatePixelShader( pPShader, nPBufLen, pPixelShaderVersion );
+ Assert( m_hPixelShader != PIXEL_SHADER_HANDLE_INVALID );
+
+ m_pShaderAPI->BindVertexShader( m_hVertexShader );
+ m_pShaderAPI->BindGeometryShader( m_hGeometryShader );
+ m_pShaderAPI->BindPixelShader( m_hPixelShader );
+}
+
+void CShaderAPITestApp::DestroyShaders()
+{
+ m_pShaderDevice->DestroyVertexShader( m_hVertexShader );
+ m_pShaderDevice->DestroyGeometryShader( m_hGeometryShader );
+ m_pShaderDevice->DestroyPixelShader( m_hPixelShader );
+
+ m_hVertexShader = VERTEX_SHADER_HANDLE_INVALID;
+ m_hGeometryShader = GEOMETRY_SHADER_HANDLE_INVALID;
+ m_hPixelShader = PIXEL_SHADER_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// DrawQuad
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::TestColoredQuad( ShaderBufferType_t nVBType, ShaderBufferType_t nIBType, bool bBuffered )
+{
+ // clear (so that we can make sure that we aren't getting results from the previous quad)
+ m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) );
+ m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 );
+
+ CreateSimpleBuffers( nVBType, nIBType, bBuffered );
+
+ // Draw a quad!
+ CreateShaders( s_pSimpleVertexShader, sizeof(s_pSimpleVertexShader),
+ NULL, 0, s_pSimplePixelShader, sizeof(s_pSimplePixelShader) );
+
+ m_pShaderAPI->Draw( MATERIAL_TRIANGLES, 0, 6 );
+ m_pShaderDevice->Present();
+
+ DestroyShaders();
+
+ DestroyBuffers();
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests dynamic buffers
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::TestDynamicBuffers()
+{
+ m_pVertexBuffer = m_pShaderDevice->CreateVertexBuffer(
+ SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, 0x100, "test" );
+ m_pIndexBuffer = m_pShaderDevice->CreateIndexBuffer(
+ SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_UNKNOWN, 30, "test" );
+
+ CreateShaders( s_pSimpleVertexShader, sizeof(s_pSimpleVertexShader),
+ NULL, 0, s_pSimplePixelShader, sizeof(s_pSimplePixelShader) );
+
+ // clear (so that we can make sure that we aren't getting results from the previous quad)
+ m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) );
+ m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 );
+
+ static unsigned char s_pColors[4][4] =
+ {
+ { 255, 0, 0, 255 },
+ { 0, 255, 0, 255 },
+ { 0, 0, 255, 255 },
+ { 255, 255, 255, 255 },
+ };
+
+ static int nCount = 0;
+
+ VertexFormat_t fmt = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR;
+ const int nLoopCount = 8;
+ float flWidth = 2.0f / nLoopCount;
+ for ( int i = 0; i < nLoopCount; ++i )
+ {
+ CVertexBuilder vb( m_pVertexBuffer, fmt );
+ vb.Lock( 4 );
+
+ vb.Position3f( -1.0f + i * flWidth, -1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( -1.0f + i * flWidth + flWidth, -1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( -1.0f + i * flWidth + flWidth, 1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+
+ vb.Position3f( -1.0f + i * flWidth, 1.0f, 0.5f );
+ vb.Normal3f( 0.0f, 0.0f, 1.0f );
+ vb.Color4ubv( s_pColors[nCount++ % 4] );
+ vb.AdvanceVertex();
+ vb.SpewData();
+ vb.Unlock( );
+
+ ++nCount;
+
+ CIndexBuilder ib( m_pIndexBuffer, MATERIAL_INDEX_FORMAT_16BIT );
+
+ ib.Lock( 6, vb.GetFirstVertex() );
+ ib.FastIndex( 0 );
+ ib.FastIndex( 2 );
+ ib.FastIndex( 1 );
+ ib.FastIndex( 0 );
+ ib.FastIndex( 3 );
+ ib.FastIndex( 2 );
+ ib.SpewData();
+ ib.Unlock( );
+
+ m_pShaderAPI->BindVertexBuffer( 0, m_pVertexBuffer, vb.Offset(), vb.GetFirstVertex(), vb.TotalVertexCount(), fmt );
+ m_pShaderAPI->BindIndexBuffer( m_pIndexBuffer, ib.Offset() );
+ m_pShaderAPI->Draw( MATERIAL_TRIANGLES, ib.GetFirstIndex(), ib.TotalIndexCount() );
+ }
+
+ m_pShaderDevice->Present();
+
+ ++nCount;
+
+ DestroyShaders();
+ DestroyBuffers();
+}
+
+
+//-----------------------------------------------------------------------------
+// Create dynamic combos
+//-----------------------------------------------------------------------------
+static uint32 NextULONG( uint8 * &pData )
+{
+ // handle unaligned read
+ uint32 nRet;
+ memcpy( &nRet, pData, sizeof( nRet ) );
+ pData += sizeof( nRet );
+ return nRet;
+}
+
+bool CShaderAPITestApp::CreateDynamicCombos_Ver5( uint8 *pComboBuffer, bool bVertexShader )
+{
+ uint8 *pCompressedShaders = pComboBuffer;
+ uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE];
+
+ // now, loop through all blocks
+ bool bOK = true;
+ while ( bOK )
+ {
+ uint32 nBlockSize = NextULONG( pCompressedShaders );
+ if ( nBlockSize == 0xffffffff )
+ {
+ // any more blocks?
+ break;
+ }
+
+ switch( nBlockSize & 0xc0000000 )
+ {
+ case 0: // bzip2
+ {
+ // uncompress
+ uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE;
+ int nRslt = BZ2_bzBuffToBuffDecompress(
+ reinterpret_cast<char *>( pUnpackBuffer ),
+ &nOutsize,
+ reinterpret_cast<char *>( pCompressedShaders ),
+ nBlockSize, 1, 0 );
+ if ( nRslt < 0 )
+ {
+ // errors are negative for bzip
+ Assert( 0 );
+ Warning( "BZIP Error (%d) decompressing shader", nRslt );
+ bOK = false;
+ }
+
+ pCompressedShaders += nBlockSize;
+ nBlockSize = nOutsize; // how much data there is
+ }
+ break;
+
+ case 0x80000000: // uncompressed
+ {
+ // not compressed, as is
+ nBlockSize &= 0x3fffffff;
+ memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize );
+ pCompressedShaders += nBlockSize;
+ }
+ break;
+
+ case 0x40000000: // lzma compressed
+ {
+ nBlockSize &= 0x3fffffff;
+
+ size_t nOutsize = CLZMA::Uncompress(
+ reinterpret_cast<uint8 *>( pCompressedShaders ),
+ pUnpackBuffer );
+ pCompressedShaders += nBlockSize;
+ nBlockSize = nOutsize; // how much data there is
+ }
+ break;
+
+ default:
+ {
+ Assert( 0 );
+ Error(" unrecognized shader compression type = file corrupt?");
+ bOK = false;
+ }
+ }
+
+ uint8 *pReadPtr = pUnpackBuffer;
+ while ( pReadPtr < pUnpackBuffer+nBlockSize )
+ {
+ uint32 nCombo_ID = NextULONG( pReadPtr );
+ (void)nCombo_ID; // Suppress local variable is initialized but not referenced warning
+ uint32 nShaderSize = NextULONG( pReadPtr );
+
+ CUtlBuffer buf( pReadPtr, nShaderSize );
+ if ( bVertexShader )
+ {
+ m_pShaderDevice->CreateVertexShader( buf, "vs_2_0" );
+ }
+ else
+ {
+ m_pShaderDevice->CreatePixelShader( buf, "ps_2_b" );
+ }
+
+ pReadPtr += nShaderSize;
+ }
+ }
+
+ delete[] pUnpackBuffer;
+
+ return bOK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Load shader
+//-----------------------------------------------------------------------------
+void CShaderAPITestApp::LoadShaderFile( const char *pName, bool bVertexShader )
+{
+ // next, try the fxc dir
+ char pFileName[MAX_PATH];
+ Q_snprintf( pFileName, MAX_PATH, "..\\hl2\\shaders\\fxc\\%s.vcs", pName );
+
+ FileHandle_t hFile = g_pFullFileSystem->Open( pFileName, "rb", "EXECUTABLE_PATH" );
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ {
+ Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName );
+ return;
+ }
+
+ ShaderHeader_t header;
+ g_pFullFileSystem->Read( &header, sizeof( ShaderHeader_t ), hFile );
+
+ // cache the dictionary
+ int nComboSize = header.m_nNumStaticCombos * sizeof( StaticComboRecord_t );
+ StaticComboRecord_t *pRecords = (StaticComboRecord_t *)malloc( nComboSize );
+ g_pFullFileSystem->Read( pRecords, nComboSize, hFile );
+
+ for ( unsigned int i = 0; i < header.m_nNumStaticCombos - 1; ++i )
+ {
+ int nStartingOffset = pRecords[i].m_nFileOffset;
+ int nEndingOffset = pRecords[i+1].m_nFileOffset;
+ int nShaderSize = nEndingOffset - nStartingOffset;
+
+ uint8 *pBuf = (uint8*)malloc( nShaderSize );
+ g_pFullFileSystem->Seek( hFile, nStartingOffset, FILESYSTEM_SEEK_HEAD );
+ g_pFullFileSystem->Read( pBuf, nShaderSize, hFile );
+
+ CreateDynamicCombos_Ver5( pBuf, bVertexShader );
+ free( pBuf );
+
+ }
+
+ free( pRecords );
+ g_pFullFileSystem->Close( hFile );
+
+}
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CShaderAPITestApp::Main()
+{
+ DisplayAdapterInfo();
+ if ( !SetMode() )
+ return 0;
+
+ // Test buffer clearing
+ m_pShaderAPI->ClearColor3ub( RandomInt( 0, 100 ), RandomInt( 0, 100 ), RandomInt( 190, 255 ) );
+ m_pShaderAPI->ClearBuffers( true, false, false, -1, -1 );
+ m_pShaderDevice->Present();
+
+ SetWindowText( m_HWnd, "ClearBuffers test results . . hit a key" );
+
+ if ( !WaitForKeypress() )
+ return 1;
+
+ // Test viewport
+ int nMaxViewports = g_pMaterialSystemHardwareConfig->MaxViewports();
+ ShaderViewport_t* pViewports = ( ShaderViewport_t* )_alloca( nMaxViewports * sizeof(ShaderViewport_t) );
+ for ( int i = 0; i < nMaxViewports; ++i )
+ {
+ int x = RandomInt( 0, 100 );
+ int y = RandomInt( 0, 100 );
+ int w = RandomInt( 100, 200 );
+ int h = RandomInt( 100, 200 );
+ pViewports[i].Init( x, y, w, h );
+
+ m_pShaderAPI->SetViewports( i+1, pViewports );
+ }
+
+ SetWindowText( m_HWnd, "SetViewports test results . . hit a key" );
+
+ if ( !WaitForKeypress() )
+ return 1;
+
+ // Sets up a full-screen viewport
+ int w, h;
+ m_pShaderDevice->GetWindowSize( w, h );
+
+ ShaderViewport_t viewport;
+ viewport.Init( 0, 0, w, h );
+ m_pShaderAPI->SetViewports( 1, &viewport );
+
+ // Test drawing a full-screen quad with interpolated vertex colors for every combo of static/dynamic VB, static dynamic IB, buffered/non-buffered.
+ char buf[1024];
+ for ( int nVBType = 0; nVBType < SHADER_BUFFER_TYPE_COUNT; ++nVBType )
+ {
+ // FIXME: Remove
+ if ( nVBType > SHADER_BUFFER_TYPE_DYNAMIC )
+ continue;
+
+ for( int nIBType = 0; nIBType < SHADER_BUFFER_TYPE_COUNT; ++nIBType )
+ {
+ // FIXME: Remove
+ if ( nIBType > SHADER_BUFFER_TYPE_DYNAMIC )
+ continue;
+
+ // MESHFIXME: make buffered vertex buffers/index buffers work.
+ int nBuffered = 0;
+// for( nBuffered = 0; nBuffered < 2; nBuffered++ )
+ {
+ TestColoredQuad( (ShaderBufferType_t)nVBType, (ShaderBufferType_t)nIBType, nBuffered != 0 );
+
+ sprintf( buf, "TestColoredQuad results VB: %d IB: %d Buffered: %d HIT A KEY!",
+ nVBType, nIBType, nBuffered != 0 );
+ SetWindowText( m_HWnd, buf );
+
+ if ( !WaitForKeypress() )
+ return 1;
+ }
+ }
+ }
+
+ SetWindowText( m_HWnd, "Dynamic Buffer Test: HIT A KEY!" );
+ TestDynamicBuffers();
+ if ( !WaitForKeypress() )
+ return 1;
+
+ g_pMaterialSystemHardwareConfig->OverrideStreamOffsetSupport( true, false );
+
+ SetWindowText( m_HWnd, "Dynamic Buffer Test (no stream offset): HIT A KEY!" );
+ TestDynamicBuffers();
+ if ( !WaitForKeypress() )
+ return 1;
+
+ g_pMaterialSystemHardwareConfig->OverrideStreamOffsetSupport( false, false );
+
+ return 1;
+}
diff --git a/unittests/shaderapitest/shaderapitest.vpc b/unittests/shaderapitest/shaderapitest.vpc
new file mode 100644
index 0000000..30ba082
--- /dev/null
+++ b/unittests/shaderapitest/shaderapitest.vpc
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------------
+// SHADERAPITEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Project "ShaderAPITest"
+{
+ $Folder "Source Files"
+ {
+ $File "shaderapitest.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib tier2
+ $Lib $LIBCOMMON\bzip2
+ }
+}
diff --git a/unittests/soundtest/soundtest.cpp b/unittests/soundtest/soundtest.cpp
new file mode 100644
index 0000000..e43b3c5
--- /dev/null
+++ b/unittests/soundtest/soundtest.cpp
@@ -0,0 +1,306 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Sound unit test application
+//
+//=============================================================================
+
+#include <windows.h>
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "filesystem.h"
+#include "datacache/idatacache.h"
+#include "appframework/appframework.h"
+#include "soundsystem/isoundsystem.h"
+#include "vstdlib/cvar.h"
+#include "filesystem_init.h"
+
+
+//-----------------------------------------------------------------------------
+// Main system interfaces
+//-----------------------------------------------------------------------------
+IFileSystem *g_pFileSystem;
+ISoundSystem *g_pSoundSystem;
+
+
+//-----------------------------------------------------------------------------
+// Standard spew functions
+//-----------------------------------------------------------------------------
+static SpewRetval_t SoundTestOutputFunc( SpewType_t spewType, char const *pMsg )
+{
+ printf( pMsg );
+ fflush( stdout );
+
+ if (spewType == SPEW_ERROR)
+ return SPEW_ABORT;
+ return (spewType == SPEW_ASSERT) ? SPEW_DEBUGGER : SPEW_CONTINUE;
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CSoundTestApp : public CDefaultAppSystemGroup<CSteamAppSystemGroup>
+{
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+
+private:
+ bool CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h );
+ bool SetupSearchPaths();
+ void AppPumpMessages();
+
+ // Windproc
+ static LONG WINAPI WinAppWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ LONG WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ HWND m_hWnd;
+ bool m_bExitMainLoop;
+};
+
+static CSoundTestApp s_SoundTestApp;
+DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT_GLOBALVAR( CSoundTestApp, s_SoundTestApp );
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+bool CSoundTestApp::Create()
+{
+ SpewOutputFunc( SoundTestOutputFunc );
+
+ // Add in the cvar factory
+ AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
+ AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "datacache.dll", DATACACHE_INTERFACE_VERSION },
+ { "soundsystem.dll", SOUNDSYSTEM_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ g_pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ g_pSoundSystem = (ISoundSystem*)FindSystem( SOUNDSYSTEM_INTERFACE_VERSION );
+
+ return ( g_pFileSystem && g_pSoundSystem );
+}
+
+void CSoundTestApp::Destroy()
+{
+ g_pFileSystem = NULL;
+ g_pSoundSystem = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+bool CSoundTestApp::CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = WinAppWindowProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ m_hWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if ( m_hWnd == INVALID_HANDLE_VALUE )
+ return false;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos (m_hWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Message handler
+//-----------------------------------------------------------------------------
+LONG CSoundTestApp::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if ( uMsg == WM_CLOSE )
+ {
+ m_bExitMainLoop = true;
+ }
+ return DefWindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+LONG WINAPI CSoundTestApp::WinAppWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return s_SoundTestApp.WindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CSoundTestApp::SetupSearchPaths()
+{
+ CFSSteamSetupInfo steamInfo;
+ steamInfo.m_pDirectoryName = NULL;
+ steamInfo.m_bOnlyUseDirectoryName = false;
+ steamInfo.m_bToolsMode = true;
+ steamInfo.m_bSetSteamDLLPath = true;
+ steamInfo.m_bSteam = g_pFileSystem->IsSteam();
+ if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
+ return false;
+
+ CFSMountContentInfo fsInfo;
+ fsInfo.m_pFileSystem = g_pFileSystem;
+ fsInfo.m_bToolsMode = true;
+ fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
+
+ if ( FileSystem_MountContent( fsInfo ) != FS_OK )
+ return false;
+
+ // Finally, load the search paths for the "GAME" path.
+ CFSSearchPathsInit searchPathsInit;
+ searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
+ searchPathsInit.m_pFileSystem = g_pFileSystem;
+ if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
+ return false;
+
+ g_pFileSystem->AddSearchPath( steamInfo.m_GameInfoPath, "SKIN", PATH_ADD_TO_HEAD );
+
+ FileSystem_AddSearchPath_Platform( g_pFileSystem, steamInfo.m_GameInfoPath );
+
+ // and now add episodic to the GAME searchpath
+ char shorts[MAX_PATH];
+ Q_strncpy( shorts, steamInfo.m_GameInfoPath, MAX_PATH );
+ Q_StripTrailingSlash( shorts );
+ Q_strncat( shorts, "/../episodic", MAX_PATH, MAX_PATH );
+
+ g_pFileSystem->AddSearchPath( shorts, "GAME", PATH_ADD_TO_HEAD );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// PreInit, PostShutdown
+//-----------------------------------------------------------------------------
+bool CSoundTestApp::PreInit( )
+{
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ if ( !CreateAppWindow( "SoundTest", bWindowed, iWidth, iHeight ) )
+ return false;
+
+ return true;
+}
+
+void CSoundTestApp::PostShutdown()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Pump messages
+//-----------------------------------------------------------------------------
+void CSoundTestApp::AppPumpMessages()
+{
+ MSG msg;
+ while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+int CSoundTestApp::Main()
+{
+ CAudioSource *pSource = g_pSoundSystem->LoadSound( "sound/ambient/alarms/alarm1.wav" );
+
+ CAudioMixer *pMixer;
+ g_pSoundSystem->PlaySound( pSource, 1.0f, &pMixer );
+
+ m_bExitMainLoop = false;
+ while ( !m_bExitMainLoop )
+ {
+ AppPumpMessages();
+ g_pSoundSystem->Update( Plat_FloatTime() );
+ }
+
+ return 1;
+}
+
+
+
diff --git a/unittests/soundtest/soundtest.vpc b/unittests/soundtest/soundtest.vpc
new file mode 100644
index 0000000..c98027e
--- /dev/null
+++ b/unittests/soundtest/soundtest.vpc
@@ -0,0 +1,24 @@
+//-----------------------------------------------------------------------------
+// SOUNDTEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_win_win32_base.vpc"
+
+$Project "Soundtest"
+{
+ $Folder "Source Files"
+ {
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "soundtest.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $DynamicFile "$SRCDIR\lib\public\appframework.lib"
+ }
+}
diff --git a/unittests/testprocess/testprocess.cpp b/unittests/testprocess/testprocess.cpp
new file mode 100644
index 0000000..23fbde8
--- /dev/null
+++ b/unittests/testprocess/testprocess.cpp
@@ -0,0 +1,191 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+
+#include <windows.h>
+#include "tier0/icommandline.h"
+#include <stdio.h>
+#include "tier0/dbg.h"
+
+
+static unsigned short g_InitialColor = 0xFFFF;
+static unsigned short g_LastColor = 0xFFFF;
+static unsigned short g_BadColor = 0xFFFF;
+static WORD g_BackgroundFlags = 0xFFFF;
+
+static void GetInitialColors( )
+{
+ // Get the old background attributes.
+ CONSOLE_SCREEN_BUFFER_INFO oldInfo;
+ GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
+ g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
+ g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
+
+ g_BadColor = 0;
+ if (g_BackgroundFlags & BACKGROUND_RED)
+ g_BadColor |= FOREGROUND_RED;
+ if (g_BackgroundFlags & BACKGROUND_GREEN)
+ g_BadColor |= FOREGROUND_GREEN;
+ if (g_BackgroundFlags & BACKGROUND_BLUE)
+ g_BadColor |= FOREGROUND_BLUE;
+ if (g_BackgroundFlags & BACKGROUND_INTENSITY)
+ g_BadColor |= FOREGROUND_INTENSITY;
+}
+
+static WORD SetConsoleTextColor( int red, int green, int blue, int intensity )
+{
+ WORD ret = g_LastColor;
+
+ g_LastColor = 0;
+ if( red ) g_LastColor |= FOREGROUND_RED;
+ if( green ) g_LastColor |= FOREGROUND_GREEN;
+ if( blue ) g_LastColor |= FOREGROUND_BLUE;
+ if( intensity ) g_LastColor |= FOREGROUND_INTENSITY;
+
+ // Just use the initial color if there's a match...
+ if (g_LastColor == g_BadColor)
+ g_LastColor = g_InitialColor;
+
+ SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags );
+ return ret;
+}
+
+
+static void RestoreConsoleTextColor( WORD color )
+{
+ SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags );
+ g_LastColor = color;
+}
+
+void CmdLib_Exit( int exitCode )
+{
+ TerminateProcess( GetCurrentProcess(), 1 );
+}
+
+CRITICAL_SECTION g_SpewCS;
+bool g_bSpewCSInitted = false;
+bool g_bSuppressPrintfOutput = false;
+
+SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg )
+{
+ // Hopefully two threads won't call this simultaneously right at the start!
+ if ( !g_bSpewCSInitted )
+ {
+ InitializeCriticalSection( &g_SpewCS );
+ g_bSpewCSInitted = true;
+ }
+
+ WORD old;
+ SpewRetval_t retVal;
+
+ EnterCriticalSection( &g_SpewCS );
+ {
+ if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG ))
+ {
+ old = SetConsoleTextColor( 1, 1, 1, 0 );
+ retVal = SPEW_CONTINUE;
+ }
+ else if( type == SPEW_WARNING )
+ {
+ old = SetConsoleTextColor( 1, 1, 0, 1 );
+ retVal = SPEW_CONTINUE;
+ }
+ else if( type == SPEW_ASSERT )
+ {
+ old = SetConsoleTextColor( 1, 0, 0, 1 );
+ retVal = SPEW_DEBUGGER;
+
+#ifdef MPI
+ // VMPI workers don't want to bring up dialogs and suchlike.
+ if ( g_bUseMPI && !g_bMPIMaster )
+ {
+ VMPI_HandleCrash( pMsg, true );
+ exit( 0 );
+ }
+#endif
+ }
+ else if( type == SPEW_ERROR )
+ {
+ old = SetConsoleTextColor( 1, 0, 0, 1 );
+ retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do).
+ }
+ else
+ {
+ old = SetConsoleTextColor( 1, 1, 1, 1 );
+ retVal = SPEW_CONTINUE;
+ }
+
+ if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR )
+ printf( "%s", pMsg );
+
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ RestoreConsoleTextColor( old );
+ }
+ LeaveCriticalSection( &g_SpewCS );
+
+ if ( type == SPEW_ERROR )
+ {
+ CmdLib_Exit( 1 );
+ }
+
+ return retVal;
+}
+
+
+void InstallSpewFunction()
+{
+ setvbuf( stdout, NULL, _IONBF, 0 );
+ setvbuf( stderr, NULL, _IONBF, 0 );
+
+ SpewOutputFunc( CmdLib_SpewOutputFunc );
+ GetInitialColors();
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests the process.cpp stuff
+//-----------------------------------------------------------------------------
+int main( int argc, char **argv )
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ InstallSpewFunction();
+
+ float flDelay = CommandLine()->ParmValue( "-delay", 0.0f );
+ const char *pEndMessage = CommandLine()->ParmValue( "-message", "Test Finished!\n" );
+ int nEndExtraBytes = CommandLine()->ParmValue( "-extrabytes", 0 );
+
+ if ( flDelay > 0.0f )
+ {
+ float t = Plat_FloatTime();
+ while ( Plat_FloatTime() - t < flDelay )
+ {
+ }
+ }
+
+ Msg( pEndMessage );
+
+ if ( nEndExtraBytes )
+ {
+ while( --nEndExtraBytes >= 0 )
+ {
+ Msg( "%c", ( nEndExtraBytes % 10 ) + '0' );
+ }
+ }
+
+ return 0;
+}
+
+
+
+
diff --git a/unittests/testprocess/testprocess.exe b/unittests/testprocess/testprocess.exe
new file mode 100644
index 0000000..eee66a3
--- /dev/null
+++ b/unittests/testprocess/testprocess.exe
Binary files differ
diff --git a/unittests/testprocess/testprocess.pdb b/unittests/testprocess/testprocess.pdb
new file mode 100644
index 0000000..d40bd3c
--- /dev/null
+++ b/unittests/testprocess/testprocess.pdb
Binary files differ
diff --git a/unittests/testprocess/testprocess.vpc b/unittests/testprocess/testprocess.vpc
new file mode 100644
index 0000000..44cc746
--- /dev/null
+++ b/unittests/testprocess/testprocess.vpc
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------------
+// TESTPROCESS.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$MacroRequired "PLATSUBDIR"
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\unittests\testprocess"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration "Debug"
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE winmm.lib"
+ }
+}
+
+$Project "Testprocess"
+{
+ $Folder "Source Files"
+ {
+ $File "testprocess.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib tier2
+ }
+}
diff --git a/unittests/tier1test/commandbuffertest.cpp b/unittests/tier1test/commandbuffertest.cpp
new file mode 100644
index 0000000..e51d647
--- /dev/null
+++ b/unittests/tier1test/commandbuffertest.cpp
@@ -0,0 +1,308 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for CommandBuffer
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "tier1/commandbuffer.h"
+#include "tier1/strtools.h"
+
+
+DEFINE_TESTSUITE( CommandBufferTestSuite )
+
+DEFINE_TESTCASE( CommandBufferTestSimple, CommandBufferTestSuite )
+{
+ Msg( "Simple command buffer test...\n" );
+
+ CCommandBuffer buffer;
+ buffer.AddText( "test_command test_arg1 test_arg2" );
+ buffer.AddText( "test_command2 test_arg3; test_command3 test_arg4" );
+ buffer.AddText( "test_command4\ntest_command5" );
+ buffer.AddText( "test_command6 // Comment; test_command7" );
+ buffer.AddText( "test_command8 // Comment; test_command9\ntest_command10" );
+ buffer.AddText( "test_command11 \"test_arg5 test_arg6\"" );
+ buffer.AddText( "test_command12 \"\"" );
+ buffer.AddText( "// Comment\ntest_command13\t\t\"test_arg7\"" );
+ buffer.AddText( "test_command14\"test_arg8\"test_arg9" );
+ buffer.AddText( "test_command15 test_arg10" );
+ buffer.AddText( "test_command16 test_arg11:test_arg12" );
+
+ int argc;
+ const char **argv;
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 3 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "test_arg1 test_arg2" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command2" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg3" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "test_arg3" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command3" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg4" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "test_arg4" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command4" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command5" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command6" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command8" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command10" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command11" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg5 test_arg6" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "\"test_arg5 test_arg6\"" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command12" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "\"\"" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command13" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg7" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "\"test_arg7\"" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 3 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command14" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg8" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg9" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "\"test_arg8\"test_arg9" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command15" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg10" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "test_arg10" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 4 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command16" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg11" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], ":" ) );
+ Shipping_Assert( !Q_stricmp( argv[3], "test_arg12" ) );
+ Shipping_Assert( !Q_stricmp( buffer.ArgS(), "test_arg11:test_arg12" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+}
+
+
+DEFINE_TESTCASE( CommandBufferTestTiming, CommandBufferTestSuite )
+{
+ Msg( "Delayed command buffer test...\n" );
+
+ CCommandBuffer buffer;
+
+ buffer.AddText( "test_command test_arg1 test_arg2" );
+ buffer.AddText( "test_command2 test_arg1 test_arg2 test_arg3", 1 );
+ buffer.AddText( "test_command3;wait;test_command4;wait 2;test_command5" );
+
+ int argc;
+ const char **argv;
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 3 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command3" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 4 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command2" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+ Shipping_Assert( !Q_stricmp( argv[3], "test_arg3" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command4" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command5" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+}
+
+
+DEFINE_TESTCASE( CommandBufferTestNested, CommandBufferTestSuite )
+{
+ Msg( "Nested command buffer test...\n" );
+
+ CCommandBuffer buffer;
+ buffer.AddText( "test_command test_arg1 test_arg2" );
+ buffer.AddText( "test_command2 test_arg3 test_arg4 test_arg5", 2 );
+
+ int argc;
+ const char **argv;
+ {
+ buffer.BeginProcessingCommands( 2 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 3 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.AddText( "test_command3;test_command4", 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command3" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command4" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 4 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command2" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg3" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg4" ) );
+ Shipping_Assert( !Q_stricmp( argv[3], "test_arg5" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+}
+
+
+DEFINE_TESTCASE( CommandBufferTestOverflow, CommandBufferTestSuite )
+{
+ Msg( "Command buffer overflow test...\n" );
+
+ CCommandBuffer buffer;
+
+ buffer.LimitArgumentBufferSize( 40 );
+ bool bOk = buffer.AddText( "test_command test_arg1 test_arg2" ); // 32 chars
+ Shipping_Assert( bOk );
+ bOk = buffer.AddText( "test_command2 test_arg3 test_arg4 test_arg5", 2 ); // 43 chars
+ Shipping_Assert( !bOk );
+
+ int argc;
+ const char **argv;
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 3 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+
+ bOk = buffer.AddText( "test_command3 test_arg6;wait;test_command4" );
+ Shipping_Assert( bOk );
+
+ // This makes sure that AddText doesn't cause argv to become bogus after
+ // compacting memory
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg1" ) );
+ Shipping_Assert( !Q_stricmp( argv[2], "test_arg2" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 2 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command3" ) );
+ Shipping_Assert( !Q_stricmp( argv[1], "test_arg6" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+ {
+ buffer.BeginProcessingCommands( 1 );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 1 );
+ Shipping_Assert( !Q_stricmp( argv[0], "test_command4" ) );
+
+ argc = buffer.DequeueNextCommand( argv );
+ Shipping_Assert( argc == 0 );
+
+ buffer.EndProcessingCommands( );
+ }
+}
+
diff --git a/unittests/tier1test/processtest.cpp b/unittests/tier1test/processtest.cpp
new file mode 100644
index 0000000..e83d7dc
--- /dev/null
+++ b/unittests/tier1test/processtest.cpp
@@ -0,0 +1,52 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for processes
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "vstdlib/iprocessutils.h"
+#include "tier1/strtools.h"
+#include "tier1/tier1.h"
+#include "tier0/dbg.h"
+
+
+DEFINE_TESTSUITE( ProcessTestSuite )
+
+DEFINE_TESTCASE( ProcessTestSimple, ProcessTestSuite )
+{
+ Msg( "Simple process test...\n" );
+
+ ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( "unittests\\testprocess.exe -delay 1.0", true );
+ g_pProcessUtils->WaitUntilProcessCompletes( hProcess );
+ int nLen = g_pProcessUtils->GetProcessOutputSize( hProcess );
+ char *pBuf = (char*)_alloca( nLen );
+ g_pProcessUtils->GetProcessOutput( hProcess, pBuf, nLen );
+ g_pProcessUtils->CloseProcess( hProcess );
+ Shipping_Assert( !Q_stricmp( pBuf, "Test Finished!\n" ) );
+}
+
+
+DEFINE_TESTCASE( ProcessTestBufferOverflow, ProcessTestSuite )
+{
+ Msg( "Buffer overflow process test...\n" );
+
+ ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( "unittests\\testprocess.exe -delay 1.0 -extrabytes 32768", true );
+ g_pProcessUtils->WaitUntilProcessCompletes( hProcess );
+ int nLen = g_pProcessUtils->GetProcessOutputSize( hProcess );
+ Shipping_Assert( nLen == 32768 + 16 );
+ char *pBuf = (char*)_alloca( nLen );
+ g_pProcessUtils->GetProcessOutput( hProcess, pBuf, nLen );
+ g_pProcessUtils->CloseProcess( hProcess );
+ Shipping_Assert( !Q_strnicmp( pBuf, "Test Finished!\n", 15 ) );
+
+ int nEndExtraBytes = 32768;
+ char *pTest = pBuf + 15;
+ while( --nEndExtraBytes >= 0 )
+ {
+ Shipping_Assert( *pTest == (( nEndExtraBytes % 10 ) + '0') );
+ ++pTest;
+ }
+}
+
diff --git a/unittests/tier1test/tier1test.cpp b/unittests/tier1test/tier1test.cpp
new file mode 100644
index 0000000..2dc85d7
--- /dev/null
+++ b/unittests/tier1test/tier1test.cpp
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for testing of tier1 libraries
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "tier1/tier1.h"
+#include "mathlib/mathlib.h"
+#include "tier1/convar.h"
+#include "vstdlib/iprocessutils.h"
+
+
+//-----------------------------------------------------------------------------
+// Used to connect/disconnect the DLL
+//-----------------------------------------------------------------------------
+class CTier1TestAppSystem : public CTier1AppSystem< IAppSystem >
+{
+ typedef CTier1AppSystem< IAppSystem > BaseClass;
+
+public:
+ virtual bool Connect( CreateInterfaceFn factory )
+ {
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+ return true;
+ }
+
+ virtual InitReturnVal_t Init()
+ {
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ return INIT_OK;
+ }
+
+ virtual void Shutdown()
+ {
+ BaseClass::Shutdown();
+ }
+};
+
+USE_UNITTEST_APPSYSTEM( CTier1TestAppSystem )
diff --git a/unittests/tier1test/tier1test.vpc b/unittests/tier1test/tier1test.vpc
new file mode 100644
index 0000000..9ba27aa
--- /dev/null
+++ b/unittests/tier1test/tier1test.vpc
@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------------
+// TIER1TEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;TIER1TEST_EXPORTS"
+ }
+}
+
+$Project "tier1test"
+{
+ $Folder "Source Files"
+ {
+ $File "commandbuffertest.cpp"
+ $File "processtest.cpp"
+ $File "tier1test.cpp"
+ $File "utlstringtest.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib mathlib
+ $Lib unitlib
+ }
+}
diff --git a/unittests/tier1test/utlstringtest.cpp b/unittests/tier1test/utlstringtest.cpp
new file mode 100644
index 0000000..c0fe872
--- /dev/null
+++ b/unittests/tier1test/utlstringtest.cpp
@@ -0,0 +1,224 @@
+#include "tier0/dbg.h"
+#include "unitlib/unitlib.h"
+#include "tier1/utlstring.h"
+
+DEFINE_TESTSUITE( UtlStringTestSuite )
+
+static void ConstructorTests()
+{
+ CUtlString string;
+ Shipping_Assert( string.Length() == 0 );
+ Shipping_Assert( string.IsEmpty() == true );
+ Shipping_Assert( string.GetForModify() && string.GetForModify()[ 0 ] == 0 );
+
+ CUtlString string2( "shiz" );
+ Shipping_Assert( string2.Length() == 4 );
+ Shipping_Assert( !V_stricmp( string2.Get(), "shiz" ) );
+
+ CUtlString string3( "thisstringismuchlongerthantwentywholehugecharacters", 20 );
+ Shipping_Assert( string3.Length() == 20 );
+}
+
+static void BasicFunctionalityTests()
+{
+ CUtlString empty;
+ empty.SetLength( 10 );
+ Assert( empty.Length() == 10 );
+ V_memcpy( empty.GetForModify(), "blah", 4 );
+ empty.GetForModify()[ 4 ] = 0;
+ Assert( empty.Length() == 4 );
+ Shipping_Assert( !V_stricmp( empty.Get(), "blah" ) );
+
+ empty.Clear();
+ Shipping_Assert( empty.IsEmpty() );
+ empty = "blah";
+ Shipping_Assert( !empty.IsEmpty() );
+ empty.Purge();
+ Shipping_Assert( empty.IsEmpty() );
+
+ empty = "CaMeLcAsE";
+
+ Shipping_Assert( empty.IsEqual_CaseSensitive( "CaMeLcAsE" ) );
+ Shipping_Assert( empty.IsEqual_CaseInsensitive( "camelCASE" ) );
+
+ CUtlString copy( empty );
+ Shipping_Assert( empty == copy );
+ empty.ToLower();
+ Shipping_Assert( empty != copy );
+
+ empty.Append( "271" );
+ Shipping_Assert( !V_stricmp( empty.Get(), "camelcase271" ) );
+ empty.Append( "35123", 3 );
+ Shipping_Assert( !V_stricmp( empty.Get(), "camelcase271351" ) );
+ empty.Append( 'A' );
+ Shipping_Assert( !V_stricmp( empty.Get(), "camelcase271351A" ) );
+
+ empty.Append( '/' );
+ empty.Append( '\\' );
+ Shipping_Assert( !V_stricmp( empty.Get(), "camelcase271351A/\\" ) );
+ empty.StripTrailingSlash();
+ empty.StripTrailingSlash();
+ Shipping_Assert( !V_stricmp( empty.Get(), "camelcase271351A" ) );
+
+ empty = "sometext";
+ empty.SetLength( 4 );
+ Shipping_Assert( empty.Get()[ 4 ] == '\0' ); // Check for null terminator
+ Shipping_Assert( empty.Length() == 4 );
+ Shipping_Assert( empty == "some" );
+}
+
+static void TrimAPITests()
+{
+ CUtlString orig( " testy " );
+ CUtlString orig2( "\n \n\ttesty\t\r\n \n\t\r" );
+ CUtlString s;
+
+ s = orig;
+ s.TrimLeft( ' ' );
+ Shipping_Assert( !V_stricmp( s.Get(), "testy " ) );
+
+ s = orig;
+ s.TrimRight( ' ' );
+ Shipping_Assert( !V_stricmp( s.Get(), " testy" ) );
+
+ s = orig2;
+ s.TrimLeft();
+ s.TrimRight();
+ Shipping_Assert( !V_stricmp( s.Get(), "testy" ) );
+
+ s = orig;
+ s.Trim( ' ' );
+ Shipping_Assert( !V_stricmp( s.Get(), "testy" ) );
+ s = orig2;
+ s.Trim();
+ Shipping_Assert( !V_stricmp( s.Get(), "testy" ) );
+}
+
+static void OperatorAPITests()
+{
+ CUtlString orig( "base" );
+ CUtlString orig2( "different" );
+
+ // operator = on CUtlString
+ orig = orig2;
+ Shipping_Assert( !V_stricmp( orig.Get(), "different" ) );
+ // perator = on const char *
+ orig = "different2";
+ Shipping_Assert( !V_stricmp( orig.Get(), "different2" ) );
+
+ orig = orig2;
+ // op ==
+ Shipping_Assert( orig == orig2 );
+ orig = "base";
+ // op !=
+ Shipping_Assert( orig != orig2 );
+
+ orig += "1";
+ Shipping_Assert( !V_stricmp( orig.Get(), "base1" ) );
+ orig2 = "2";
+ orig += orig2;
+ Shipping_Assert( !V_stricmp( orig.Get(), "base12" ) );
+ orig += '3';
+ Shipping_Assert( !V_stricmp( orig.Get(), "base123" ) );
+ // integer
+ orig += 123;
+ Shipping_Assert( !V_stricmp( orig.Get(), "base123123" ) );
+ orig += 1.5f;
+ Shipping_Assert( !V_stricmp( orig.Get(), "base1231231.5" ) );
+
+ orig = "1";
+ orig2 = "2";
+ CUtlString newString = orig + orig2;
+ Shipping_Assert( !V_stricmp( newString.Get(), "12" ) );
+ newString = orig + "3";
+ Shipping_Assert( !V_stricmp( newString.Get(), "13" ) );
+ newString = orig + 42;
+ Shipping_Assert( !V_stricmp( newString.Get(), "142" ) );
+
+ orig = "this is a long string";
+ newString = orig.Slice( 4 );
+ Shipping_Assert( !V_stricmp( newString.Get(), " is a long string" ) );
+ newString = orig.Slice( 5, 10 );
+ Shipping_Assert( !V_stricmp( newString.Get(), "is a " ) );
+
+ newString = orig.Left( 4 );
+ Shipping_Assert( !V_stricmp( newString.Get(), "this" ) );
+ newString = orig.Right( 6 );
+ Shipping_Assert( !V_stricmp( newString.Get(), "string" ) );
+ newString = orig.Replace( 's', 'q' );
+ Shipping_Assert( !V_stricmp( newString.Get(), "thiq iq a long qtring" ) );
+}
+
+static void PatternTests()
+{
+ CUtlString str( "this is a very long pattern of very long things" );
+ CUtlString pattern( "this is*" );
+
+ Shipping_Assert( str.MatchesPattern( pattern ) );
+
+ pattern = "notpresent";
+ Shipping_Assert( !str.MatchesPattern( pattern ) );
+}
+
+static void FmtStr( CUtlString &str, const char *pFmt, ... )
+{
+ va_list argptr;
+ va_start( argptr, pFmt );
+ str.FormatV( pFmt, argptr );
+ va_end( argptr );
+}
+
+static void FormatTests()
+{
+ CUtlString str;
+ str.Format( "%s %s %i", "shiz", "baz", 1 );
+ Shipping_Assert( !V_stricmp( str.Get(), "shiz baz 1" ) );
+
+ FmtStr( str, "blah%i", 3 );
+ Shipping_Assert( !V_stricmp( str.Get(), "blah3" ) );
+}
+
+static void FileNameAPITests()
+{
+ CUtlString path( "c:\\source2\\game\\source2\\somefile.ext" );
+
+ CUtlString absPath = path.AbsPath();
+ Shipping_Assert( absPath == path );
+ CUtlString file = path.UnqualifiedFilename();
+ Shipping_Assert( !V_stricmp( file.Get(), "somefile.ext" ) );
+ CUtlString dir = path.DirName();
+ Shipping_Assert( !V_stricmp( dir.Get(), "c:\\source2\\game\\source2" ) );
+ dir = dir.DirName();
+ Shipping_Assert( !V_stricmp( dir.Get(), "c:\\source2\\game" ) );
+ CUtlString baseName = path.StripExtension();
+ Shipping_Assert( !V_stricmp( baseName.Get(), "c:\\source2\\game\\source2\\somefile" ) );
+ dir = path.StripFilename();
+ Shipping_Assert( !V_stricmp( dir.Get(), "c:\\source2\\game\\source2" ) );
+
+ file = path.GetBaseFilename();
+ Shipping_Assert( !V_stricmp( file.Get(), "somefile" ) );
+ CUtlString ext = path.GetExtension();
+ Shipping_Assert( !V_stricmp( ext.Get(), "ext" ) );
+
+ absPath = path.PathJoin( dir.Get(), file.Get() );
+ Shipping_Assert( !V_stricmp( absPath.Get(), "c:\\source2\\game\\source2\\somefile" ) );
+}
+
+DEFINE_TESTCASE( UtlStringTest, UtlStringTestSuite )
+{
+ Msg( "Running CUtlString tests\n" );
+
+ ConstructorTests();
+
+ BasicFunctionalityTests();
+
+ TrimAPITests();
+
+ OperatorAPITests();
+
+ PatternTests();
+
+ FormatTests();
+
+ FileNameAPITests();
+} \ No newline at end of file
diff --git a/unittests/tier2test/tier2test.cpp b/unittests/tier2test/tier2test.cpp
new file mode 100644
index 0000000..24e072b
--- /dev/null
+++ b/unittests/tier2test/tier2test.cpp
@@ -0,0 +1,44 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for testing of tier2 libraries
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "mathlib/mathlib.h"
+
+
+//-----------------------------------------------------------------------------
+// Used to connect/disconnect the DLL
+//-----------------------------------------------------------------------------
+class CTier2TestAppSystem : public CTier2AppSystem< IAppSystem >
+{
+ typedef CTier2AppSystem< IAppSystem > BaseClass;
+
+public:
+ virtual bool Connect( CreateInterfaceFn factory )
+ {
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !g_pFullFileSystem )
+ return false;
+ return true;
+ }
+
+ virtual InitReturnVal_t Init()
+ {
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ return INIT_OK;
+ }
+};
+
+USE_UNITTEST_APPSYSTEM( CTier2TestAppSystem )
diff --git a/unittests/tier2test/tier2test.vpc b/unittests/tier2test/tier2test.vpc
new file mode 100644
index 0000000..4560e44
--- /dev/null
+++ b/unittests/tier2test/tier2test.vpc
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------------
+// TIER2TEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;TIER2TEST_EXPORTS"
+ }
+}
+
+$Project "tier2test"
+{
+ $Folder "Source Files"
+ {
+ $File "tier2test.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib mathlib
+ $Lib unitlib
+ $Lib bitmap
+ $Lib tier2
+ }
+}
diff --git a/unittests/tier3test/tier3test.cpp b/unittests/tier3test/tier3test.cpp
new file mode 100644
index 0000000..2ccd66d
--- /dev/null
+++ b/unittests/tier3test/tier3test.cpp
@@ -0,0 +1,44 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Unit test program for tier3 level libraries testing
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "unitlib/unitlib.h"
+#include "filesystem.h"
+#include "tier3/tier3.h"
+#include "mathlib/mathlib.h"
+
+
+//-----------------------------------------------------------------------------
+// Used to connect/disconnect the DLL
+//-----------------------------------------------------------------------------
+class CTier3TestAppSystem : public CTier3AppSystem< IAppSystem >
+{
+ typedef CTier3AppSystem< IAppSystem > BaseClass;
+
+public:
+ virtual bool Connect( CreateInterfaceFn factory )
+ {
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !g_pFullFileSystem )
+ return false;
+ return true;
+ }
+
+ virtual InitReturnVal_t Init()
+ {
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ return INIT_OK;
+ }
+};
+
+USE_UNITTEST_APPSYSTEM( CTier3TestAppSystem )
diff --git a/unittests/tier3test/tier3test.vpc b/unittests/tier3test/tier3test.vpc
new file mode 100644
index 0000000..8cd6d62
--- /dev/null
+++ b/unittests/tier3test/tier3test.vpc
@@ -0,0 +1,41 @@
+//-----------------------------------------------------------------------------
+// TIER3TEST.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;TIER3TEST_EXPORTS"
+ }
+}
+
+$Project "tier3test"
+{
+ $Folder "Source Files"
+ {
+ $File "tier3test.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\movieobjects\dmx_to_vcd.h"
+ $File "$SRCDIR\public\interpolatortypes.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib mathlib
+ $Lib unitlib
+ $Lib bitmap
+ $Lib tier2
+ $Lib tier3
+ }
+}