Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA
2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -0,0 +1,25 @@
This document is going to be a place to put descriptions of our reasoning for any "confusing" design decisions made during the conversion to DX8.
TODO: WRITE PROPER DESCRIPTIONS FOR ALL OF THE FOLLOWING!!!
Lighting
--------
The user has two choices regarding lighting. SimpleSceneClass will just install the first four LightClasses
into DX8 and let them affect all objects. Otherwise, you need to implement a SceneClass which wraps each
top-level render object and associates a LightEnvironmentClass with it. In your 'Customized_Render' function
you need to update each object's LightEnvironment object from the lights around it.
Mesh Rendering System
---------------------
Grouped by FVF format and material settings. Many meshes can share a single VB and IB...
MeshModel Sharing
-----------------
It would be nice if MeshClass just Add_Ref'd the same mesh model whenever we re-use a mesh. The problems with
this are:
- the transform which is used during deffered rendering and during decal rendering
- instance specific light environment pointer is in the mesh model class
- lots of other stuff <-- FILL THIS IN!

View File

@@ -0,0 +1,274 @@
Now we've got WW3D up and running with the skeleton app. Here is a somewhat more detailed/organized
list of what is left to do:
LIMITATIONS/POTENTIAL PROBLEMS:
- Skins need to use a limited set of rendering features?
- Polys going into the alpha system must use a limited set of rendering features.
- Additional procedural material passes cannot be applied to alpha blended meshes (no multi-pass alpha)
- Meshes only get two vertex color arrays. Their vertex materials must be configured properly to enable them.
- HY: Simplescene supports only four global lights
- HY: WW3D Spoltlights have an approximate inverse linear attentuation instead of max linear attentuation
UPDATED TODO LIST:
agg_def:
- Texture replacement feature needs to be implemented
assetmgr:
- Texture cache,
- Dazzle loader,
bmp2d
- works. Code for setting aspect ratio was added but not used due to legacy - HY
boxrobj
- Alpha blending support needed
- Vis rendering code needed
camera
- *** CameraClass projection convention changed to -1<x<1... Fix all code in commando and E&B!
- Also convert all uses of CameraClass::Un_Project!
cullablematpass
- collapsed into matpass
dazzle
- Fully commented out
decalmsh
- works
decalsys
- DistAlphaVP to be converted (renegade)
dx8wrapper
- Toggle_Windowed function commented out
- One-time init functions commented out (TextureLoader,SegmentedLine,Dazzle)
dynamesh
- works except for alpha support
font3d
- works
intersec
- Compiles but not tested,
- Probably contains a lot of un-used functions because all intersect calls are now routing through Cast_Ray!
line3d
- works except for alpha sorting
mapper
- All mappers need to properly handle the u-v array index
- Animating1DMapper commented out
- Axial mapper commented out
- Silhouette mapper commented out
matrixmapper
- All mappers need to properly handle the u-v array index
matinfo
- Texture reduction factor access commented out
matpass
- works except for skins and culling
mesh
- static sort list code commented out
- vis rendering code commented out
- ORIENTED and ALIGNED features broken
- dependencies code commented out
meshmdl
- shadow rendering
- vis rendering
- get_deformed_screenspace_vertices commented out (Can we delete this? skins are working...)
- get_deformed_eyespace_vertices commented out (Can we delete this? skins are working...)
- Render_Prelit
- needs to use the dynamic index buffer when rendering cullable material passes
meshmdlio
- animated texture loading not implemented
- use mesh TWOSIDED flag to set shaders on loading (and take out Jani's temp code to do this on the fly)
metalmap
- fully commented out
pointgr
- works except for alpha support
r3dobj
- fully commented out - may be able to DELETE
render2d
- works
rendobj
- texture_reduction feature commented out - may not be needed (only have a global texture reduction factor?)
rinfo
- surrender-specific vis code
ringobj
- works
- no alpha support
- vis rendering code commented out
scene
- static sort list code commented out
segline
- material handling code commented out
- rendering code commented out
shattersystem
- works, but might not for multi-texturing.
- Naty check if dynamesh supports multi-texturing. you changed the interface
- a bit. HY
sphereobj
- works
- no alpha support
- no viz
- might have bug in sphere generation when sphere is too big
sr_util
- DELETED!
statistics
- fully commented out
texfcach
- fully commented out
texproject
- works except for the second stage stuff (is this generally broken?).
textdraw
- works. This is a legacy font system to be removed from Renegade.
texture
- lots of miscellaneous missing features here
- NoLOD flag to be supported for UI textures
texturefile
- fully commented out
textureloader
- fully commented out, this is the background loading system
txt.cpp
- works. This is a legacy font system to be removed from E&B.
txt2d.cpp
- works. This is a legacy font system to be removed from E&B.
vertmaterial
- all uses of VertexMaterialClass may need to configure the UV-array source index.
- all users of VMC must properly enable/disable lighting
- all users of VMC must configure the diffuse and emissive color source if they want per-vertex arrays...
- changed so the default is visible - HY
- TODO: Make Get_Preset const so no one can modify the static presets
ww3d
- TextureLoader support commented out of Begin_Render
- Statistics code commented out
- Read_Gerd_Render_Device_Description commented out (?)
- Set/Get_Texture_Reduction commented out
- Flush_Texture_Cache function commented out
- Detect_Drivers commented out
- On_Activate/Deactivate_App commented out
- Update_Pixel_Center commented out
General:
- Add support for multiple texture stages
- Create sort system
- Add support for exclusion masks. The way this works is that we support exclusion masks differently at the top-level object level and the low-level rendering object level (note that this means that the mid-levels will get skipped over unless we collapse the container bits together with the low-level object bits. However we probably cannot do this, since the subobjects bits may be more permissive than the container's bits. Should we worry about this?). At the top level, when the light environment is constructed, we filter the light's exclusion bits vs. the light environments' exclusion bits (passed into the construct call, stored in the light environment at creation, or something). If it does not pass, it is not inserted into the light environment. When we insert a light into the light environment, we also store its exclusion bits (perhaps we only do this if "light aggregation" is turned off - see below). Then the low-level code for applying the current light environment receives the low-level robj's exclusion bits, compares them, and decides whether to actually set the light into D3D. For this to work properly, we need a flag to prevent lights which are rejected from the light environment because it is full of more-important lights from contributing to the ambient.
- Change the "Get_Texture_Array" functions in MeshMatDescClass to private, change name to PEEK!
- have texture category sorting functions take account of both texture stages
NOTE: We have three enums/#defines/consts for the number of texture stages:
RENDERER_MAX_TEX_STAGES, MeshMatDescClass::MAX_TEX_STAGES, MeshBuilderClass::MAX_STAGES.
We are even explicitly comparing two of them and asserting if they are different in
dx8renderer.cpp. There is a similar situation with DX8FVFCategoryContainer::MAX_PASSES,
MeshMatDescClass::MAX_PASSES and MeshBuilderClass::MAX_PASSES. We need to clean this up!
Features we are removing support for (will need application changes):
- indirect textures (we will provide a (slow) function to swap textures)
Ok, the WW3D2 skeleton program compiles and links now with no references to SR. Some files I took more care on, others I blocked out the entire file for later. Every piece of code is wrapped with #ifdef WW3D_DX8 with a matching #endif and comment, there should be no typo's in the #ifdef's because I used a macro to insert them all (so we can do searches and safely assume that we see all of them).
Here are some (raw) notes I took while commenting out all references to SR:
X - VertexMaterialClass - need to separate from sr (SOON)
X - Basic texture functionality (loading from files, etc)
- More advanced texture handling code needs to be re-implemented, but try to maintain our current interfaces as much as possible. Many issues here: background thread loader, texture file cache, texture LOD, indirect textures ?... (SOON)
X - ShaderClass - just convert to our own, mimicing the bitfield that SR used for now (SOON)
- Exclusion masks - this is not supported but some application code is probably using it. We need to decide what to do here.
- Camera aligned and camera oriented meshes - the way we used to do them required the mesh push transform code to change the camera matrix, which doesn't work well with our current scheme. We need to think how to support this stuff.
X - MaterialInfo Class - can un-comment when we have the above material systems in place, evaluate where this is used?
- sr_util - all of these functions un-needed? (no more As_srVector3 :-)
- agg_def.cpp - all texture features commented out
X - box render objects completely commented out - materials, gerd
- texturefcache - commented out
- TextureFileClass - commented out
X - DynaMeshClass - commented out, need materials, gerd replacement, new rendering interface before we can bring this back. A *lot* of stuff uses DynaMeshClass... (shatter system, text systems, etc)
- Dazzles - commented out, gerd, materials
- streaming textures - commented out, new texture code needed
- texture creation functions in assetmanager commented out
X - Bitmap2DObjClass - commented out, do we need this any more? - depends on dynamesh
X - BW Render, used for shadow rendering, uses some srVectorProcessor stuff
X - Camera - internals commented out
X - MatPass, CullableMatPass - all procedural material passes commented out, design new, low-level rendering interface, then port these to it.
X - passdata.h - no sure what this is for, it is commented out
X - meshmodel - all material functions commented out, will also probably re-write the guts of meshmodel to store "sub-meshes" rather than material arrays. depends on design of new low-level rendering system. (SOON)
X - decal system - all material handling commented out, rendering commented out, depends on new low-level rendering system
X - matrix mapper - this is used to generate u-v coordinates in a surrender vertex processor, new system needs to support this functionality, then we can port this (well, it will use DX8 features to do the same thing in HW...)
X - intersec.h - commented out, Eric Cosky's collision code, do we need this any more?
X - dx8renderer - uses srVectorProcessor
X - dx8wrapper - uses Gerd
X - fog.cpp - all fog objects commented out, probably won't implement fog with a render object, just have fog settings be stored in the scene
X - font3d - surface loading! textures
X - lightclass - hmmmm, re-consider lighting...
X - lightenv - uses lightclass as input.
X - line3d - uses srModeller, srMeshModel. Do we need this any more?
X - mapper.cpp - srVertexProcessor, need to convert to HW form (DELETED)
X - mesh - materials, gerd transforms, meshmodel
X - meshgeometry - uses srVectorProcessor in a couple of places
X - meshmdl - lots of material and gerd stuff, re-design?
- metalmap - uses vectorprocessor, textures
X - pointgroups - surrender types, gerd rendering, materials
X - particle buffer - needs point group
X - particle emitter - materials, particle buffer
X - particle loader - needs particle classes, materials
X - polyinfo - don't know what this is?
X - projector - built on top of matrix mapper, used for projected textures
X - r3dobj - image3dobj, can we delete this?
X - render2d - textures, gerd calls
X - rendertype - lots of GERD stuff, Jani is this an "obsolete" optimization?
- rinfo - material passes, gerd stuff, vis commands which use ext-functions for the surrender software rasterizer.
X - ring render object - materials, gerd rendering calls
X - scene - fog settings, don't use FogClass any more, just plug fog settings into the new low-level interface...
- scene - ensure that the scene ambient light value gets added into the light environment's effective ambient (E&B uses the scene ambient and currently it doesn't do anything)
- segline - custom gerd pipeline, materials
X - shatter system - dynamic meshes, materials.
X - sphere - materials, gerd rendering calls
- statistics - Jani's texture stats, new texture system must support this.
- texfcache - completely commented out
X - texproject - needs procedural material pass interface, vertex processor...
X - textdraw - textures, dynamic meshes
- texture loader - is this the backround thread loader? commented out
X - txt.cpp - uses textures
X - txt2d.cpp - uses txt, dynameshes
X - vertexbuffer.cpp - not sure what this is, uses srVectorProcessor
- ww3d - lots of gerd calls, surrender types, screen capture, movie capture, texture reduction, statistics
- Investigate cost of BeginScene-EndScene, set viewport has to occur right after beginScene... Should we minimize our viewport changes? If the cost is high, we might need to separate the viewport definition from the camera...
- Silhouette mapper un-implemented
- Axial mapper un-implemented
- How should handle the extra render states for dealing with TexGen? If we apply these states, there is nothing to remove them!
-- HY render states for texgen and texmaterial are encapsulated in vertex material & mapper
-- if the vertex material detects a null mapper the defaults are set.
- Make Dynamesh support Cast_Ray so that the E&B intersection code works!
Shaders have been converted to Shader->Apply() and the state is being tracked by the shader class.
Materials have been converted to Material->Apply() which calls Mapper->Apply()
Now, some big issues we need to think about are (in no particular order):
SOME GOALS - Maintain as many existing class interfaces as we can so that our apps don't have much re-coding!
Input data is existing W3D files, no format changes or new tools! (for now anyway :-)
Incorporate the ideas in Jani's DX8 code, grouping small meshes together in larger VBs, etc
GERD Replacement - our new low-level rendering interface needs to be the replacement for everywhere we are using the GERD. It needs to supply all of the features that we are using in the GERD currently while allowing us to use Jani's mesh optimizations. There is a lot to think about here and almost everything depends on us doing this right. It also might need to serve as an abstraction layer so that I can implement a software VIS-renderer behind it.
SOLUTION: No GERD replacement, no abstraction layer. DX8Wrapper will be a thin insulation layer though.
VIS - I'll have to re-implement the rendering code for VIS. It will be a 32bit, solid fill, zbuffering software rasterizer. Not too hard but still a bit of work. Needs to be done in a way that all render objects can be rendered through it easily.
SOLUTION: Vis rendering will branch at the render object level to a new custom vis rasterizer possibly based on the bw shadow code
TEXTURES - there are many features in the textures system, the "file cache", background loading, LOD, tga loading, global texture reduction factor, etc.
SOLUTION: Naty is working on this system.
MESHMODEL - I think we'll need to do some load-time conversion of all of our mesh-models into a form similar to Jani's dx8 mesh code. More thought needs to be put into this.
ALPHA SORTING - This issue becomes especially complex when you think about mixing different types of render objects. This issue will influence how we design the low level rendering interface.
SOLUTION: There will be a custom alpha sorting/rendering system. RenderObjects will be allowed to talk to either DX8 or this system.
UNIX - It looks like Neal has put some effort into making ww3d compile under Unix. We will probably make life very difficult for him if there are DX8 calls all over the place :-)
SOLUTION: UNIX version of E&B will not use WW3D2. It only needs collision detection so we'll implement a custom solution.
Optimizations:
==============
* Currently we search a linear list for textures with the same name every time we ask
the asset manager for a new texture. We should probably use the hashtable template
for this instead.
* The VectorProcessorClass stuff is completely unoptimized - we might want to
optimize this in future.
********* NEW STUFF ************
DX8 Features which we currently do not support and might want to in future:
* Different modes than clamp and wrap - DX8 exposes all kinds of cool modes like mirror, mirror once - do we expose these in our tools? How well are they supported in hardware? If they are not universally supported then we probably don't want to expose them...
* Cube and volume textures - support via inheriting from shared base class with TextureClass?
* Anisotropic filtering: currently trilinear is enabled in the TextureClass but not in our tools. There are issues to think about for both trilinear and anisotropic like: not all hardware supports it, some hardware supports it if there is just one texture but not otherwise, or with a slowdown etc. - this is probably not something we want the artist to specify directly: perhaps the artist can mark those meshes which would benefit from "advanced filtering", and we turn it on selectively depending on hardware?
We can do fancy texture matrix operations "for free" now. (Only on Hardware T&L cards!)

View File

@@ -0,0 +1,7 @@
- Detect skins that are using materials that are too complex (max of two uv channels, one pass???)
- Remove Translucency from max plugin
- Editor has to resolve all of the cases where it is rendering to secondary windows
- VIS system needs a new z-buffering software renderer
- WWConfig needs to be updated
- Classic environment mapper and env mapper are the same thing now...

View File

@@ -0,0 +1,191 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// FramGrab.cpp: implementation of the FrameGrabClass class.
//
//////////////////////////////////////////////////////////////////////
#include "framgrab.h"
#include <stdio.h>
#include <io.h>
//#include <errno.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
FrameGrabClass::FrameGrabClass(const char *filename, MODE mode, int width, int height, int bitcount, float framerate)
{
HRESULT hr;
Mode = mode;
Filename = filename;
FrameRate = framerate;
Counter = 0;
Stream = 0;
AVIFile = 0;
if(Mode != AVI) return;
AVIFileInit(); // opens AVIFile library
// find the first free file with this prefix
int counter = 0;
int result;
char file[256];
do {
sprintf(file, "%s%d.AVI", filename, counter++);
result = _access(file, 0);
} while(result != -1);
// Create new AVI file using AVIFileOpen.
hr = AVIFileOpen(&AVIFile, file, OF_WRITE | OF_CREATE, NULL);
if (hr != 0) {
char buf[256];
sprintf(buf, "Unable to open %s\n", Filename);
OutputDebugString(buf);
CleanupAVI();
return;
}
// Create a stream using AVIFileCreateStream.
AVIStreamInfo.fccType = streamtypeVIDEO;
AVIStreamInfo.fccHandler = mmioFOURCC('M','S','V','C');
AVIStreamInfo.dwFlags = 0;
AVIStreamInfo.dwCaps = 0;
AVIStreamInfo.wPriority = 0;
AVIStreamInfo.wLanguage = 0;
AVIStreamInfo.dwScale = 1;
AVIStreamInfo.dwRate = (int)FrameRate;
AVIStreamInfo.dwStart = 0;
AVIStreamInfo.dwLength = 0;
AVIStreamInfo.dwInitialFrames = 0;
AVIStreamInfo.dwSuggestedBufferSize = 0;
AVIStreamInfo.dwQuality = 0;
AVIStreamInfo.dwSampleSize = 0;
SetRect(&AVIStreamInfo.rcFrame, 0, 0, width, height);
AVIStreamInfo.dwEditCount = 0;
AVIStreamInfo.dwFormatChangeCount = 0;
sprintf(AVIStreamInfo.szName,"G");
hr = AVIFileCreateStream(AVIFile, &Stream, &AVIStreamInfo);
if (hr != 0) {
CleanupAVI();
return;
}
// Set format of new stream
BitmapInfoHeader.biWidth = width;
BitmapInfoHeader.biHeight = height;
BitmapInfoHeader.biBitCount = (unsigned short)bitcount;
BitmapInfoHeader.biSizeImage = ((((UINT)BitmapInfoHeader.biBitCount * BitmapInfoHeader.biWidth + 31) & ~31) / 8) * BitmapInfoHeader.biHeight;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER); // size of structure
BitmapInfoHeader.biPlanes = 1; // must be set to 1
BitmapInfoHeader.biCompression = BI_RGB; // uncompressed
BitmapInfoHeader.biXPelsPerMeter = 1; // not used
BitmapInfoHeader.biYPelsPerMeter = 1; // not used
BitmapInfoHeader.biClrUsed = 0; // all colors are used
BitmapInfoHeader.biClrImportant = 0; // all colors are important
hr = AVIStreamSetFormat(Stream, 0, &BitmapInfoHeader, sizeof(BitmapInfoHeader));
if (hr != 0) {
CleanupAVI();
return;
}
Bitmap = (long *) GlobalAllocPtr(GMEM_MOVEABLE, BitmapInfoHeader.biSizeImage);
}
FrameGrabClass::~FrameGrabClass()
{
if(Mode == AVI) {
CleanupAVI();
}
}
void FrameGrabClass::CleanupAVI() {
if(Bitmap != 0) { GlobalFreePtr(Bitmap); Bitmap = 0; }
if(Stream != 0) { AVIStreamRelease(Stream); Stream = 0; }
if(AVIFile != 0) { AVIFileRelease(AVIFile); AVIFile = 0; }
AVIFileExit();
Mode = RAW;
}
void FrameGrabClass::GrabAVI(void *BitmapPointer)
{
// CompressDIB(&bi, lpOld, &biNew, lpNew);
// Save the compressed data using AVIStreamWrite.
HRESULT hr = AVIStreamWrite(Stream, Counter++, 1, BitmapPointer, BitmapInfoHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
if(hr != 0) {
char buf[256];
sprintf(buf, "avi write error %x/%d\n", hr, hr);
OutputDebugString(buf);
}
}
void FrameGrabClass::GrabRawFrame(void * /*BitmapPointer*/)
{
}
void FrameGrabClass::ConvertGrab(void *BitmapPointer)
{
ConvertFrame(BitmapPointer);
Grab( Bitmap );
}
void FrameGrabClass::Grab(void *BitmapPointer)
{
if(Mode == AVI)
GrabAVI(BitmapPointer);
else
GrabRawFrame(BitmapPointer);
}
void FrameGrabClass::ConvertFrame(void *BitmapPointer)
{
int width = BitmapInfoHeader.biWidth;
int height = BitmapInfoHeader.biHeight;
long *image = (long *) BitmapPointer;
// copy the data, doing a vertical flip & byte re-ordering of the pixel longwords
int y = height;
while(y--) {
int x = width;
int yoffset = y * width;
int yoffset2 = (height - y) * width;
while(x--) {
long *source = &image[yoffset + x];
long *dest = &Bitmap[yoffset2 + x];
*dest = *source;
unsigned char *c = (unsigned char *) dest;
c[3] = c[0];
c[0] = c[2];
c[2] = c[3];
c[3] = 0;
}
}
}

View File

@@ -0,0 +1,148 @@
MAPPER_NAME
Description:
What the mapper does
Params:
What to type in the argument space
<Argument Name> = (type) <default value>
e.g. if it says
UPerSec=(float) 0.0;
it means you have to type in
UPerSec=1.0;
to get your U coordinate scrolling at one unit per second and it has to be a float. If you don't type anything the
default value is 0.0.
Types:
(float) - any real number
(int) - any integer
(bool) - either TRUE or FALSE
LINEAR_OFFSET
Description:
Makes the texture scroll at the speed specified
Params:
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
UScale=(float) 1.0;
VScale=(float) 1.0;
CLASSIC_ENVIRONMENT
Description:
Uses the Normals to look up the environment map
ENVIRONMENT
Description:
Uses the Reflection direction to look up the environment map
SCREEN
Description:
Projects takes the screen coordinate as the UV coordinate
Params:
UScale=(float) 1.0f;
VScale=(float) 1.0f;
SILHOUETTE
Description: Obsolete, not supported
SCALE
Description:
Scales the UV coordinates. Useful for detail mapping.
Params:
UScale=(float) 1.0f;
VScale=(float) 1.0f;
GRID
Description:
Given a texture that is divided up in to a grid, it animates the texture by looking
up the texture from the topleft to the bottom right, going left to right and then
top to bottom (the same way you would read English text). The texture map must be divided
up evenly.
Params:
FPS=(float) 1.0f; The frames per second
Log2Width=(int) 1; So 0=width 1, 1=width 2, 2=width 4. The default means animate using a texture divided up into quarters.
Last=(int) GridWidth*GridWidth; The last frame to use
ROTATE
Description:
Rotates a texture map counterclockwise about a specified center then scales the texture
Params:
Speed=(float) 0.1f; In Hertz. 1 = 1 rotate per second
UCenter=(float) 0.0f;
VCenter=(float) 0.0f;
UScale=(float) 1.0;
VScale=(float) 1.0;
SINE_LINEAR_OFFSET
Description:
Moves the texture map in the shape of a Lissajous figure.
Params:
UAmp=(float) 1.0f;
UFreq=(float) 1.0f;
UPhase=(float) 0.0f;
VAmp=(float) 1.0f;
VFreq=(float) 1.0f;
VPhase=(float) 0.0f;
STEP_LINEAR_OFFSET
Description:
Similar to Linear Offset but moves stuff around in discrete steps
Params:
UStep=(float) 0.0f;
VStep=(float) 0.0f;
SPS=(float) 0.0f; Steps per second
ZIGZAG_LINEAR_OFFSET
Description:
Similar to Linear Offset but reverses direction periodically.
Params:
UPerSec=(float) 0.0f;
VPerSec=(float) 0.0f;
Period=(float) 0.0f; Time it takes to make a zigzag in seconds
WS_CLASSIC_ENVIRONMENT
Description:
World space normal environment map
WS_ENVIRONMENT
Description:
World space reflection environment map
GRID_CLASSIC_ENVIRONMENT
Description:
Animated normal environment map
GRID_ENVIRONMENT
Description:
Animated reflection environtment map
RANDOM
Description: Randomly rotates and translates a texture with linear offset
FPS=(float) 0.0f; Frames per second
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
EDGE
Description: Uses the Z-coordinate of the reflection or normal vector to access the U coordinate
The V-coordinate is linear offset
VPerSec=(float) 0.0
UseReflect=FALSE
VStart=(float) 0.0
BUMPENV
Description: Sets up and possibly animates the bump matrix, also has the LinearOffset features
NOTE: even if you don't want to animate the bump matrix, you should use this mapper
so that the matrix gets set up with the identity settings.
BumpRotation = (float) 0.1f; In Hertz. 1 = 1 rotate per second (DEFAULT = 0.0)
BumpScale = scale factor applied to the bumps (DEFAULT = 1.0)
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
UScale=(float) 1.0;
VScale=(float) 1.0;
-----------
TODO:
-the ability to affect the period of the zigzag in only one direction (V) while the other (U) continues to offset undisturbed in the original linear fashion.
-groovy scaling
-scale random
-random mapper random time

View File

@@ -0,0 +1,25 @@
HY 2/14/01 Created.
Polygons are rendered via:
A. The Mesh packet renderer
B. Render object's render method
C. Alpha sorting pipeline
NB. Shaders and Vertex Mateirals have presets that you can use to set state
Guidelines for B type polygons:
1. Always set the World matrix
2. Always use a Shader (Shader.Apply())
3. Always use a VertexMaterial (VertexMaterial.Apply())
4. If there is a Texture, use Texture.Apply()
For any other render state, save the initial state, set your state, render then
restore the original state.
e.g. save the VIEW matrix if you're nuking it, then restore it after you're done.
Guidelines for C type polygons:
1. All vertices submitted must be in View Coordinates
2. All states submitted must be entirely contained in Shader,VertexMaterial & Texture

View File

@@ -0,0 +1,161 @@
;---------------------------------------------------------------------------------------------------------------------
;
; DAZZLE.INI
; This file is a sample DAZZLE.INI file, indicating how to set up dazzles and lens
; flares for an application. PLEASE USE THIS FOR REFERENCE ONLY. You should consult the
; asset manager of your project in order to find the location of the actual DAZZLE.INI
; file that is specific to your project.
;
; PLEASE NOTE: some of the tools and library code assume that the dazzle type 'DEFAULT'
; exists and is the first dazzle type.
;
;
; DAZZLE DEFINITION PARAMETERS:
;-------------------------------
; After the dot product between the camera direction and the camera space location of the light source,
; we do a power to Halopow for the halo size and power to DazzlePow for the dazzle size. Halo/DazzleArea
; defines the angle where the values are valid, so any angle beyond HaloArea/DazzleArea results the halo or
; dazzle being scaled to zero. The angles are re-scaled from normalized angle scale of (-1.0...1.0)
; only HaloArea/DazzleArea defined part is used.
;
; HaloIntensityPow - 1.0 would be linear fadeout, smaller than that will steepen the curve (smaller hotspot)
; HaloSizePow - 1.0 would be linear fadeout, smaller than that will steepen the curve (smaller hotspot)
; DazzleIntensityPow
; DazzleSizePow
; HaloArea
; DazzleArea - Something like 0.05 is a good starting point for a dazzle...
; HaloScaleX - X scale factor for the halo
; HaloScaleY - Y scale factor for the halo
; DazzleScaleX - X scale factor for the dazzle
; DazzleScaleY - Y scale factor for the dazzle
; HaloIntensity - base intensity of the halo
; DazzleIntensity - base intensity of the dazzle
;
; Direction area defines the maximum difference between the light direction and the eyespace location,
; so the dazzle can only be seen if the camera is inside the light cone. Value 0.0 means dazzle is not
; directional, it can be seen from any direction. Halo is not affected. Dazzle direction defines the light
; direction vector.
;
; DazzleDirectionArea - Something like 0.5 might be a good test value
; DazzleDirection - direction vector (gth - we should lock this paramter to 0,0,1)
; FadeoutStart - distance in meters where the halo and dazzle start to fade out
; FadeoutEnd - distance in meters where the halo and dazzle are completely faded out
; SizeOptimizationLimit- NOT FINISHED
; HistoryWeight - The weight of history for the intensities. The history weight is per millisecond,
; so if you want to have any real history, values higher than 0.95 are recommended
; (don't use value of 1.0 or anything higher!)
; DazzleTextureName - texture filename for the dazzle
; HaloTextureName - texture filename for the halo
; UseCameraTranslation - If false, camera matrix's translation term isn't used. If translation isn't used
; the dazzle is treated as always visible, scene graph visibility is not used.
; HaloColor - color of the halo
; DazzleColor - color of the dazzle
;
; DazzleTestColor - Dazzle test color could be white in many cases but if the level has a lot of
; white then another color can be defined.
; LensflareName - Dazzle can use a lensflare by defining a name of lensflare in the ini
;
;
; LENSFLARE DEFINITION PARAMETERS:
;----------------------------------
;
; TextureName - Texture to be used by the lensflare
; FlareCount - The number of flare sprites in lensflare. The FlareLocation, FlareSize and FlareColor
; paremeters are procedural, the names are produced based on this parameter. If
; FlareCount is 3, there exists FlareLocation1, FlareLocation2 and FlareLocation3... And so on.
; FlareLocation - 1D-locations of the flares. 0.0 means the location of the center of the screen and
; 1.0 means the location of the lightsource. The values can be smaller than zero and
; larger than 1.0.
; FlareSize - Normalized flare sizes
; FlareColor - Colors for each flare sprite
; FlareUV - Uv coordinates (as there can be only one texture used, but one may want multiple images).
; The values is a 4-float vector: start_u, start_v, end_u, end_v.
;
;---------------------------------------------------------------------------------------------------------------------
[Dazzles_List]
0=DEFAULT
1=SUN
[Lensflares_List]
0=SUN_LENSFLARE
;========================================== DAZZLE DEFINITIONS ====================
[DEFAULT]
HaloIntensity=1.0
HaloIntensityPow=0.95
HaloSizePow=0.95
HaloArea=1.0
HaloScaleX=0.2
HaloScaleY=0.2
DazzleArea=0.05
DazzleDirectionArea=0
DazzleDirection=0,1,1
DazzleSizePow=0.9
DazzleIntensityPow=0.9
DazzleIntensity=50
DazzleScaleX=1.0
DazzleScaleY=1.0
FadeoutStart=30.0
FadeoutEnd=40.0
SizeOptimizationLimit=0.05
HistoryWeight=0.975
UseCameraTranslation=1
HaloTextureName=SunHalo.tga
DazzleTextureName=SunDazzle.tga
DazzleColor=1,1,1
HaloColor=1,1,1
DazzleTestColor=1,1,1
[SUN]
HaloSizePow=1.0
HaloIntensityPow=1.0
HaloArea=1.0
HaloScaleX=0.25
HaloScaleY=0.25
DazzleArea=0.04
DazzleDirectionArea=0
DazzleDirection=0,1,1
DazzleSizePow=1.0
DazzleIntensityPow=1.0
DazzleIntensity=1.0
DazzleScaleX=4.0
DazzleScaleY=4.0
FadeoutStart=20.0
FadeoutEnd=30.0
SizeOptimizationLimit=0.05
HistoryWeight=0.1
UseCameraTranslation=0
HaloTextureName=SunHalo.tga
DazzleTextureName=SunDazzle.tga
DazzleColor=1.0,1.0,1.0
LensflareName=SUN_LENSFLARE
;========================================== LENSFLARE DEFINITIONS =================
[SUN_LENSFLARE]
TextureName=SunLensFlare.tga
FlareCount=6
FlareLocation1=1.3
FlareLocation2=0.4
FlareLocation3=-0.1
FlareLocation4=-0.6
FlareLocation5=-0.9
FlareLocation6=-1.2
FlareSize1=0.06
FlareSize2=0.04
FlareSize3=0.03
FlareSize4=0.15
FlareSize5=0.20
FlareSize6=0.30
FlareUV1=0.000,0.000,0.118,0.238
FlareUV2=0.134,0.048,0.204,0.190
FlareUV3=0.228,0.079,0.276,0.175
FlareUV4=0.000,0.619,0.165,1.000
FlareUV5=0.181,0.365,0.488,1.000
FlareUV6=0.496,0.000,1.000,1.000

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,173 @@
State Management Responsibilities
Key:
G - Global
DX8W - DX8 Wrapper
LE - Wrapper::Set Light Environment
DX8R - DX8 Renderer (affects meshes only)
Shader - ShaderClass
VM - VertexMaterial
Map - Mapper (part of VertexMaterial)
Texture - TextureClass
WW3D
_D3DRENDERSTATETYPE {
ZENABLE = 7,
FILLMODE = 8, WW3D
SHADEMODE = 9,
LINEPATTERN = 10,
ZWRITEENABLE = 14, Shader
ALPHATESTENABLE = 15, Shader
LASTPIXEL = 16,
SRCBLEND = 19, Shader
DESTBLEND = 20, Shader
CULLMODE = 22, Shader
ZFUNC = 23, Shader
ALPHAREF = 24, Shader
ALPHAFUNC = 25, Shader
DITHERENABLE = 26, G
ALPHABLENDENABLE = 27, Shader
FOGENABLE = 28, Shader
SPECULARENABLE = 29, Shader
ZVISIBLE = 30,
FOGCOLOR = 34, Shader
FOGTABLEMODE = 35, G
FOGSTART = 36, Scene
FOGEND = 37, Scene
FOGDENSITY = 38,
EDGEANTIALIAS = 40,
ZBIAS = 47, G (set to zero after device is initted)
RANGEFOGENABLE = 48, G
STENCILENABLE = 52,
STENCILFAIL = 53,
STENCILZFAIL = 54,
STENCILPASS = 55,
STENCILFUNC = 56,
STENCILREF = 57,
STENCILMASK = 58,
STENCILWRITEMASK = 59,
TEXTUREFACTOR = 60,
WRAP0 = 128,
WRAP1 = 129,
WRAP2 = 130,
WRAP3 = 131,
WRAP4 = 132,
WRAP5 = 133,
WRAP6 = 134,
WRAP7 = 135,
CLIPPING = 136,
LIGHTING = 137, VM
AMBIENT = 139, LE
FOGVERTEXMODE = 140, G
COLORVERTEX = 141, G
LOCALVIEWER = 142,
NORMALIZENORMALS = 143,
DIFFUSEMATERIALSOURCE = 145, VM
SPECULARMATERIALSOURCE = 146, G
AMBIENTMATERIALSOURCE = 147, VM, set to same setting as diffuse material source
EMISSIVEMATERIALSOURCE = 148, VM
VERTEXBLEND = 151,
CLIPPLANEENABLE = 152,
SOFTWAREVERTEXPROCESSING = 153, G
POINTSIZE = 154,
POINTSIZE_MIN = 155,
POINTSPRITEENABLE = 156,
POINTSCALEENABLE = 157,
POINTSCALE_A = 158,
POINTSCALE_B = 159,
POINTSCALE_C = 160,
MULTISAMPLEANTIALIAS = 161,
MULTISAMPLEMASK = 162,
PATCHEDGESTYLE = 163,
PATCHSEGMENTS = 164,
DEBUGMONITORTOKEN = 165,
POINTSIZE_MAX = 166,
INDEXEDVERTEXBLENDENABLE = 167,
COLORWRITEENABLE = 168,
TWEENFACTOR = 170,
BLENDOP = 171,
FORCE_DWORD = 0x7fffffff
} D3DRENDERSTATETYPE;
_D3DTEXTURESTAGESTATETYPE {
COLOROP = 1, Shader stages 0,1 only
COLORARG1 = 2, Shader stages 0,1 only
COLORARG2 = 3, Shader stages 0,1 only
ALPHAOP = 4, Shader stages 0,1 only
ALPHAARG1 = 5, Shader stages 0,1 only
ALPHAARG2 = 6, Shader stages 0,1 only
BUMPENVMAT00 = 7,
BUMPENVMAT01 = 8,
BUMPENVMAT10 = 9,
BUMPENVMAT11 = 10,
TEXCOORDINDEX = 11, DX8R, Map
ADDRESSU = 13, Texture
ADDRESSV = 14, Texture
BORDERCOLOR = 15,
MAGFILTER = 16, Texture
MINFILTER = 17, Texture
MIPFILTER = 18, Texture
MIPMAPLODBIAS = 19,
MAXMIPLEVEL = 20,
MAXANISOTROPY = 21,
BUMPENVLSCALE = 22,
BUMPENVLOFFSET = 23,
TEXTURETRANSFORMFLAGS = 24, Map
ADDRESSW = 25,
COLORARG0 = 26,
ALPHAARG0 = 27,
RESULTARG = 28,
FORCE_DWORD = 0x7fffffff
} D3DTEXTURESTAGESTATETYPE;
Variables
_D3DTRANSFORMSTATETYPE {
D3DTS_VIEW = 2, Pointgroup (restores), Camera
D3DTS_PROJECTION = 3, Camera
D3DTS_TEXTURE0 = 16,
D3DTS_TEXTURE1 = 17,
D3DTS_TEXTURE2 = 18,
D3DTS_TEXTURE3 = 19,
D3DTS_TEXTURE4 = 20,
D3DTS_TEXTURE5 = 21,
D3DTS_TEXTURE6 = 22,
D3DTS_TEXTURE7 = 23,
D3DTS_FORCE_DWORD = 0x7fffffff
D3DTS_WORLDMATRIX(0) = box, pointgroup, camera
} D3DTRANSFORMSTATETYPE;
Clip Planes =
Clip Status =
Current Texture Palette =
Cursor Position =
Cursor Properties =
Gamma Ramp =
Indices = DX8W
Light = LE
Material = VM
Palette Entries =
Pixel Shader =
Pixel Shader Const =
Render Target =
Stream Source = DX8W
Texture = DX8W
WORLD Transform = box, DX8R, pointgr
VIEW Transform = pointgr
PROJECTION Transform = camera
TEXTURE Transform = Map
Vertex Shader = DX8W
Vertex Shader Const =
Viewport = Camera, WW3D

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,354 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtree.h $*
* *
* Org Author:: Greg Hjelstrom *
* *
* Author:: Kenny Mitchell *
* *
* $Modtime:: 6/26/02 2:58p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* 06/26/02 KM Integrating shader system
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABTREE_H
#define AABTREE_H
#include "always.h"
#include "refcount.h"
#include "simplevec.h"
#include "vector3.h"
#include "vector3i.h"
#include "aaplane.h"
#include "bittype.h"
#include "colmath.h"
#include "wwdebug.h"
#include "aabtreebuilder.h"
#include "obbox.h"
#include <tri.h>
#include <float.h>
class MeshClass;
class CameraClass;
class RayCollisionTestClass;
class AABoxCollisionTestClass;
class OBBoxCollisionTestClass;
class OBBoxIntersectionTestClass;
class ChunkLoadClass;
class ChunkSaveClass;
class MeshGeometryClass;
class OBBoxClass;
class ChunkLoadClass;
struct BoxRayAPTContextStruct;
#define AABTREE_LEAF_FLAG 0x80000000
/*
** AABTreeClass
** This class encapsulates an Axis-Aligned Bounding Box Tree for a mesh. This tree
** can be used to perform hierarchical culling for collision detection. Note that
** this class is constructed using the AABTreeBuilderClass; these two classes are
** very tightly coupled. Pretty much the only code which needs to know about the AABTreeClass
** is in MeshGeometryClass. I moved these out into a separate file just to reduce the
** size of meshmdl.cpp.
*/
class AABTreeClass : public W3DMPO, public RefCountClass
{
W3DMPO_GLUE(AABTreeClass)
public:
AABTreeClass(void);
AABTreeClass(AABTreeBuilderClass * builder);
AABTreeClass(const AABTreeClass & that);
~AABTreeClass(void);
void Load_W3D(ChunkLoadClass & cload);
// Uniformly scale the AABTree
void Scale(float scale);
int Get_Node_Count(void) { return NodeCount; }
int Get_Poly_Count(void) { return PolyCount; }
int Compute_Ram_Size(void);
void Generate_APT(const OBBoxClass & box,SimpleDynVecClass<uint32> & apt);
void Generate_APT(const OBBoxClass & box,const Vector3 & viewdir,SimpleDynVecClass<uint32> & apt);
bool Cast_Ray(RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray(const Vector3 & start_point,
int axis_dir, unsigned char & flags);
bool Cast_AABox(AABoxCollisionTestClass & boxtest);
bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
void Set_Mesh(MeshGeometryClass * mesh);
private:
AABTreeClass & operator = (const AABTreeClass & that);
void Read_Poly_Indices(ChunkLoadClass & cload);
void Read_Nodes(ChunkLoadClass & cload);
void Build_Tree_Recursive(AABTreeBuilderClass::CullNodeStruct * node,int &curpolyindex);
void Reset(void);
void Update_Bounding_Boxes(void);
void Update_Min_Max(int index,Vector3 & min,Vector3 & max);
/*
** CullNodeStruct - the culling tree is built out of an array of these structures
** They contain the extents of an axis-aligned box, indices to children nodes,
** and indices into the polygon array
** (05/22/2000 gth - changed this structure to support either child nodes -or-
** a polygon array but not both at the same time. Also switched to 32bit indices
** so that the code doesn't become useless as quickly )
*/
struct CullNodeStruct
{
Vector3 Min;
Vector3 Max;
uint32 FrontOrPoly0;
uint32 BackOrPolyCount;
// accessors
inline bool Is_Leaf(void);
inline int Get_Back_Child(void); // returns index of back child (only call for non-LEAFs!!!)
inline int Get_Front_Child(void); // returns index of front child (only call for non-LEAFs!!!)
inline int Get_Poly0(void); // returns index of first polygon (only call on LEAFs)
inline int Get_Poly_Count(void); // returns polygon count (only call on LEAFs)
// initialization
inline void Set_Front_Child(uint32 index);
inline void Set_Back_Child(uint32 index);
inline void Set_Poly0(uint32 index);
inline void Set_Poly_Count(uint32 count);
};
/*
** OBBoxAPTContextStruct - this is a temporary datastructure used in building
** an APT by culling the mesh to an oriented bounding box.
*/
struct OBBoxAPTContextStruct
{
OBBoxAPTContextStruct(const OBBoxClass & box,SimpleDynVecClass<uint32> & apt) :
Box(box), APT(apt)
{ }
OBBoxClass Box;
SimpleDynVecClass<uint32> & APT;
};
/**
** OBBoxRayAPTContextStruct - temporary datastructure used in building an APT
** by culling the mesh to a oriented box and eliminating backfaces to a ray.
*/
struct OBBoxRayAPTContextStruct
{
OBBoxRayAPTContextStruct(const OBBoxClass & box,const Vector3 & viewdir,SimpleDynVecClass<uint32> & apt) :
Box(box),
ViewVector(viewdir),
APT(apt)
{ }
OBBoxClass Box;
Vector3 ViewVector;
SimpleDynVecClass<uint32> & APT;
};
void Generate_OBBox_APT_Recursive(CullNodeStruct * node,OBBoxAPTContextStruct & context);
void Generate_OBBox_APT_Recursive(CullNodeStruct * node, OBBoxRayAPTContextStruct & context);
bool Cast_Ray_Recursive(CullNodeStruct * node,RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray_Recursive(CullNodeStruct * node, const Vector3 & start_point,
int axis_r, int axis_1, int axis_2, int direction, unsigned char & flags);
bool Cast_AABox_Recursive(CullNodeStruct * node,AABoxCollisionTestClass & boxtest);
bool Cast_OBBox_Recursive(CullNodeStruct * node,OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox_Recursive(CullNodeStruct * node,OBBoxIntersectionTestClass & boxtest);
bool Cast_Ray_To_Polys(CullNodeStruct * node,RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray_To_Polys(CullNodeStruct * node, const Vector3 & start_point,
int axis_r, int axis_1, int axis_2, int direction, unsigned char & flags);
bool Cast_AABox_To_Polys(CullNodeStruct * node,AABoxCollisionTestClass & boxtest);
bool Cast_OBBox_To_Polys(CullNodeStruct * node,OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox_With_Polys(CullNodeStruct * node,OBBoxIntersectionTestClass & boxtest);
void Update_Bounding_Boxes_Recursive(CullNodeStruct * node);
int NodeCount; // number of nodes in the tree
CullNodeStruct * Nodes; // array of nodes
int PolyCount; // number of polygons in the parent mesh (and the number of indexes in our array)
uint32 * PolyIndices; // linear array of polygon indices, nodes index into this array
MeshGeometryClass * Mesh; // pointer to the parent mesh (non-ref-counted; we are a member of this mesh)
friend class MeshClass;
friend class MeshGeometryClass;
friend class AuxMeshDataClass;
friend class AABTreeBuilderClass;
};
inline int AABTreeClass::Compute_Ram_Size(void)
{
return NodeCount * sizeof(CullNodeStruct) +
PolyCount * sizeof(int) +
sizeof(AABTreeClass);
}
inline bool AABTreeClass::Cast_Ray(RayCollisionTestClass & raytest)
{
WWASSERT(Nodes != NULL);
return Cast_Ray_Recursive(&(Nodes[0]),raytest);
}
inline int AABTreeClass::Cast_Semi_Infinite_Axis_Aligned_Ray(const Vector3 & start_point,
int axis_dir, unsigned char & flags)
{
// These tables translate between the axis_dir representation (which is an integer in which 0
// indicates a ray along the positive x axis, 1 along the negative x axis, 2 the positive y
// axis, 3 negative y axis, 4 positive z axis, 5 negative z axis) and a four-integer
// representation (axis_r is the axis number - 0, 1 or 2 - of the axis along which the ray is
// cast; axis_1 and axis_2 are the axis numbers of the other two axes; direction is 0 for
// negative and 1 for positive direction of the ray).
static const int axis_r[6] = { 0, 0, 1, 1, 2, 2 };
static const int axis_1[6] = { 1, 1, 2, 2, 0, 0 };
static const int axis_2[6] = { 2, 2, 0, 0, 1, 1 };
static const int direction[6] = { 1, 0, 1, 0, 1, 0 };
WWASSERT(Nodes != NULL);
WWASSERT(axis_dir >= 0);
WWASSERT(axis_dir < 6);
// The functions called after this point will 'or' bits into this variable, so it needs to
// be initialized here to TRI_RAYCAST_FLAG_NONE.
flags = TRI_RAYCAST_FLAG_NONE;
return Cast_Semi_Infinite_Axis_Aligned_Ray_Recursive(&(Nodes[0]), start_point,
axis_r[axis_dir], axis_1[axis_dir], axis_2[axis_dir], direction[axis_dir], flags);
}
inline bool AABTreeClass::Cast_AABox(AABoxCollisionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Cast_AABox_Recursive(&(Nodes[0]),boxtest);
}
inline bool AABTreeClass::Cast_OBBox(OBBoxCollisionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Cast_OBBox_Recursive(&(Nodes[0]),boxtest);
}
inline bool AABTreeClass::Intersect_OBBox(OBBoxIntersectionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Intersect_OBBox_Recursive(&(Nodes[0]),boxtest);
}
inline void AABTreeClass::Update_Bounding_Boxes(void)
{
WWASSERT(Nodes != NULL);
Update_Bounding_Boxes_Recursive(&(Nodes[0]));
}
/***********************************************************************************************
AABTreeClass::CullNodeStruct implementation
These nodes can be either leaf nodes or non-leaf nodes. If they are leaf nodes, they
will contain an index to their first polygon index and a polygon count. If they are
non-leafs they will contain indices to their front and back children. Since I'm re-using
the same variables for the child indices and the polygon indices, you have to call
the Is_Leaf function then only call the appropriate functions. The flag indicating whether
this node is a leaf is stored in the MSB of the FrontOrPoly0 variable. It will always
be stripped off by these accessor functions
***********************************************************************************************/
inline bool AABTreeClass::CullNodeStruct::Is_Leaf(void)
{
return ((FrontOrPoly0 & AABTREE_LEAF_FLAG) != 0);
}
inline int AABTreeClass::CullNodeStruct::Get_Front_Child(void)
{
WWASSERT(!Is_Leaf());
return FrontOrPoly0; // we shouldn't be calling this on a leaf and the leaf bit should be zero...
}
inline int AABTreeClass::CullNodeStruct::Get_Back_Child(void)
{
WWASSERT(!Is_Leaf());
return BackOrPolyCount;
}
inline int AABTreeClass::CullNodeStruct::Get_Poly0(void)
{
WWASSERT(Is_Leaf());
return (FrontOrPoly0 & ~AABTREE_LEAF_FLAG);
}
inline int AABTreeClass::CullNodeStruct::Get_Poly_Count(void)
{
WWASSERT(Is_Leaf());
return BackOrPolyCount;
}
inline void AABTreeClass::CullNodeStruct::Set_Front_Child(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
FrontOrPoly0 = index;
}
inline void AABTreeClass::CullNodeStruct::Set_Back_Child(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
BackOrPolyCount = index;
}
inline void AABTreeClass::CullNodeStruct::Set_Poly0(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
FrontOrPoly0 = (index | AABTREE_LEAF_FLAG);
}
inline void AABTreeClass::CullNodeStruct::Set_Poly_Count(uint32 count)
{
WWASSERT(count < 0x7FFFFFFF);
BackOrPolyCount = count;
}
#endif

View File

@@ -0,0 +1,998 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtreebuilder.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/24/01 5:49p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* AABTreeBuilderClass::AABTreeBuilderClass -- Constructor *
* AABTreeBuilderClass::~AABTreeBuilderClass -- Destructor *
* AABTreeBuilderClass::Reset -- reset the builder, delete all arrays *
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* AABTreeBuilderClass::Build_Tree -- recursivly builds the culling tree *
* AABTreeBuilderClass::Select_Splitting_Plane -- select a partition for the given polys *
* AABTreeBuilderClass::Compute_Plane_Score -- evaluate the suitability of a partition plane *
* AABTreeBuilderClass::Which_Side -- which side of a plane is the given poly *
* AABTreeBuilderClass::Split_Polys -- partition the polys with a plane *
* AABTreeBuilderClass::Compute_Bounding_Box -- compute bounding boxes for the cull nodes *
* AABTreeBuilderClass::Assign_Index -- assign an array index to each node *
* AABTreeBuilderClass::Node_Count -- Count the nodes in the tree *
* AABTreeBuilderClass::Poly_Count -- returns number of polys *
* AABTreeBuilderClass::Node_Count_Recursive -- internal implementation of Node_Count *
* AABTreeBuilderClass::Submit_Tree -- install nodes into an AABTreeClass *
* AABTreeBuilderClass::Submit_Tree_Recursive -- internal implementation of Submit_Tree *
* AABTreeBuilderClass::Update_Min -- ensure given vector is < min of the poly *
* AABTreeBuilderClass::Update_Max -- ensure given vector is > max of poly *
* AABTreeBuilderClass::Update_Min_Max -- ensure given vector is in min max of poly *
* AABTreeBuilderClass::Export -- Saves this AABTree into a W3D chunk *
* AABTreeBuilderClass::Build_W3D_AABTree_Recursive -- Build array of indices and W3dMeshAAB *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "aabtreebuilder.h"
#include "chunkio.h"
#include "w3d_file.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#undef WWASSERT
#define WWASSERT assert // can't use WWASSERT because we use this module in the MAX plugin...
const float COINCIDENCE_EPSILON = 0.001f;
/***********************************************************************************************
* AABTreeBuilderClass::AABTreeBuilderClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AABTreeBuilderClass::AABTreeBuilderClass(void) :
Root(NULL),
CurPolyIndex(0),
PolyCount(0),
Polys(NULL),
VertCount(0),
Verts(NULL)
{
}
/***********************************************************************************************
* AABTreeBuilderClass::~AABTreeBuilderClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/19/2000 gth : Created. *
*=============================================================================================*/
AABTreeBuilderClass::~AABTreeBuilderClass(void)
{
Reset();
}
/***********************************************************************************************
* AABTreeBuilderClass::Reset -- reset the builder, delete all arrays *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/19/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Reset(void)
{
if (Root) {
delete Root; Root = NULL;
}
if (Verts != NULL) {
delete[] Verts;
Verts = NULL;
}
if (Polys != NULL) {
delete[] Polys;
Polys = NULL;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_AABTree(int polycount,TriIndex * polys,int vertcount,Vector3 * verts)
{
WWASSERT(polycount > 0);
WWASSERT(vertcount > 0);
WWASSERT(polys != NULL);
WWASSERT(verts != NULL);
/*
** If we already have allocated data, release it
*/
Reset();
/*
** Copy the mesh data
*/
VertCount = vertcount;
PolyCount = polycount;
Verts = W3DNEWARRAY Vector3[VertCount];
Polys = W3DNEWARRAY TriIndex[PolyCount];
for (int vi=0; vi<VertCount; vi++) {
Verts[vi] = verts[vi];
}
for (int pi=0; pi<PolyCount; pi++) {
Polys[pi] = polys[pi];
}
/*
** First, create a list of all of the poly indices
*/
int * polyindices = W3DNEWARRAY int[PolyCount];
for (int i=0; i<PolyCount; i++) {
polyindices[i] = i;
}
/*
** Build the tree, note that the array of poly indices will be
** deleted by the Build_Tree function.
*/
Root = W3DNEW CullNodeStruct;
Build_Tree(Root,PolyCount,polyindices);
polyindices = NULL;
/*
** fill in the remaining information needed in the tree:
** for example: bounding boxes, index assignments
*/
Compute_Bounding_Box(Root);
Assign_Index(Root,0);
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
* 6/28/02 KJM : This version handles 32-bit indexes
*=============================================================================================*/
void AABTreeBuilderClass::Build_AABTree(int polycount,Vector3i * polys,int vertcount,Vector3 * verts)
{
WWASSERT(polycount > 0);
WWASSERT(vertcount > 0);
WWASSERT(polys != NULL);
WWASSERT(verts != NULL);
/*
** If we already have allocated data, release it
*/
Reset();
/*
** Copy the mesh data
*/
VertCount = vertcount;
PolyCount = polycount;
Verts = new Vector3[VertCount];
Polys = new TriIndex[PolyCount];
for (int vi=0; vi<VertCount; vi++) {
Verts[vi] = verts[vi];
}
for (int pi=0; pi<PolyCount; pi++) {
Polys[pi].I = polys[pi].I;
Polys[pi].J = polys[pi].J;
Polys[pi].K = polys[pi].K;
}
/*
** First, create a list of all of the poly indices
*/
int * polyindices = new int[PolyCount];
for (int i=0; i<PolyCount; i++) {
polyindices[i] = i;
}
/*
** Build the tree, note that the array of poly indices will be
** deleted by the Build_Tree function.
*/
Root = new CullNodeStruct;
Build_Tree(Root,PolyCount,polyindices);
polyindices = NULL;
/*
** fill in the remaining information needed in the tree:
** for example: bounding boxes, index assignments
*/
Compute_Bounding_Box(Root);
Assign_Index(Root,0);
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_Tree -- recursivly builds the culling tree *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_Tree(CullNodeStruct * node,int polycount,int * polyindices)
{
/*
** First, if there are only a few polys left, just terminate the tree
*/
if (polycount <= MIN_POLYS_PER_NODE) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
/*
** Try to find a suitable partitioning plane.
*/
SplitChoiceStruct sc;
sc = Select_Splitting_Plane(polycount,polyindices);
/*
** If the algorithm could not separate any polys, just install the polys
** in this node and terminate. TODO: explore how this happens.
*/
if (sc.FrontCount + sc.BackCount != polycount) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
/*
** Decide whether to actually partition this node. If the partitioning
** will not gain us anything, just install the polys in this node and terminate
** the tree.
*/
#if 0
if (sc.Cost == MAX_COST) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
#endif
/*
** Ok, split the polys
*/
SplitArraysStruct arrays;
Split_Polys(polycount,polyindices,sc,&arrays);
/*
** Free the memory in use by the input tile-list
*/
delete[] polyindices;
/*
** Build a front tree if necessary. Remember that the Build function
** deletes the poly array.
*/
if (arrays.FrontCount) {
WWASSERT(arrays.FrontPolys != NULL);
node->Front = W3DNEW CullNodeStruct;
Build_Tree(node->Front,arrays.FrontCount,arrays.FrontPolys);
arrays.FrontPolys = NULL;
}
/*
** Build a back tree if necessary. Remember that the build function
** deletes the tile array.
*/
if (arrays.BackCount) {
WWASSERT(arrays.BackPolys != NULL);
node->Back = W3DNEW CullNodeStruct;
Build_Tree(node->Back,arrays.BackCount,arrays.BackPolys);
arrays.BackPolys = NULL;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Select_Splitting_Plane -- select a partition for the given polys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::SplitChoiceStruct
AABTreeBuilderClass::Select_Splitting_Plane(int polycount,int * polyindices)
{
WWASSERT(polyindices != NULL);
const int NUM_TRYS = 50;
SplitChoiceStruct best_plane_stats;
SplitChoiceStruct considered_plane_stats;
/*
** Try putting axis-aligned planes through some random vertices
*/
for (int trys = 0; trys < MIN(NUM_TRYS,polycount); trys++) {
AAPlaneClass plane;
/*
** Select a random poly and vertex index;
*/
int poly_index = polyindices[rand() % polycount];
int vert_index = rand() % 3;
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * vert = Verts + (*polyverts)[vert_index];
/*
** Select a random plane
*/
switch(rand() % 3) {
case 0: plane.Set(AAPlaneClass::XNORMAL,vert->X); break;
case 1: plane.Set(AAPlaneClass::YNORMAL,vert->Y); break;
case 2: plane.Set(AAPlaneClass::ZNORMAL,vert->Z); break;
};
/*
** Get the score for this plane
*/
considered_plane_stats = Compute_Plane_Score(polycount,polyindices,plane);
if (considered_plane_stats.Cost < best_plane_stats.Cost) {
best_plane_stats = considered_plane_stats;
}
}
return best_plane_stats;
}
/***********************************************************************************************
* AABTreeBuilderClass::Compute_Plane_Score -- evaluate the suitability of a partition plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::SplitChoiceStruct
AABTreeBuilderClass::Compute_Plane_Score(int polycount,int * polyindices,const AAPlaneClass & plane)
{
/*
** The score of a splitting plane is based on the following factors:
** - the volumes of the resulting two children volumes,
** - the number of polys in each child volume
*/
SplitChoiceStruct sc;
sc.Plane = plane;
for (int i=0; i<polycount; i++) {
switch(Which_Side(plane,polyindices[i])) {
case FRONT:
case ON:
case BOTH:
{
sc.FrontCount++;
Update_Min_Max(polyindices[i],sc.FMin,sc.FMax );
break;
}
case BACK:
{
sc.BackCount++;
Update_Min_Max(polyindices[i],sc.BMin,sc.BMax );
break;
}
}
}
/*
** Inflate the box a tiny amount so that we never
** get volumes of zero!
*/
sc.BMin -= Vector3(WWMATH_EPSILON,WWMATH_EPSILON,WWMATH_EPSILON);
sc.BMax += Vector3(WWMATH_EPSILON,WWMATH_EPSILON,WWMATH_EPSILON);
/*
** Compute the cost.
*/
float back_cost = (sc.BMax.X - sc.BMin.X) * (sc.BMax.Y - sc.BMin.Y) * (sc.BMax.Z - sc.BMin.Z) * sc.BackCount;
float front_cost = (sc.FMax.X - sc.FMin.X) * (sc.FMax.Y - sc.FMin.Y) * (sc.FMax.Z - sc.FMin.Z) * sc.FrontCount;
sc.Cost = front_cost + back_cost;
if ((sc.FrontCount == 0) || (sc.BackCount == 0)) {
sc.Cost = FLT_MAX;
}
return sc;
}
/***********************************************************************************************
* AABTreeBuilderClass::Which_Side -- which side of a plane is the given poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::OverlapType
AABTreeBuilderClass::Which_Side(const AAPlaneClass & plane,int poly_index)
{
/*
** Check each vertex to see if it is in front, behind or on the plane
*/
int mask = 0;
for (int vi=0; vi<3; vi++) {
const Vector3 & point = Verts[ Polys[poly_index][vi] ];
float delta = point[plane.Normal] - plane.Dist;
if (delta > COINCIDENCE_EPSILON) {
mask |= POS;
}
if (delta < -COINCIDENCE_EPSILON) {
mask |= NEG;
}
mask |= ON;
}
/*
** Now evaluate the status of all of the verts to determine whether the
** triangle is in front, behind, on or overlapping the plane
*/
/*
** If all verts were ON the plane, the triangle is ON the plane
*/
if (mask == ON) {
return ON;
}
/*
** If all verts were POS or ON, the triangle is POS (IN_FRONT)
*/
if ((mask & ~(POS | ON)) == 0) {
return POS;
}
/*
** If all verts were NEG or ON, the triangle is NEG (BEHIND)
*/
if ((mask & ~(NEG | ON)) == 0) {
return NEG;
}
/*
** Otherwise, the triangle spans the plane
*/
return BOTH;
}
/***********************************************************************************************
* AABTreeBuilderClass::Split_Polys -- partition the polys with a plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Split_Polys
(
int polycount,
int * polyindices,
const SplitChoiceStruct & sc,
SplitArraysStruct * arrays
)
{
/*
** Note that this routine arrays of polygons. The caller is then responsible for keeping
** track of the memory this routine allocates.
*/
if (sc.FrontCount > 0) {
arrays->FrontPolys = W3DNEWARRAY int[sc.FrontCount];
}
if (sc.BackCount > 0) {
arrays->BackPolys = W3DNEWARRAY int[sc.BackCount];
}
arrays->FrontCount = 0;
arrays->BackCount = 0;
for (int i=0; i<polycount; i++) {
switch(Which_Side(sc.Plane,polyindices[i])) {
case FRONT:
case ON:
case BOTH:
arrays->FrontPolys[arrays->FrontCount++] = polyindices[i];
break;
case BACK:
arrays->BackPolys[arrays->BackCount++] = polyindices[i];
break;
}
}
/*
** when we are all done, the counts should match.
*/
WWASSERT(arrays->FrontCount == sc.FrontCount);
WWASSERT(arrays->BackCount == sc.BackCount);
}
/***********************************************************************************************
* AABTreeBuilderClass::Compute_Bounding_Box -- compute bounding boxes for the cull nodes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Compute_Bounding_Box(CullNodeStruct * node)
{
/*
** compute bounding volumes of the children
*/
if (node->Front) {
Compute_Bounding_Box(node->Front);
}
if (node->Back) {
Compute_Bounding_Box(node->Back);
}
/*
** compute bounding volume for the polys in this node
*/
const float really_big=WWMATH_FLOAT_MAX;
node->Min.Set(really_big,really_big,really_big);
node->Max.Set(-really_big,-really_big,-really_big);
for (int poly_index = 0; poly_index < node->PolyCount; poly_index++) {
Update_Min_Max(node->PolyIndices[poly_index],node->Min,node->Max );
}
/*
** bound the polys in the front child node
*/
if (node->Front) {
if (node->Front->Min.X < node->Min.X) node->Min.X = node->Front->Min.X;
if (node->Front->Max.X > node->Max.X) node->Max.X = node->Front->Max.X;
if (node->Front->Min.Y < node->Min.Y) node->Min.Y = node->Front->Min.Y;
if (node->Front->Max.Y > node->Max.Y) node->Max.Y = node->Front->Max.Y;
if (node->Front->Min.Z < node->Min.Z) node->Min.Z = node->Front->Min.Z;
if (node->Front->Max.Z > node->Max.Z) node->Max.Z = node->Front->Max.Z;
}
/*
** bound the polys in the back child node
*/
if (node->Back) {
if (node->Back->Min.X < node->Min.X) node->Min.X = node->Back->Min.X;
if (node->Back->Max.X > node->Max.X) node->Max.X = node->Back->Max.X;
if (node->Back->Min.Y < node->Min.Y) node->Min.Y = node->Back->Min.Y;
if (node->Back->Max.Y > node->Max.Y) node->Max.Y = node->Back->Max.Y;
if (node->Back->Min.Z < node->Min.Z) node->Min.Z = node->Back->Min.Z;
if (node->Back->Max.Z > node->Max.Z) node->Max.Z = node->Back->Max.Z;
}
WWASSERT(node->Min.X != really_big);
WWASSERT(node->Min.Y != really_big);
WWASSERT(node->Min.Z != really_big);
WWASSERT(node->Max.X != -really_big);
WWASSERT(node->Max.Y != -really_big);
WWASSERT(node->Max.Z != -really_big);
}
/***********************************************************************************************
* AABTreeBuilderClass::Assign_Index -- assign an array index to each node *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Assign_Index(CullNodeStruct * node,int index)
{
/*
** This function is used to assign a sequential index to
** each node in the tree. The AABTree stores its nodes in
** an array so this index is used to determine which slot
** in the array to put each node into.
*/
WWASSERT(node);
node->Index = index;
index++;
if (node->Front) {
index = Assign_Index(node->Front,index);
}
if (node->Back) {
index = Assign_Index(node->Back,index);
}
return index;
}
/***********************************************************************************************
* AABTreeBuilderClass::Node_Count -- Count the nodes in the tree *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Node_Count(void)
{
if (Root) {
return Node_Count_Recursive(Root,0);
} else {
return 0;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Poly_Count -- returns number of polys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/23/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Poly_Count(void)
{
return PolyCount;
}
/***********************************************************************************************
* AABTreeBuilderClass::Node_Count_Recursive -- internal implementation of Node_Count *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Node_Count_Recursive(CullNodeStruct * node,int curcount)
{
curcount++;
if (node->Front) {
curcount = Node_Count_Recursive(node->Front,curcount);
}
if (node->Back) {
curcount = Node_Count_Recursive(node->Back,curcount);
}
return curcount;
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Min -- ensure given vector is < min of the poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/22/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Min(int poly_index,Vector3 & min)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X < min.X) min.X = point->X;
if (point->Y < min.Y) min.Y = point->Y;
if (point->Z < min.Z) min.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Max -- ensure given vector is > max of poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/22/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Max(int poly_index,Vector3 & max)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X > max.X) max.X = point->X;
if (point->Y > max.Y) max.Y = point->Y;
if (point->Z > max.Z) max.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Min_Max -- ensure given vector is in min max of poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/24/98 BMG : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Min_Max(int poly_index, Vector3 & min, Vector3 & max)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X < min.X) min.X = point->X;
if (point->Y < min.Y) min.Y = point->Y;
if (point->Z < min.Z) min.Z = point->Z;
if (point->X > max.X) max.X = point->X;
if (point->Y > max.Y) max.Y = point->Y;
if (point->Z > max.Z) max.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Export -- Saves this AABTree into a W3D chunk *
* *
* This function will export the AABTree into a W3D chunk so that it can be loaded by its *
* sister class "AABTreeClass" in the WW3D library. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/22/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Export(ChunkSaveClass & csave)
{
csave.Begin_Chunk(W3D_CHUNK_AABTREE);
/*
** Pack the tree into an array of W3dMeshAABTreeNode's and polygon indices
*/
W3dMeshAABTreeNode * nodes = W3DNEWARRAY W3dMeshAABTreeNode[Node_Count()];
uint32 * poly_indices = W3DNEWARRAY uint32[Poly_Count()];
int cur_node = 0;
int cur_poly = 0;
Build_W3D_AABTree_Recursive(Root,nodes,poly_indices,cur_node,cur_poly);
/*
** Write out the header
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_HEADER);
W3dMeshAABTreeHeader header;
memset(&header,0,sizeof(header));
header.NodeCount = Node_Count();
header.PolyCount = Poly_Count();
csave.Write(&header,sizeof(header));
csave.End_Chunk();
/*
** Write out the array of polygon indices
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_POLYINDICES);
csave.Write(poly_indices,Poly_Count() * sizeof(uint32));
csave.End_Chunk();
/*
** Write out the array of nodes
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_NODES);
for (int ni=0; ni<Node_Count(); ni++) {
csave.Write(&(nodes[ni]),sizeof(W3dMeshAABTreeNode));
}
csave.End_Chunk();
csave.End_Chunk(); // W3D_CHUNK_AABTREE done
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_W3D_AABTree_Recursive -- Build array of indices and W3dMeshAABTr *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/22/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_W3D_AABTree_Recursive
(
AABTreeBuilderClass::CullNodeStruct * node,
W3dMeshAABTreeNode * w3d_nodes,
uint32 * poly_indices,
int & cur_node,
int & cur_poly
)
{
/*
** Copy data from the builder's node into our node
*/
W3dMeshAABTreeNode * newnode = &(w3d_nodes[node->Index]);
newnode->Min.X = node->Min.X;
newnode->Min.Y = node->Min.Y;
newnode->Min.Z = node->Min.Z;
newnode->Max.X = node->Max.X;
newnode->Max.Y = node->Max.Y;
newnode->Max.Z = node->Max.Z;
/*
** If this is a non-leaf node, set up the child indices, otherwise set up the polygon indices
*/
if (node->Front != NULL) {
WWASSERT(node->Back != NULL); // if we have one child, we better have both!
newnode->FrontOrPoly0 = node->Front->Index;
newnode->BackOrPolyCount = node->Back->Index;
} else {
newnode->FrontOrPoly0 = cur_poly | 0x80000000;
newnode->BackOrPolyCount = node->PolyCount;
}
/*
** Copy the polygon indices for this node into our array
*/
for (int pcounter = 0; pcounter < node->PolyCount; pcounter++) {
poly_indices[cur_poly++] = node->PolyIndices[pcounter];
}
/*
** Install the children
*/
if (node->Front) {
Build_W3D_AABTree_Recursive(node->Front,w3d_nodes,poly_indices,cur_node,cur_poly);
}
if (node->Back) {
Build_W3D_AABTree_Recursive(node->Back,w3d_nodes,poly_indices,cur_node,cur_poly);
}
}

View File

@@ -0,0 +1,211 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtreebuilder.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/24/01 5:49p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABTREEBUILDER_H
#define AABTREEBUILDER_H
#include "always.h"
#include "vector3.h"
#include "vector3i.h"
#include "aaplane.h"
#include "bittype.h"
#include "meshgeometry.h"
#include <float.h>
class AABTreeClass;
class ChunkSaveClass;
struct W3dMeshAABTreeNode;
/*
** AABTreeBuilderClass
** This class serves simply to build AABTreeClasses. It first builds a tree
** which uses an easier to manage data structure (but uses more memory). Then
** the tree is converted into the representation used in the AABTreeClass.
*/
class AABTreeBuilderClass
{
public:
AABTreeBuilderClass(void);
~AABTreeBuilderClass(void);
void Build_AABTree(int polycount,TriIndex * polys,int vertcount,Vector3 * verts);
void Build_AABTree(int polycount,Vector3i* polys,int vertcount,Vector3 * verts);
void Export(ChunkSaveClass & csave);
int Node_Count(void);
int Poly_Count(void);
enum
{
MIN_POLYS_PER_NODE = 4,
SMALL_VERTEX = -100000,
BIG_VERTEX = 100000
};
private:
/*
** This CullNodeStruct is used in building the AABTree. It is much more
** wasteful in terms of memory footprint and number of allocations than the
** streamlined version found in the actual AABTreeClass.
*/
struct CullNodeStruct
{
CullNodeStruct(void) : Index(0),Min(0,0,0),Max(0,0,0),Front(NULL),Back(NULL),PolyCount(0),PolyIndices(NULL) {}
~CullNodeStruct(void)
{
if (Front) { delete Front; }
if (Back) { delete Back; }
if (PolyIndices) { delete[] PolyIndices; }
}
int Index;
Vector3 Min;
Vector3 Max;
CullNodeStruct * Front;
CullNodeStruct * Back;
int PolyCount;
int * PolyIndices;
};
/*
** SplitChoiceStruct - encapsulates the results of evaluating the suitability of a partition
*/
struct SplitChoiceStruct
{
SplitChoiceStruct(void) :
Cost(FLT_MAX),
FrontCount(0),
BackCount(0),
BMin(BIG_VERTEX,BIG_VERTEX,BIG_VERTEX),
BMax(SMALL_VERTEX,SMALL_VERTEX,SMALL_VERTEX),
FMin(BIG_VERTEX,BIG_VERTEX,BIG_VERTEX),
FMax(SMALL_VERTEX,SMALL_VERTEX,SMALL_VERTEX),
Plane(AAPlaneClass::XNORMAL,0)
{
}
float Cost; // try to minimize this!
int FrontCount; // number of polys in front of the plane
int BackCount; // number of polys behind the plane
Vector3 BMin; // min of the bounding box of the "back" child
Vector3 BMax; // max of the bounding box of the "back" child
Vector3 FMin; // min of the bounding box of the "front" child
Vector3 FMax; // max of the bounding box of the "front" child
AAPlaneClass Plane; // partitioning plane
};
struct SplitArraysStruct
{
SplitArraysStruct(void) :
FrontCount(0),
BackCount(0),
FrontPolys(NULL),
BackPolys(NULL)
{
}
int FrontCount;
int BackCount;
int * FrontPolys;
int * BackPolys;
};
enum OverlapType
{
POS = 0x01,
NEG = 0x02,
ON = 0x04,
BOTH = 0x08,
OUTSIDE = POS,
INSIDE = NEG,
OVERLAPPED = BOTH,
FRONT = POS,
BACK = NEG,
};
/*
** Internal functions
*/
void Reset();
void Build_Tree(CullNodeStruct * node,int polycount,int * polyindices);
SplitChoiceStruct Select_Splitting_Plane(int polycount,int * polyindices);
SplitChoiceStruct Compute_Plane_Score(int polycont,int * polyindices,const AAPlaneClass & plane);
void Split_Polys(int polycount,int * polyindices,const SplitChoiceStruct & sc,SplitArraysStruct * arrays);
OverlapType Which_Side(const AAPlaneClass & plane,int poly_index);
void Compute_Bounding_Box(CullNodeStruct * node);
int Assign_Index(CullNodeStruct * node,int index);
int Node_Count_Recursive(CullNodeStruct * node,int curcount);
void Update_Min(int poly_index,Vector3 & set_min);
void Update_Max(int poly_index,Vector3 & set_max);
void Update_Min_Max(int poly_index, Vector3 & set_min, Vector3 & set_max);
void Build_W3D_AABTree_Recursive(CullNodeStruct * node,
W3dMeshAABTreeNode * w3dnodes,
uint32 * poly_indices,
int & cur_node,
int & cur_poly);
/*
** Tree
*/
CullNodeStruct * Root;
int CurPolyIndex;
/*
** Mesh data
*/
int PolyCount;
TriIndex * Polys;
int VertCount;
Vector3 * Verts;
friend class AABTreeClass;
};
#endif //AABTREEBUILDER_H

View File

@@ -0,0 +1,894 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/agg_def.cpp $*
* *
* Author:: Patrick Smith
* *
* $Modtime:: 4/05/01 10:21a $*
* *
* $Revision:: 5 $*
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "agg_def.h"
#include "htree.h"
#include "w3derr.h"
#include "chunkio.h"
#include "wwdebug.h"
#include "assetmgr.h"
#include "matinfo.h"
#include "texture.h"
#include "wwstring.h"
#include <windows.h>
///////////////////////////////////////////////////////////////////////////////////
//
// Local constants
//
const char * const EMPTY_STRING = "";
///////////////////////////////////////////////////////////////////////////////////
//
// Global variable initialization
//
AggregateLoaderClass _AggregateLoader;
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (void)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (const AggregateDefClass &src)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
// Invoke the assignment operator
(*this) = src;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (RenderObjClass &base_model)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
Initialize (base_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// ~AggregateDefClass
//
AggregateDefClass::~AggregateDefClass (void)
{
// Free the name buffer if necessary
if (m_pName != NULL) {
// free() is used because the buffer was allocated with ::_strdup().
::free (m_pName);
m_pName = NULL;
}
Free_Subobject_List ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// operator=
//
const AggregateDefClass &
AggregateDefClass::operator= (const AggregateDefClass &src)
{
int index;
// Free the name buffer if necessary
if (m_pName != NULL) {
::free (m_pName);
m_pName = NULL;
}
// Start with a fresh set of data
Free_Subobject_List ();
// Copy the src object's name and info struct
Set_Name (src.Get_Name ());
::memcpy (&m_Info, &src.m_Info, sizeof (m_Info));
::memcpy (&m_MiscInfo, &src.m_MiscInfo, sizeof (m_MiscInfo));
m_Version = src.m_Version;
// Loop through all the entries in the src object's subobj list
for (index = 0; index < src.m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = src.m_SubobjectList[index];
if (pinfo != NULL) {
// Copy the src object's info for this subobj
W3dAggregateSubobjectStruct *new_info = W3DNEW W3dAggregateSubobjectStruct;
::memcpy (new_info, pinfo, sizeof (W3dAggregateSubobjectStruct));
// Add this subobj to our list
m_SubobjectList.Add (new_info);
}
}
// Return a reference to ourselves
return *this;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Free_Subobject_List
//
void
AggregateDefClass::Free_Subobject_List (void)
{
// Delete all the stucture pointers contained in the subobject list
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = m_SubobjectList[index];
if (pinfo) {
delete pinfo;
}
}
// Reset the lists contents
m_SubobjectList.Delete_All ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create
//
RenderObjClass *
AggregateDefClass::Create (void)
{
// Attempt to create an instance of the hierarchy
RenderObjClass *pmodel = Create_Render_Object (m_Info.BaseModelName);
if (pmodel != NULL) {
// Perform the aggregation
Attach_Subobjects (*pmodel);
// Let the new object know what its new name and base name are.
pmodel->Set_Name (m_pName);
pmodel->Set_Base_Model_Name (m_Info.BaseModelName);
pmodel->Set_Sub_Objects_Match_LOD ((m_MiscInfo.Flags & W3D_AGGREGATE_FORCE_SUB_OBJ_LOD) == W3D_AGGREGATE_FORCE_SUB_OBJ_LOD);
} else {
WWDEBUG_SAY (("Unable to load aggregate %s.\r\n", m_Info.BaseModelName));
}
// Return a pointer to the new aggregate
return pmodel;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Find_Subobject
//
RenderObjClass *
AggregateDefClass::Find_Subobject
(
RenderObjClass &model,
const char mesh_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN],
const char bone_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN]
)
{
RenderObjClass *parent_model = &model;
parent_model->Add_Ref ();
// Loop through all the models in our "path" until we've either failed
// or found the exact mesh we were looking for...
for (int index = 1;
(mesh_path[index][0] != 0) && (parent_model != NULL);
index ++) {
// Look one level deeper into the subobject chain...
RenderObjClass *sub_obj = NULL;
if (bone_path[index][0] == 0) {
sub_obj = parent_model->Get_Sub_Object_By_Name (mesh_path[index]);
} else {
int bone_index = parent_model->Get_Bone_Index (bone_path[index]);
int subobj_count = parent_model->Get_Num_Sub_Objects_On_Bone (bone_index);
// Loop through all the subobjects on this bone
for (int subobj_index = 0; (subobj_index < subobj_count) && (sub_obj == NULL); subobj_index ++) {
// Is this the subobject we were looking for?
RenderObjClass *ptemp_obj = parent_model->Get_Sub_Object_On_Bone (subobj_index, bone_index);
if (::lstrcmpi (ptemp_obj->Get_Name (), mesh_path[index]) == 0) {
sub_obj = ptemp_obj;
} else {
REF_PTR_RELEASE (ptemp_obj);
}
}
}
REF_PTR_RELEASE (parent_model);
// The parent for the next iteration is the subobject on this one.
parent_model = sub_obj;
}
// Return a pointer to the subobject
return parent_model;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Attach_Subobjects
//
void
AggregateDefClass::Attach_Subobjects (RenderObjClass &base_model)
{
// Now loop through all the subobjects and attach them to the appropriate bone
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *psubobj_info = m_SubobjectList[index];
if (psubobj_info != NULL) {
// Now create this subobject and attach it to its bone.
RenderObjClass *prender_obj = Create_Render_Object (psubobj_info->SubobjectName);
if (prender_obj != NULL) {
// Attach this object to the requested bone
if (base_model.Add_Sub_Object_To_Bone (prender_obj, psubobj_info->BoneName) == false) {
WWDEBUG_SAY (("Unable to attach %s to %s.\r\n", psubobj_info->SubobjectName, psubobj_info->BoneName));
}
// Release our hold on this pointer
prender_obj->Release_Ref ();
} else {
WWDEBUG_SAY (("Unable to load aggregate subobject %s.\r\n", psubobj_info->SubobjectName));
}
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create_Render_Object
//
RenderObjClass *
AggregateDefClass::Create_Render_Object (const char *passet_name)
{
// Assume failure
RenderObjClass *prender_obj = NULL;
// Attempt to get an instance of the render object from the asset manager
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
// If we couldn't find the render object in the asset manager, then attempt to
// load it from file
if ((prender_obj == NULL) &&
Load_Assets (passet_name)) {
// It should be in the asset manager now, so attempt to get it again.
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
}
// Return a pointer to the render object
return prender_obj;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load_Assets
//
bool
AggregateDefClass::Load_Assets (const char *passet_name)
{
// Assume failure
bool retval = false;
// Param OK?
if (passet_name != NULL) {
// Determine what the current working directory is
char path[MAX_PATH];
::GetCurrentDirectory (sizeof (path), path);
// Ensure the path is directory delimited
if (path[::lstrlen(path)-1] != '\\') {
::lstrcat (path, "\\");
}
// Assume the filename is simply the "asset name" + the w3d extension
::lstrcat (path, passet_name);
::lstrcat (path, ".w3d");
// If the file exists, then load it into the asset manager.
if (::GetFileAttributes (path) != 0xFFFFFFFF) {
retval = WW3DAssetManager::Get_Instance()->Load_3D_Assets (path);
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Initialize
//
void
AggregateDefClass::Initialize (RenderObjClass &base_model)
{
// Start with fresh lists
Free_Subobject_List ();
// Determine what the render objects original name was.
const char *orig_model_name = base_model.Get_Base_Model_Name ();
orig_model_name = (orig_model_name == NULL) ? base_model.Get_Name () : orig_model_name;
// Record information about this base model
::lstrcpy (m_Info.BaseModelName, orig_model_name);
m_Info.SubobjectCount = 0;
m_MiscInfo.OriginalClassID = base_model.Class_ID ();
m_MiscInfo.Flags = 0;
m_MiscInfo.Flags |= base_model.Is_Sub_Objects_Match_LOD_Enabled () ? W3D_AGGREGATE_FORCE_SUB_OBJ_LOD : 0;
// Pass the aggregate name along
Set_Name (base_model.Get_Name ());
// Create a new instance of the model which we can use
// to compare with the supplied model and determine
// which 'bones-models' and textures are new.
RenderObjClass *pvanilla_model = (RenderObjClass *)Create_Render_Object (orig_model_name);
// Build lists of changes from the delta between the original model and the provided one
Build_Subobject_List (*pvanilla_model, base_model);
// Release the model if necessary
REF_PTR_RELEASE (pvanilla_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Build_Subobject_List
//
void
AggregateDefClass::Build_Subobject_List
(
RenderObjClass &original_model,
RenderObjClass &model
)
{
int index;
// Loop through all the bones in this render obj
int bone_count = model.Get_Num_Bones ();
for (int bone_index = 0; bone_index < bone_count; bone_index ++) {
const char *pbone_name = model.Get_Bone_Name (bone_index);
// Build a list of nodes that are contained in the vanilla model
DynamicVectorClass <RenderObjClass *> orig_node_list;
for (index = 0;
index < original_model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = original_model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
orig_node_list.Add (psubobj);
}
}
// Build a list of nodes that are contained in this bone
DynamicVectorClass <RenderObjClass *> node_list;
for (index = 0;
index < model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
node_list.Add (psubobj);
}
}
int node_count = node_list.Count ();
if (node_count > 0) {
// Loop through the subobjects and add each one to our internal list
W3dAggregateSubobjectStruct subobj_info = { 0 };
for (int node_index = 0; node_index < node_count; node_index ++) {
RenderObjClass *psubobject = node_list[node_index];
WWASSERT (psubobject != NULL);
// Is this subobject new? (i.e. not in a 'vanilla' instance?)
const char *prototype_name = psubobject->Get_Name ();
if (psubobject != NULL &&
(Is_Object_In_List (prototype_name, orig_node_list) == false)) {
// Add this subobject to our list
::lstrcpy (subobj_info.SubobjectName, prototype_name);
::lstrcpy (subobj_info.BoneName, pbone_name);
Add_Subobject (subobj_info);
m_Info.SubobjectCount ++;
// Attach this render object to the 'original' model (this is done
// so we can do texture compares later)
RenderObjClass *prender_obj = WW3DAssetManager::Get_Instance ()->Create_Render_Obj (prototype_name);
((RenderObjClass &)original_model).Add_Sub_Object_To_Bone (prender_obj, pbone_name);
REF_PTR_RELEASE (prender_obj);
}
}
}
// Free our hold on the render objs in the original node list
for (index = 0; index < orig_node_list.Count (); index ++) {
REF_PTR_RELEASE (orig_node_list[index]);
}
orig_node_list.Delete_All ();
// Free our hold on the render objs in the node list
for (index = 0; index < node_list.Count (); index ++) {
REF_PTR_RELEASE (node_list[index]);
}
node_list.Delete_All ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Is_Object_In_List
//
bool
AggregateDefClass::Is_Object_In_List
(
const char *passet_name,
DynamicVectorClass <RenderObjClass *> &node_list
)
{
// Assume failure
bool retval = false;
// Loop through the nodes in the list until we've found the one
// were are looking for.
for (int node_index = 0; (node_index < node_list.Count ()) && (retval == false); node_index ++) {
RenderObjClass *prender_obj = node_list[node_index];
// Is this the render object we were looking for?
if (prender_obj != NULL &&
::lstrcmpi (prender_obj->Get_Name (), passet_name) == 0) {
retval = true;
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
WW3DErrorType
AggregateDefClass::Load_W3D (ChunkLoadClass &chunk_load)
{
W3dTextureReplacerHeaderStruct header = { 0 };
while (chunk_load.Open_Chunk()) {
WW3DErrorType error = WW3D_ERROR_OK;
switch (chunk_load.Cur_Chunk_ID()) {
case W3D_CHUNK_AGGREGATE_HEADER:
error = Read_Header(chunk_load);
break;
case W3D_CHUNK_AGGREGATE_INFO:
error = Read_Info(chunk_load);
break;
case W3D_CHUNK_TEXTURE_REPLACER_INFO:
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
if (header.ReplacedTexturesCount > 0) {
WWDEBUG_SAY(("Obsolete texture replacement chunk encountered in aggregate: %s\r\n",m_pName));
}
}
break;
case W3D_CHUNK_AGGREGATE_CLASS_INFO:
error = Read_Class_Info(chunk_load);
break;
default:
// Unknown chunk.
break;
}
chunk_load.Close_Chunk();
if (error != WW3D_ERROR_OK) return (error);
}
return WW3D_ERROR_OK;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Header
//
WW3DErrorType
AggregateDefClass::Read_Header (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Is this the header chunk?
W3dAggregateHeaderStruct header = { 0 };
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
// Copy the name from the header structure
m_pName = ::_strdup (header.Name);
m_Version = header.Version;
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Info
//
WW3DErrorType
AggregateDefClass::Read_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our member structure
::memset (&m_Info, 0, sizeof (m_Info));
if (chunk_load.Read (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Read all the subobjects from the file
for (UINT isubobject = 0;
(isubobject < m_Info.SubobjectCount) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Read this subobject's definition from the file
ret_val = Read_Subobject (chunk_load);
}
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Subobject
//
WW3DErrorType
AggregateDefClass::Read_Subobject (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the subobject information from the file
W3dAggregateSubobjectStruct subobj_info = { 0 };
if (chunk_load.Read (&subobj_info, sizeof (subobj_info)) == sizeof (subobj_info)) {
// Add this subobject to our list
Add_Subobject (subobj_info);
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Add_Subobject
//
void
AggregateDefClass::Add_Subobject (const W3dAggregateSubobjectStruct &subobj_info)
{
// Create a new structure and copy the contents of the src
W3dAggregateSubobjectStruct *pnew_entry = W3DNEW W3dAggregateSubobjectStruct;
::lstrcpy (pnew_entry->SubobjectName, subobj_info.SubobjectName);
::lstrcpy (pnew_entry->BoneName, subobj_info.BoneName);
// Add this new entry to the list
m_SubobjectList.Add (pnew_entry);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Class_Info
//
WW3DErrorType
AggregateDefClass::Read_Class_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our header structure
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
if (chunk_load.Read (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
//////////////////////////////////////////////////////////////////////////////////
//
// Save
//
WW3DErrorType
AggregateDefClass::Save_W3D (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies an aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE) == TRUE) {
// Attempt to save the different sections of the aggregate definition
if ((Save_Header (chunk_save) == WW3D_ERROR_OK) &&
(Save_Info (chunk_save) == WW3D_ERROR_OK) &&
(Save_Class_Info (chunk_save) == WW3D_ERROR_OK)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Close the aggregate chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Header
//
WW3DErrorType
AggregateDefClass::Save_Header (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_HEADER) == TRUE) {
// Fill the header structure
W3dAggregateHeaderStruct header = { 0 };
header.Version = W3D_CURRENT_AGGREGATE_VERSION;
::lstrcpyn (header.Name, m_pName, sizeof (header.Name));
header.Name[sizeof (header.Name) - 1] = 0;
// Write the header out to the chunk
if (chunk_save.Write (&header, sizeof (header)) == sizeof (header)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the header chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Info
//
WW3DErrorType
AggregateDefClass::Save_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate settings
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_INFO) == TRUE) {
// Write the settings structure out to the chunk
if (chunk_save.Write (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Write all the subobjects to the file
for (int isubobject = 0;
(isubobject < m_SubobjectList.Count ()) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Write this object to the file
ret_val = Save_Subobject (chunk_save, m_SubobjectList[isubobject]);
}
}
// End the settings chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Subobject
//
WW3DErrorType
AggregateDefClass::Save_Subobject
(
ChunkSaveClass &chunk_save,
W3dAggregateSubobjectStruct *psubobject
)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Write the subobj structure out to the chunk
if (chunk_save.Write (psubobject, sizeof (W3dAggregateSubobjectStruct)) == sizeof (W3dAggregateSubobjectStruct)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Save_Class_Info
//
WW3DErrorType
AggregateDefClass::Save_Class_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the texture replacer header
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_CLASS_INFO) == TRUE) {
// Write the class information structure out to the chunk
if (chunk_save.Write (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the class info chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
PrototypeClass *
AggregateLoaderClass::Load_W3D (ChunkLoadClass &chunk_load)
{
// Assume failure
AggregatePrototypeClass *pprototype = NULL;
// Create a definition object
AggregateDefClass *pdefinition = W3DNEW AggregateDefClass;
if (pdefinition != NULL) {
// Ask the definition object to load the aggregate data
if (pdefinition->Load_W3D (chunk_load) != WW3D_ERROR_OK) {
// Error! Free the definition
delete pdefinition;
pdefinition = NULL;
} else {
// Success! Create a prototype from the definition
pprototype = W3DNEW AggregatePrototypeClass (pdefinition);
}
}
// Return a pointer to the prototype
return pprototype;
}

View File

@@ -0,0 +1,259 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/agg_def.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 4/05/01 9:52a $*
* *
* $Revision:: 2 $*
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AGGREGATE_DEF_H
#define AGGREGATE_DEF_H
#include "proto.h"
#include "rendobj.h"
#include "w3d_file.h"
#include "w3derr.h"
#include "vector.h"
#include "bittype.h"
#include <string.h>
#ifdef _UNIX
#include "osdep.h"
#endif
// Forward declarations
class ChunkLoadClass;
class ChunkSaveClass;
class IndirectTextureClass;
///////////////////////////////////////////////////////////////////////////////////
//
// Macros
//
#ifndef SAFE_FREE
#define SAFE_FREE(pointer) \
{ \
if (pointer) { \
::free (pointer); \
pointer = 0; \
} \
}
#endif //SAFE_FREE
//////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
// Description of an aggregate object. Used by the asset manager
// to construct aggregates. An 'aggregate' is simply a 'shell' that
// contains references to a hierarchy model and subobjects to attach to its bones.
//
class AggregateDefClass
{
public:
///////////////////////////////////////////////////////////
//
// Public constructors/destructors
//
AggregateDefClass (void);
AggregateDefClass (RenderObjClass &base_model);
AggregateDefClass (const AggregateDefClass &src);
virtual ~AggregateDefClass (void);
///////////////////////////////////////////////////////////
//
// Public operators
//
const AggregateDefClass &operator= (const AggregateDefClass &src);
///////////////////////////////////////////////////////////
//
// Public methods
//
virtual WW3DErrorType Load_W3D (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Save_W3D (ChunkSaveClass &chunk_save);
const char * Get_Name (void) const { return m_pName; }
void Set_Name (const char *pname) { SAFE_FREE (m_pName); m_pName = ::_strdup (pname); }
RenderObjClass * Create (void);
AggregateDefClass * Clone (void) const { return W3DNEW AggregateDefClass (*this); }
//
// Public accessors
//
ULONG Class_ID (void) const { return m_MiscInfo.OriginalClassID; }
//
// Initialization
//
void Initialize (RenderObjClass &base_model);
protected:
///////////////////////////////////////////////////////////
//
// Protected data types
//
typedef struct _TEXTURE_INFO
{
W3dTextureReplacerStruct names;
IndirectTextureClass * pnew_texture;
bool operator == (_TEXTURE_INFO &src) { return false; }
bool operator != (_TEXTURE_INFO &src) { return true; }
} TEXTURE_INFO;
///////////////////////////////////////////////////////////
//
// Protected methods
//
//
// Loading methods
//
virtual WW3DErrorType Read_Header (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Info (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Subobject (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Class_Info (ChunkLoadClass &chunk_load);
//
// Saving methods
//
virtual WW3DErrorType Save_Header (ChunkSaveClass &chunk_save);
virtual WW3DErrorType Save_Info (ChunkSaveClass &chunk_save);
virtual WW3DErrorType Save_Subobject (ChunkSaveClass &chunk_save, W3dAggregateSubobjectStruct *psubobject);
virtual WW3DErrorType Save_Class_Info (ChunkSaveClass &chunk_save);
//
// Creation methods
//
virtual void Attach_Subobjects (RenderObjClass &base_model);
//
// Search methods
//
virtual RenderObjClass * Find_Subobject (RenderObjClass &model, const char mesh_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN], const char bone_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN]);
//
// Misc. methods
//
virtual void Free_Subobject_List (void);
virtual void Add_Subobject (const W3dAggregateSubobjectStruct &subobj_info);
virtual bool Load_Assets (const char *asset_name);
virtual RenderObjClass *Create_Render_Object (const char *passet_name);
virtual bool Is_Object_In_List (const char *passet_name, DynamicVectorClass <RenderObjClass *> &node_list);
virtual void Build_Subobject_List (RenderObjClass &original_model, RenderObjClass &model);
private:
///////////////////////////////////////////////////////////
//
// Private member data
//
DWORD m_Version;
DynamicVectorClass<W3dAggregateSubobjectStruct *> m_SubobjectList;
W3dAggregateInfoStruct m_Info;
W3dAggregateMiscInfo m_MiscInfo;
char * m_pName;
};
///////////////////////////////////////////////////////////////////////////////////
//
// AggregatePrototypeClass
//
class AggregatePrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(AggregatePrototypeClass)
public:
///////////////////////////////////////////////////////////
//
// Public constructors/destructors
//
AggregatePrototypeClass (AggregateDefClass *pdef) { m_pDefinition = pdef; }
///////////////////////////////////////////////////////////
//
// Public methods
//
virtual const char * Get_Name(void) const { return m_pDefinition->Get_Name (); }
virtual int Get_Class_ID(void) const { return m_pDefinition->Class_ID (); }
virtual RenderObjClass * Create (void) { return m_pDefinition->Create (); }
virtual void DeleteSelf() { delete this; }
virtual AggregateDefClass * Get_Definition (void) const { return m_pDefinition; }
virtual void Set_Definition (AggregateDefClass *pdef) { m_pDefinition = pdef; }
protected:
virtual ~AggregatePrototypeClass (void) { delete m_pDefinition; }
private:
///////////////////////////////////////////////////////////
//
// Private member data
//
AggregateDefClass * m_pDefinition;
};
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateLoaderClass
//
class AggregateLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_AGGREGATE; }
virtual PrototypeClass * Load_W3D (ChunkLoadClass &chunk_load);
};
///////////////////////////////////////////////////////////////////////////////////
//
// Global variables
//
extern AggregateLoaderClass _AggregateLoader;
#endif //__AGGREGATE_DEF_H

View File

@@ -0,0 +1,565 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/animatedsoundmgr.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 12/13/01 6:05p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// MBL Update for CNC3 INCURSION - 10.23.2002 - Expanded param handling, Added STOP command
//
#include <string.h> // stricmp()
#include "animatedsoundmgr.h"
#include "ini.h"
#include "inisup.h"
#include "ffactory.h"
#include "wwfile.h"
#include <stdio.h>
#include "definition.h"
#include "definitionmgr.h"
#include "definitionclassids.h"
#include "wwaudio.h"
#include "audiblesound.h"
#include "htree.h"
#include "hanim.h"
#include "soundlibrarybridge.h"
#include "WWDebug.h"
//////////////////////////////////////////////////////////////////////
// Static member initialization
//////////////////////////////////////////////////////////////////////
HashTemplateClass<StringClass, AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimationNameHash;
DynamicVectorClass<AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimSoundLists;
SoundLibraryBridgeClass* AnimatedSoundMgrClass::SoundLibrary = NULL;
//////////////////////////////////////////////////////////////////////
// Local inlines
//////////////////////////////////////////////////////////////////////
static WWINLINE INIClass *
Get_INI (const char *filename)
{
INIClass *ini = NULL;
//
// Get the file from our filefactory
//
FileClass *file = _TheFileFactory->Get_File (filename);
if (file) {
//
// Create the INI object
//
if (file->Is_Available ()) {
ini = new INIClass (*file);
}
//
// Close the file
//
_TheFileFactory->Return_File (file);
}
return ini;
}
static int
Build_List_From_String
(
const char * buffer,
const char * delimiter,
StringClass ** string_list
)
{
int count = 0;
WWASSERT (buffer != NULL);
WWASSERT (delimiter != NULL);
WWASSERT (string_list != NULL);
if ((buffer != NULL) &&
(delimiter != NULL) &&
(string_list != NULL))
{
int delim_len = ::strlen (delimiter);
//
// Determine how many entries there will be in the list
//
for (const char *entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
// Increment the count of entries
count ++;
}
if (count > 0) {
//
// Allocate enough StringClass objects to hold all the strings in the list
//
(*string_list) = new StringClass[count];
//
// Parse the string and pull out its entries.
//
count = 0;
for (entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
//
// Copy this entry into its own string
//
StringClass entry_string = entry;
char *delim_start = ::strstr (entry_string, delimiter);
if (delim_start != NULL) {
delim_start[0] = 0;
}
//
// Add this entry to our list
//
if ((entry_string.Get_Length () > 0) || (count == 0)) {
(*string_list)[count++] = entry_string;
}
}
} else if (delim_len > 0) {
count = 1;
(*string_list) = new StringClass[count];
(*string_list)[0] = buffer;
}
}
//
// Return the number of entries in our list
//
return count;
}
static bool
Is_In_Param_List
(
StringClass *param_list,
int param_count,
const char *param_to_check
)
{
//
// Check incoming parameters
//
WWASSERT( param_list != NULL );
if ( param_list == NULL )
{
return( false );
}
WWASSERT( param_count >= 2 );
if ( param_count < 2 )
{
return( false );
}
WWASSERT( param_to_check != NULL );
if ( param_to_check == NULL )
{
return( false );
}
//
// Note: params 0 & 1 are fixed to frame and name...
//
for ( int param_index = 2; param_index < param_count; param_index ++ )
{
{
StringClass string = param_list[ param_index ];
// OutputDebugString( "MBL: Comparing " );
// OutputDebugString( string.Peek_Buffer() );
// OutputDebugString( " with " );
// OutputDebugString( param_to_check );
// OutputDebugString( "\n" );
// if ( stricmp( string.Peek_Buffer(), param_to_check ) == 0 ) // Breaks with whitespaces
if ( strstr( string.Peek_Buffer(), param_to_check ) != 0 )
{
return( true );
}
}
}
return( false );
}
//////////////////////////////////////////////////////////////////////
//
// Initialize
//
//////////////////////////////////////////////////////////////////////
void
AnimatedSoundMgrClass::Initialize (const char *ini_filename)
{
//
// Don't re-initialize...
//
if (AnimSoundLists.Count () > 0) {
return ;
}
const char *DEFAULT_INI_FILENAME = "w3danimsound.ini";
//
// Determine which filename to use
//
const char *filename_to_use = ini_filename;
if (filename_to_use == NULL) {
filename_to_use = DEFAULT_INI_FILENAME;
}
//
// Get the INI file which contains the data for this viewer
//
INIClass *ini_file = ::Get_INI (filename_to_use);
if (ini_file != NULL) {
//
// Loop over all the sections in the INI
//
List<INISection *> &section_list = ini_file->Get_Section_List ();
for ( INISection *section = section_list.First ();
section != NULL && section->Is_Valid ();
section = section->Next_Valid ())
{
//
// Get the animation name from the section name
//
StringClass animation_name = section->Section;
::strupr (animation_name.Peek_Buffer ());
// OutputDebugString( "MBL Section / animation: " );
// OutputDebugString( animation_name.Peek_Buffer() );
// OutputDebugString( "\n" );
//
// Allocate a sound list
//
ANIM_SOUND_LIST *sound_list = new ANIM_SOUND_LIST;
//
// Loop over all the entries in this section
//
int entry_count = ini_file->Entry_Count (section->Section);
for (int entry_index = 0; entry_index < entry_count; entry_index ++) {
StringClass value;
//
// Get the data associated with this entry
//
const char *entry_name = ini_file->Get_Entry (section->Section, entry_index);
// OutputDebugString( " MBL Entry name: " );
// OutputDebugString( entry_name );
// OutputDebugString( "\n" );
if (strcmp(entry_name, "BoneName") == 0) {
ini_file->Get_String (value, section->Section, entry_name);
sound_list->BoneName = value;
// OutputDebugString( " MBL (BoneName) entry line value: " );
// OutputDebugString( value.Peek_Buffer() );
// OutputDebugString( "\n" );
} else {
ini_file->Get_String (value, section->Section, entry_name);
// OutputDebugString( " MBL (not BoneName) entry line value: " );
// OutputDebugString( value.Peek_Buffer() );
// OutputDebugString( "\n" );
//
// Extract the parameters from the section
//
int len = value.Get_Length ();
StringClass definition_name (len + 1, true);
int action_frame = 0;
//
// Separate the parameters into an easy-to-handle data structure
//
StringClass *param_list = NULL;
int param_count = ::Build_List_From_String (value, ",", &param_list);
// if ((param_count >= 2) && (param_count <= 3))
{
action_frame = ::atoi (param_list[0]);
definition_name = param_list[1];
definition_name.Trim ();
//
// Tie the relevant information together and store it
// in the list of sounds for this animation
//
ANIM_SOUND_INFO* sound_info = new ANIM_SOUND_INFO;
sound_info->Frame = action_frame;
sound_info->SoundName = definition_name;
//
// "2D" check
//
// if ((param_count == 3) && (atoi(param_list[2]) == 2)) {
// sound_info->Is2D = true;
// }
//
sound_info->Is2D = false;
if ( Is_In_Param_List( param_list, param_count, "2D" ) )
{
sound_info->Is2D = true;
}
//
// "STOP" check
//
sound_info->IsStop = false;
if ( Is_In_Param_List( param_list, param_count, "STOP" ) )
{
sound_info->IsStop = true;
}
sound_list->Add_Sound_Info (sound_info);
delete [] param_list;
}
}
}
if (sound_list->List.Count () != 0) {
//
// Add this sound list to our hash-table and vector-array
//
AnimationNameHash.Insert (animation_name, sound_list);
AnimSoundLists.Add (sound_list);
} else {
//WWDEBUG_SAY (("AnimatedSoundMgrClass::Initialize -- No sounds added for %d!\n", animation_name.Peek_Buffer ()));
delete sound_list;
}
}
delete ini_file;
}
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//////////////////////////////////////////////////////////////////////
void
AnimatedSoundMgrClass::Shutdown (void)
{
//
// Reset the animation name hash
//
AnimationNameHash.Remove_All ();
//
// Free each of the sound objects
//
for (int index = 0; index < AnimSoundLists.Count (); index ++) {
/*
ANIM_SOUND_LIST* list = AnimSoundLists[index];
for (int i = 0; i < list->Count(); i++) {
delete (*list)[i];
}
*/
delete AnimSoundLists[index];
}
AnimSoundLists.Delete_All ();
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Does_Animation_Have_Embedded_Sounds
//
//////////////////////////////////////////////////////////////////////
const char*
AnimatedSoundMgrClass::Get_Embedded_Sound_Name (HAnimClass *anim)
{
if (anim == NULL) {
return NULL;
}
ANIM_SOUND_LIST* list = Find_Sound_List (anim);
if (list == NULL) {
return NULL;
}
return list->BoneName.Peek_Buffer();
}
//////////////////////////////////////////////////////////////////////
//
// Find_Sound_List
//
//////////////////////////////////////////////////////////////////////
AnimatedSoundMgrClass::ANIM_SOUND_LIST *
AnimatedSoundMgrClass::Find_Sound_List (HAnimClass *anim)
{
//
// Build the full name of the animation
//
StringClass full_name (0, true);
full_name = anim->Get_Name ();
//
// Make the name uppercase
//
::strupr (full_name.Peek_Buffer ());
//
// Lookup the sound list for this animation
//
ANIM_SOUND_LIST *retval = AnimationNameHash.Get (full_name);
return retval;
}
//////////////////////////////////////////////////////////////////////
//
// Trigger_Sound
//
//////////////////////////////////////////////////////////////////////
float
AnimatedSoundMgrClass::Trigger_Sound
(
HAnimClass * anim,
float old_frame,
float new_frame,
const Matrix3D & tm
)
{
if ((SoundLibrary == NULL) || (anim == NULL)) {
return old_frame;
}
float retval = old_frame;
#ifndef W3D_MAX4
//
// Lookup the sound list for this animation
//
ANIM_SOUND_LIST *sound_list = Find_Sound_List (anim);
if (sound_list != NULL) {
for (int index = 0; index < sound_list->List.Count (); index ++) {
int frame = sound_list->List[index]->Frame;
//
// Is the animation passing the frame we need?
//
if ((old_frame < frame) && (new_frame >= frame)) {
//
// Don't trigger the sound if its skipped too far past...
//
//if (WWMath::Fabs (new_frame - old_frame) < 3.0F) {
//
// Stop the audio?
//
if (sound_list->List[index]->IsStop == true)
{
//
// Stop the audio
//
SoundLibrary->Stop_Playing_Audio( sound_list->List[index]->SoundName.Peek_Buffer() );
}
else
{
//
// Play the audio
//
if (sound_list->List[index]->Is2D == true)
{
SoundLibrary->Play_2D_Audio(sound_list->List[index]->SoundName.Peek_Buffer());
}
else
{
SoundLibrary->Play_3D_Audio(sound_list->List[index]->SoundName.Peek_Buffer(), tm);
}
}
//WWDEBUG_SAY (("Triggering Sound %d %s\n", GetTickCount (), sound_list->List[index]->SoundName));
retval = frame;
//}
}
}
//retval = true;
}
#endif
return retval;
}
void AnimatedSoundMgrClass::Set_Sound_Library(SoundLibraryBridgeClass* library)
{
SoundLibrary = library;
}

View File

@@ -0,0 +1,136 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/animatedsoundmgr.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 12/13/01 6:05p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// MBL Update for CNC3 INCURSION - 10.23.2002 - Expanded param handling, Added STOP command
//
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __ANIMATEDSOUNDMGR_H
#define __ANIMATEDSOUNDMGR_H
#include "simplevec.h"
#include "vector.h"
#include "hashtemplate.h"
//////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////
class HTreeClass;
class HAnimClass;
class Matrix3D;
class SoundLibraryBridgeClass;
//////////////////////////////////////////////////////////////////////
//
// AnimatedSoundMgrClass
//
//////////////////////////////////////////////////////////////////////
class AnimatedSoundMgrClass
{
public:
///////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////
//
// Initialization and shutdown
//
static void Initialize (const char *ini_filename = NULL);
static void Shutdown (void);
//
// Sound playback
//
static const char* Get_Embedded_Sound_Name (HAnimClass *anim);
static float Trigger_Sound (HAnimClass *anim, float old_frame, float new_frame, const Matrix3D &tm);
// Bridges E&B code with WW3D.
static void Set_Sound_Library(SoundLibraryBridgeClass* library);
private:
///////////////////////////////////////////////////////////////////
// Private data types
///////////////////////////////////////////////////////////////////
struct AnimSoundInfo
{
AnimSoundInfo() : Frame(0), SoundName(), Is2D(false), IsStop(false) {}
int Frame;
StringClass SoundName;
bool Is2D;
bool IsStop;
};
typedef AnimSoundInfo ANIM_SOUND_INFO;
struct AnimSoundList
{
AnimSoundList() : List(), BoneName("root") {}
~AnimSoundList()
{
for (int i = 0; i < List.Count(); i++) {
delete List[i];
}
}
void Add_Sound_Info(ANIM_SOUND_INFO* info) {List.Add(info);}
SimpleDynVecClass<ANIM_SOUND_INFO*> List;
StringClass BoneName;
};
typedef AnimSoundList ANIM_SOUND_LIST;
///////////////////////////////////////////////////////////////////
// Private member data
///////////////////////////////////////////////////////////////////
static HashTemplateClass<StringClass, ANIM_SOUND_LIST *> AnimationNameHash;
static DynamicVectorClass<ANIM_SOUND_LIST *> AnimSoundLists;
static SoundLibraryBridgeClass* SoundLibrary;
///////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////
static ANIM_SOUND_LIST * Find_Sound_List (HAnimClass *anim);
};
#endif //__ANIMATEDSOUNDMGR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,328 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/animobj.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/10/01 11:18a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Animatable3DObjClass::Base_Update -- animation update function for the base pose *
* Animatable3DObjClass::Anim_Update -- Update function for a single animation *
* Animatable3DObjClass::Blend_Update -- update function for a blend of two animations *
* Animatable3DObjClass::Combo_Update -- Animation update for a combination of anims *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef ANIMOBJ_H
#define ANIMOBJ_H
#include "always.h"
#include "composite.h"
#include "htree.h"
#include "hanim.h"
class SkinClass;
class RenderInfoClass;
/**
** Animatable3DObjClass
** This class performs some of the work necessary to implement hierarchical animation.
** It implements much of the bone and animation interface of RenderObjClass.
*/
class Animatable3DObjClass : public CompositeRenderObjClass
{
public:
Animatable3DObjClass(const char * htree_name);
Animatable3DObjClass(const Animatable3DObjClass & src);
Animatable3DObjClass & operator = (const Animatable3DObjClass &);
virtual ~Animatable3DObjClass(void);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Hierarchical Animation
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Animation(void);
virtual void Set_Animation( HAnimClass * motion,
float frame, int anim_mode = ANIM_MODE_MANUAL);
virtual void Set_Animation( HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage);
virtual void Set_Animation( HAnimComboClass * anim_combo);
virtual void Set_Animation_Frame_Rate_Multiplier(float multiplier); // 020607 srj -- added
virtual HAnimClass * Peek_Animation_And_Info(float& frame, int& numFrames, int& mode, float& mult); // 020710 srj -- added
virtual HAnimClass * Peek_Animation( void );
virtual bool Is_Animation_Complete( void ) const;
virtual int Get_Num_Bones(void);
virtual const char * Get_Bone_Name(int bone_index);
virtual int Get_Bone_Index(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(int boneindex);
virtual void Capture_Bone(int boneindex);
virtual void Release_Bone(int boneindex);
virtual bool Is_Bone_Captured(int boneindex) const;
virtual void Control_Bone(int bindex,const Matrix3D & objtm,bool world_space_translation = false);
virtual const HTreeClass * Get_HTree(void) const { return HTree; }
//
// Simple bone evaluation methods for when the caller doesn't want
// to update the heirarchy, but needs to know the transform of
// a bone at a given frame.
//
virtual bool Simple_Evaluate_Bone(int boneindex, Matrix3D *tm) const;
virtual bool Simple_Evaluate_Bone(int boneindex, float frame, Matrix3D *tm) const;
// (gth) TESTING DYNAMICALLY SWAPPING SKELETONS!
virtual void Set_HTree(HTreeClass * htree);
///Generals change so we can set sub-object transforms directly without having them revert to base pose
///when marked dirty. DON'T USE THIS UNLESS YOU HAVE A GOOD REASON! -MW
void Friend_Set_Hierarchy_Valid(bool onoff) const { IsTreeValid = onoff; }
protected:
// internally used to compute the current frame if the object is in ANIM_MODE_MANUAL
float Compute_Current_Frame(float *newDirection=NULL) const;
// Update the sub-object transforms according to the current anim state and root transform.
virtual void Update_Sub_Object_Transforms(void);
// Update the transforms using the base pose only
void Base_Update(const Matrix3D & root);
// Update the transforms using a single frame of motion data
void Anim_Update( const Matrix3D & root,
HAnimClass * motion,
float frame);
// Update the transforms blending two frames of motion data
void Blend_Update( const Matrix3D & root,
HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage);
// Update the transforms with an AnimationCombination
void Combo_Update( const Matrix3D & root,
HAnimComboClass *anim);
// flag to kep track of whether the hierarchy tree transforms are currently valid
bool Is_Hierarchy_Valid(void) const { return IsTreeValid; }
void Set_Hierarchy_Valid(bool onoff) const { IsTreeValid = onoff; }
// Progress anims for single anim (loop and once)
void Single_Anim_Progress( void );
// Release any anims
void Release( void );
protected:
// Is the hierarchy tree currently valid
mutable bool IsTreeValid;
// Hierarchy Tree
HTreeClass * HTree;
// Animation state for the next frame. When we add more flexible motion
// compositing, add a new state and its associated data to the union below
enum {
NONE = 0,
BASE_POSE,
SINGLE_ANIM,
DOUBLE_ANIM,
MULTIPLE_ANIM,
};
int CurMotionMode;
union {
// CurMotionMode == SINGLE_ANIM
struct {
HAnimClass * Motion;
float Frame;
float PrevFrame;
int AnimMode;
mutable int LastSyncTime;
float animDirection;
float frameRateMultiplier; // 020607 srj -- added
} ModeAnim;
// CurMotionMode == DOUBLE_ANIM
struct {
HAnimClass * Motion0;
HAnimClass * Motion1;
float Frame0;
float Frame1;
float PrevFrame0;
float PrevFrame1;
float Percentage;
} ModeInterp;
// CurMotionMode == MULTIPLE_ANIM
struct {
HAnimComboClass * AnimCombo;
} ModeCombo;
};
friend class SkinClass;
};
/***********************************************************************************************
* Animatable3DObjClass::Base_Update -- animation update function for the base pose *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Base_Update(const Matrix3D & root)
{
/*
** This method simply puts the meshes in the base pose's configuration
*/
if (HTree) {
HTree->Base_Update(root);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Anim_Update -- Update function for a single animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Anim_Update(const Matrix3D & root,HAnimClass * motion,float frame)
{
/*
** Apply motion to the base pose
*/
if ((motion) && (HTree)) {
if (ModeAnim.Motion->Class_ID() == HAnimClass::CLASSID_HRAWANIM)
HTree->Anim_Update(Transform,(HRawAnimClass*)ModeAnim.Motion,ModeAnim.Frame);
else
HTree->Anim_Update(root,motion,frame);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Blend_Update -- update function for a blend of two animations *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Blend_Update
(
const Matrix3D & root,
HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage
)
{
/*
** Apply motion to the base pose
*/
if (HTree) {
HTree->Blend_Update(root,motion0,frame0,motion1,frame1,percentage);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Combo_Update -- Animation update for a combination of anims *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Combo_Update( const Matrix3D & root, HAnimComboClass *anim )
{
if (HTree) {
HTree->Combo_Update(root, anim);
}
Set_Hierarchy_Valid(true);
}
#endif //ANIMOBJ_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,446 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/assetmgr.h 19 12/17/01 7:55p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/assetmgr.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/15/01 4:14p $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef ASSETMGR_H
#define ASSETMGR_H
#include "always.h"
#include "vector.h"
#include "htreemgr.h"
#include "hanimmgr.h"
#include "slist.h"
#include "texture.h"
#include "hashtemplate.h"
#include "simplevec.h"
class HAnimClass;
class HTreeClass;
class ChunkLoadClass;
class FileClass;
class FileFactoryClass;
class PrototypeLoaderClass;
class Font3DDataClass;
class Font3DInstanceClass;
class FontCharsClass;
class RenderObjClass;
class HModelClass;
class PrototypeClass;
class HTreeManagerClass;
class HAnimManagerClass;
class HAnimIterator;
class TextureIterator;
class TextureFileCache;
class StreamingTextureClass;
struct StreamingTextureConfig;
class TextureClass;
class MetalMapManagerClass;
/*
** AssetIterator
** This object can iterate through the 3D assets which
** currently exist in the Asset Manager. It tells you the names
** of the assets which the manager can create for you.
*/
class AssetIterator
{
public:
virtual ~AssetIterator(void) { };
virtual void First(void) { Index = 0; }
virtual void Next(void) { Index ++; }
virtual bool Is_Done(void) = 0;
virtual const char * Current_Item_Name(void) = 0;
protected:
AssetIterator(void) { Index = 0; }
int Index;
};
/*
** RenderObjIterator
** The render obj iterator simply adds a method for determining
** the class id of a render object prototype in the system.
*/
class RenderObjIterator : public AssetIterator
{
public:
virtual int Current_Item_Class_ID(void) = 0;
};
/*
WW3DAssetManager
This object is the manager of all of the 3D data. Load your meshes, animations,
etc etc using the Load_3D_Assets function.
WARNING: hierarchy trees should be loaded before the meshes and animations
which attach to them.
-------------------------------------------------------------------------------------
Dec 11, 1997, Asset Manager Brainstorming:
- WW3DAssetManager will be diferentiated from other game data asset managers
(sounds, strings, etc) because they behave differently and serve different
purposes
- WW3D creates "clones" from the blueprints it has of render objects whereas
Our commando data asset manager will provide the data (file images) for the
blueprints. Maybe the CommandoDataManager could deal in MemoryFileClasses.
Or void * and then the ww3d manager could convert to MemoryFiles...
- Future caching: In the case that we want to implement a caching system,
assets must be "released" when not in use.
- CommandoW3d asset manager asks the game data asset manager for assets by name.
Game data manager must have a "directory" structure which maps each named
asset to data on disk. It then returns an image of the file once it has
been loaded into ram.
- Assets must be individual files, named with the asset name used in code/scripting
We will write a tool which chops w3d files up so that all of the individual assets
are brought out into their own file and named with the actual w3d name.
- Data Asset Manager will load the file into ram, give it to us and forget about it
W3d will release_ref it or delete it and the file image will go away.
- Each time the 3d asset manager is requested for an asset, it will look through
the render objects it has, if the asset isn't found, it will ask for the asset
from the generic data asset manager. When creating the actual render object,
the 3d asset manager may find that it needs another asset (such as a hierarchy tree).
It will then recurse, and ask for that asset.
- Copy Mode will go away. It will be internally set based on whether the mesh
contains vertex animation. All other render objects will simply "Clone".
- Commando will derive a Cmdo3DAssetManager which knows about the special chunks
required for the terrains.
-------------------------------------------------------------------------------------
July 28, 1998
- Exposed the prototype system and added prototype loaders (PrototypeClass and
PrototypeLoaderClass in proto.h). This now allows the user to install his own
loaders for new render object types.
- Simplified the interface by removing the special purpose creation functions,
leaving only the Create_Render_Obj function.
- In certain cases some users need to know what kind of render object was created
so we added a Class_ID mechanism to RenderObjClass.
- Class_ID for render objects is not enough. Need the asset iterator to be able
to tell you the Class_ID of each asset in the asset manager. This also means that
the prototype class needs to be able to tell you the class ID. Actually this
code only seems to be used by tools such as SView but is needed anyway...
*/
class WW3DAssetManager
{
public:
/*
** Constructor and destructor
*/
WW3DAssetManager(void);
virtual ~WW3DAssetManager(void);
/*
** Access to the single instance of a WW3DAssetManager. The user
** can subclass their own asset manager class but should only
** create one instance. (a violation of this will be caught with
** a run-time assertion)
**
** The "official" way to get at the global asset manager is to
** use a line of code like this:
** WW3DAssetManager::Get_Instance();
*/
static WW3DAssetManager * Get_Instance(void) { return TheInstance; }
static void Delete_This(void) { if (TheInstance) delete TheInstance; TheInstance=NULL; }
/*
** Load data from any type of w3d file
*/
virtual bool Load_3D_Assets( const char * filename);
virtual bool Load_3D_Assets(FileClass & assetfile);
/*
** Get rid of all of the currently loaded assets
*/
virtual void Free_Assets(void);
/*
** Release any assets that only the asset manager has a reference to.
*/
virtual void Release_Unused_Assets(void);
/*
** Release assets not in the given exclusion list.
*/
virtual void Free_Assets_With_Exclusion_List(const DynamicVectorClass<StringClass> & model_exclusion_list);
virtual void Create_Asset_List(DynamicVectorClass<StringClass> & model_exclusion_list);
/*
** create me an instance of one of the prototype render objects
*/
virtual RenderObjClass * Create_Render_Obj(const char * name);
/*
** query if there is a render object with the specified name
*/
virtual bool Render_Obj_Exists(const char * name);
/*
** Iterate through all render objects or through the
** sub-categories of render objects. NOTE! the user is responsible
** for releasing the iterator when finished with it!
*/
virtual RenderObjIterator * Create_Render_Obj_Iterator(void);
virtual void Release_Render_Obj_Iterator(RenderObjIterator *);
/*
** Access to HAnims, Used by Animatable3DObj's
** TODO: make HAnims accessible from the HMODELS (or Animatable3DObj...)
*/
virtual AssetIterator * Create_HAnim_Iterator(void);
virtual HAnimClass * Get_HAnim(const char * name);
virtual bool Add_Anim (HAnimClass *new_anim) { return HAnimManager.Add_Anim (new_anim); }
/*
** Access to textures
*/
// virtual AssetIterator * Create_Texture_Iterator(void);
HashTemplateClass<StringClass,TextureClass*>& Texture_Hash() { return TextureHash; }
static void Log_Texture_Statistics();
virtual TextureClass * Get_Texture
(
const char * filename,
MipCountType mip_level_count=MIP_LEVELS_ALL,
WW3DFormat texture_format=WW3D_FORMAT_UNKNOWN,
bool allow_compression=true,
TextureBaseClass::TexAssetType type=TextureBaseClass::TEX_REGULAR,
bool allow_reduction=true
);
virtual void Release_All_Textures(void);
virtual void Release_Unused_Textures(void);
virtual void Release_Texture(TextureClass *);
virtual void Load_Procedural_Textures();
virtual MetalMapManagerClass* Peek_Metal_Map_Manager() { return MetalManager; }
/*
** Access to Font3DInstances. (These are not saved, we just use the
** asset manager as a convienient way to create them.)
*/
virtual Font3DInstanceClass * Get_Font3DInstance( const char * name);
/*
** Access to FontChars. Used by Render2DSentenceClass
*/
virtual FontCharsClass * Get_FontChars( const char * name, int point_size, bool is_bold = false );
/*
** Access to HTrees, Used by Animatable3DObj's
*/
virtual AssetIterator * Create_HTree_Iterator(void);
virtual HTreeClass * Get_HTree(const char * name);
/*
** Prototype Loaders, The user can register new loaders here. Note that
** a the pointer to your loader will be stored inside the asset manager.
** For this reason, your loader should be a static or global object.
*/
virtual void Register_Prototype_Loader(PrototypeLoaderClass * loader);
/*
** The Add_Prototype is public so that we can add prototypes for procedurally
** generated objects to the asset manager.
*/
void Add_Prototype(PrototypeClass * newproto);
void Remove_Prototype(PrototypeClass *proto);
void Remove_Prototype(const char *name);
PrototypeClass * Find_Prototype(const char * name);
/*
** Load on Demand
*/
bool Get_WW3D_Load_On_Demand( void ) { return WW3D_Load_On_Demand; }
void Set_WW3D_Load_On_Demand( bool on_off ) { WW3D_Load_On_Demand = on_off; }
/*
** Add fog to objects on load
*/
bool Get_Activate_Fog_On_Load( void ) { return Activate_Fog_On_Load; }
void Set_Activate_Fog_On_Load( bool on_off ) { Activate_Fog_On_Load = on_off; }
// Log texture statistics
void Log_All_Textures();
protected:
/*
** Access to Font3DData. (These are privately managed/accessed)
*/
virtual AssetIterator * Create_Font3DData_Iterator(void);
virtual void Add_Font3DData(Font3DDataClass * font);
virtual void Remove_Font3DData(Font3DDataClass * font);
virtual Font3DDataClass * Get_Font3DData(const char * name);
virtual void Release_All_Font3DDatas( void);
virtual void Release_Unused_Font3DDatas( void);
virtual void Release_All_FontChars( void );
void Free(void);
PrototypeLoaderClass * Find_Prototype_Loader(int chunk_id);
bool Load_Prototype(ChunkLoadClass & cload);
/*
** Compile time control over the dynamic arrays:
*/
enum
{
PROTOLOADERS_VECTOR_SIZE = 32,
PROTOLOADERS_GROWTH_RATE = 16,
PROTOTYPES_VECTOR_SIZE = 256,
PROTOTYPES_GROWTH_RATE = 32,
};
/*
** Prototype Loaders
** These objects are responsible for importing certain W3D chunk types and turning
** them into prototypes.
*/
DynamicVectorClass < PrototypeLoaderClass * > PrototypeLoaders;
/*
** Prototypes
** These objects are abstract factories for named render objects. Prototypes is
** a dynamic array of pointers to the currently loaded prototypes.
*/
DynamicVectorClass < PrototypeClass * > Prototypes;
/*
** Prototype Hash Table
** This structure is simply used to speed up the name lookup for prototypes
*/
enum
{
PROTOTYPE_HASH_TABLE_SIZE = 4096,
PROTOTYPE_HASH_BITS = 12,
PROTOTYPE_HASH_MASK = 0x00000FFF
};
PrototypeClass * * PrototypeHashTable;
/*
** managers of HTrees, HAnims, Textures....
*/
HTreeManagerClass HTreeManager;
HAnimManagerClass HAnimManager;
/*
** When enabled, this handles all the caching for the texture class.
** If NULL then textures are not being cached.
*/
TextureFileCache * TextureCache;
/*
** list of Font3DDatas
*/
SList<Font3DDataClass> Font3DDatas;
/*
** list of FontChars
*/
SimpleDynVecClass<FontCharsClass*> FontCharsList;
/*
** Should .W3D be loaded if not in memory
*/
bool WW3D_Load_On_Demand;
/*
** Should we activate fog on objects while loading them
*/
bool Activate_Fog_On_Load;
// Metal Map Manager
MetalMapManagerClass * MetalManager;
/*
** Texture hash table for quick texture lookups
*/
HashTemplateClass<StringClass, TextureClass *> TextureHash;
/*
** The 3d asset manager is a singleton, there should be only
** one and it is accessible through Get_Instance()
*/
static WW3DAssetManager * TheInstance;
/*
** the iterator classes are friends
*/
friend class RObjIterator;
friend class HAnimIterator;
friend class HTreeIterator;
friend class Font3DDataIterator;
friend class TextureIterator;
// Font3DInstance need access to the Font3DData
friend class Font3DInstanceClass;
};
#endif

View File

@@ -0,0 +1,115 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "assetstatus.h"
#include "hashtemplate.h"
#include "wwstring.h"
#include "rawfile.h"
AssetStatusClass AssetStatusClass::Instance;
const char* ReportCategoryNames[AssetStatusClass::REPORT_COUNT]={
"LOAD_ON_DEMAND_ROBJ",
"LOAD_ON_DEMAND_HANIM",
"LOAD_ON_DEMAND_HTREE",
"MISSING_ROBJ",
"MISSING_HANIM",
"MISSING_HTREE"
};
AssetStatusClass::AssetStatusClass()
:
Reporting (true),
LoadOnDemandReporting(false)
{
}
AssetStatusClass::~AssetStatusClass()
{
#ifdef WWDEBUG
if (Reporting) {
StringClass report("Load-on-demand and missing assets report\r\n\r\n");
for (int i=0;i<REPORT_COUNT;++i) {
report+="Category: ";
report+=ReportCategoryNames[i];
report+="\r\n\r\n";
HashTemplateIterator<StringClass,int> ite(ReportHashTables[i]);
for (ite.First();!ite.Is_Done();ite.Next()) {
report+=ite.Peek_Key();
int count=ite.Peek_Value();
if (count>1) {
StringClass tmp(0,true);
tmp.Format("\t(reported %d times)",count);
report+=tmp;
}
report+="\r\n";
}
report+="\r\n";
}
if (report.Get_Length()) {
RawFileClass raw_log_file("asset_report.txt");
raw_log_file.Create();
raw_log_file.Open(RawFileClass::WRITE);
raw_log_file.Write(report,report.Get_Length());
raw_log_file.Close();
}
}
#endif
}
void AssetStatusClass::Add_To_Report(int index, const char* name)
{
StringClass lower_case_name(name,true);
_strlwr(lower_case_name.Peek_Buffer());
// This is a bit slow - two accesses to the same member, but currently there's no better way to do it.
int count=ReportHashTables[index].Get(lower_case_name);
count++;
ReportHashTables[index].Set_Value(lower_case_name,count);
}
void AssetStatusClass::Report_Load_On_Demand_RObj(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_ROBJ,name);
}
void AssetStatusClass::Report_Load_On_Demand_HAnim(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_HANIM,name);
}
void AssetStatusClass::Report_Load_On_Demand_HTree(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_HTREE,name);
}
void AssetStatusClass::Report_Missing_RObj(const char* name)
{
Add_To_Report(REPORT_MISSING_ROBJ,name);
}
void AssetStatusClass::Report_Missing_HAnim(const char* name)
{
Add_To_Report(REPORT_MISSING_HANIM,name);
}
void AssetStatusClass::Report_Missing_HTree(const char* name)
{
Add_To_Report(REPORT_MISSING_HTREE,name);
}

View File

@@ -0,0 +1,68 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef WW3D_ASSET_STATUS_H
#define WW3D_ASSET_STATUS_H
#include "always.h"
#include "hashtemplate.h"
class AssetStatusClass
{
public:
enum {
REPORT_LOAD_ON_DEMAND_ROBJ,
REPORT_LOAD_ON_DEMAND_HANIM,
REPORT_LOAD_ON_DEMAND_HTREE,
REPORT_MISSING_ROBJ,
REPORT_MISSING_HANIM,
REPORT_MISSING_HTREE,
REPORT_COUNT
};
AssetStatusClass();
~AssetStatusClass();
void Enable_Reporting(bool enable) { Reporting=enable; }
void Enable_Load_On_Demand_Reporting(bool enable) { LoadOnDemandReporting=enable; }
void Report_Load_On_Demand_RObj(const char* name);
void Report_Load_On_Demand_HAnim(const char* name);
void Report_Load_On_Demand_HTree(const char* name);
void Report_Missing_RObj(const char* name);
void Report_Missing_HAnim(const char* name);
void Report_Missing_HTree(const char* name);
static AssetStatusClass* Peek_Instance() { return &Instance; }
private:
bool Reporting;
bool LoadOnDemandReporting;
static AssetStatusClass Instance;
HashTemplateClass<StringClass, int> ReportHashTables[REPORT_COUNT];
void Add_To_Report(int index, const char* name);
};
#endif

View File

@@ -0,0 +1,448 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitmaphandler.h"
#include "wwdebug.h"
#include "colorspace.h"
void Bitmap_Assert(bool condition)
{
WWASSERT(condition);
}
void BitmapHandlerClass::Create_Mipmap_B8G8R8A8(
unsigned char* dest_surface,
unsigned dest_surface_pitch,
unsigned char* src_surface,
unsigned src_surface_pitch,
unsigned width,
unsigned height)
{
unsigned src_pitch=src_surface_pitch/4;
for (unsigned y=0;y<height;y+=2) {
unsigned* dest=(unsigned*)dest_surface;
dest_surface+=dest_surface_pitch;
unsigned* src=(unsigned*)src_surface;
src_surface+=src_surface_pitch;
for (unsigned x=0;x<width;x+=2) {
unsigned bgra3=src[src_pitch];
unsigned bgra1=*src++;
unsigned bgra4=src[src_pitch];
unsigned bgra2=*src++;
*dest++=Combine_A8R8G8B8(bgra1,bgra2,bgra3,bgra4);
}
}
}
void BitmapHandlerClass::Copy_Image_Generate_Mipmap(
unsigned width,
unsigned height,
unsigned char* dest_surface,
unsigned dest_pitch,
WW3DFormat dest_format,
unsigned char* src_surface,
unsigned src_pitch,
WW3DFormat src_format,
unsigned char* mip_surface,
unsigned mip_pitch,
const Vector3& hsv_shift)
{
// Optimized loop if source and destination are 32 bit
bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
if (src_format==dest_format && src_format==WW3D_FORMAT_A8R8G8B8) {
dest_pitch/=4;
src_pitch/=4;
mip_pitch/=4;
for (unsigned y=0;y<height/2;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=2*y*dest_pitch;
unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=y*2*src_pitch;
unsigned* mip_ptr=(unsigned*)mip_surface;
mip_ptr+=y*mip_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<width/2;x++) {
b8g8r8a8_10=src_ptr[src_pitch];
dest_ptr[dest_pitch]=b8g8r8a8_10;
b8g8r8a8_00=*src_ptr++;
*dest_ptr++=b8g8r8a8_00;
b8g8r8a8_11=src_ptr[src_pitch];
dest_ptr[dest_pitch]=b8g8r8a8_11;
b8g8r8a8_01=*src_ptr++;
*dest_ptr++=b8g8r8a8_01;
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
*mip_ptr++=b8g8r8a8;
}
}
return;
}
WWASSERT(src_format!=WW3D_FORMAT_P8); // This function doesn't support paletted formats
unsigned src_bpp=Get_Bytes_Per_Pixel(src_format);
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
for (unsigned y=0;y<height/2;++y) {
unsigned char* dest_ptr=dest_surface+2*y*dest_pitch;
unsigned char* src_ptr=src_surface+y*2*src_pitch;
unsigned char* mip_ptr=mip_surface+y*mip_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=dest_bpp) {
Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8_00);
Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_bpp,dest_format,b8g8r8a8_01);
Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_pitch,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_pitch,dest_format,b8g8r8a8_10);
Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_pitch,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_bpp+dest_pitch,dest_format,b8g8r8a8_11);
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
Write_B8G8R8A8(mip_ptr,dest_format,b8g8r8a8);
}
}
}
// ----------------------------------------------------------------------------
//
// Copy image from source surface to destination surface with stretch and color
// space conversion if needed. If 'generate_mip_level' is set, process image
// in 2x2 blocks and generate mipmap on top of the original source image while
// copying.
//
// ----------------------------------------------------------------------------
void BitmapHandlerClass::Copy_Image(
unsigned char* dest_surface,
unsigned dest_surface_width,
unsigned dest_surface_height,
unsigned dest_surface_pitch,
WW3DFormat dest_surface_format,
unsigned char* src_surface,
unsigned src_surface_width,
unsigned src_surface_height,
unsigned src_surface_pitch,
WW3DFormat src_surface_format,
const unsigned char* src_palette,
unsigned src_palette_bpp,
bool generate_mip_level,
const Vector3& hsv_shift)
{
WWASSERT(dest_surface_width);
WWASSERT(dest_surface_height);
// Bumpmap?
if (dest_surface_format==WW3D_FORMAT_U8V8 ||
dest_surface_format==WW3D_FORMAT_L6V5U5 ||
dest_surface_format==WW3D_FORMAT_X8L8V8U8) {
unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
for( unsigned y=0; y<dest_surface_height; y++ ) {
unsigned char* dest_ptr=dest_surface;
dest_ptr+=y*dest_surface_pitch;
unsigned char* src_ptr_mid=src_surface;
src_ptr_mid+=y*src_surface_pitch;
unsigned char* src_ptr_next_line = ( src_ptr_mid + src_surface_pitch );
unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_surface_pitch );
if( y == src_surface_height-1 ) // Don't go past the last line
src_ptr_next_line = src_ptr_mid;
if( y == 0 ) // Don't go before first line
src_ptr_prev_line = src_ptr_mid;
for( unsigned x=0; x<dest_surface_width; x++ ) {
unsigned pixel00;
unsigned pixel01;
unsigned pixelM1;
unsigned pixel10;
unsigned pixel1M;
Read_B8G8R8A8(pixel00,src_ptr_mid,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel01,src_ptr_mid+src_bpp,src_surface_format,NULL,0);
Read_B8G8R8A8(pixelM1,src_ptr_mid-src_bpp,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel10,src_ptr_prev_line,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel1M,src_ptr_next_line,src_surface_format,NULL,0);
// Convert to luminance
unsigned char bv00;
unsigned char bv01;
unsigned char bvM1;
unsigned char bv10;
unsigned char bv1M;
Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
int iDu = (vM1-v01); // The delta-u bump value
int iDv = (v1M-v10); // The delta-v bump value
if( (v00 < vM1) && (v00 < v01) ) { // If we are at valley
iDu = vM1-v00; // Choose greater of 1st order diffs
if( iDu < v00-v01 )
iDu = v00-v01;
}
// The luminance bump value (land masses are less shiny)
unsigned short uL = ( v00>1 ) ? 63 : 127;
switch(dest_surface_format) {
case WW3D_FORMAT_U8V8:
*dest_ptr++ = (unsigned char)iDu;
*dest_ptr++ = (unsigned char)iDv;
break;
case WW3D_FORMAT_L6V5U5:
*(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
*(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
*(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
dest_ptr += 2;
break;
case WW3D_FORMAT_X8L8V8U8:
*dest_ptr++ = (unsigned char)iDu;
*dest_ptr++ = (unsigned char)iDv;
*dest_ptr++ = (unsigned char)uL;
*dest_ptr++ = (unsigned char)0L;
break;
default:
WWASSERT(0); // Unknown bumpmap format
break;
}
// Move one pixel to the left (src is 32-bpp)
src_ptr_mid+=src_bpp;
src_ptr_prev_line+=src_bpp;
src_ptr_next_line+=src_bpp;
}
}
return;
}
bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
if (src_surface_format==dest_surface_format && (src_surface_format==WW3D_FORMAT_A8R8G8B8 || src_surface_format==WW3D_FORMAT_X8R8G8B8)) {
// One-to-one copy or scaling?
dest_surface_pitch/=4;
src_surface_pitch/=4;
if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
// Generate the next mip level while copying the current surface?
if (generate_mip_level) {
if (dest_surface_width==1) {
unsigned b8g8r8a8=*(unsigned*)src_surface;
if (has_hsv_shift) Recolor(b8g8r8a8,hsv_shift);
*(unsigned*)dest_surface=b8g8r8a8;
}
else {
for (unsigned y=0;y<dest_surface_height/2;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=2*y*dest_surface_pitch;
unsigned* src_ptr=(unsigned*)src_surface;
unsigned* mip_ptr=src_ptr;
src_ptr+=y*2*src_surface_pitch;
mip_ptr+=y*src_surface_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<dest_surface_width/2;x++) {
// Read four pixels from the source
b8g8r8a8_10=src_ptr[src_surface_pitch];
b8g8r8a8_00=*src_ptr++;
b8g8r8a8_11=src_ptr[src_surface_pitch];
b8g8r8a8_01=*src_ptr++;
// Recolor if necessary
if (has_hsv_shift) {
Recolor(b8g8r8a8_00,hsv_shift);
Recolor(b8g8r8a8_01,hsv_shift);
Recolor(b8g8r8a8_10,hsv_shift);
Recolor(b8g8r8a8_11,hsv_shift);
}
// Write the four pixels to the destination
dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
*dest_ptr++=b8g8r8a8_00;
dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
*dest_ptr++=b8g8r8a8_01;
// Write combined four pixels to the destination mip map level
*mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
}
}
}
}
else {
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=y*dest_surface_pitch;
const unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x) {
unsigned b8g8r8a8=*src_ptr++;
Recolor(b8g8r8a8,hsv_shift);
*dest_ptr++=b8g8r8a8;
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x) {
*dest_ptr++=*src_ptr++;
}
}
}
}
}
else {
// For now do only point-sampling
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=y*dest_surface_pitch;
unsigned src_y=y*src_surface_height/dest_surface_height;
const unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=src_y*src_surface_pitch;
for (unsigned x=0;x<dest_surface_width;++x) {
unsigned src_x=x*src_surface_width/dest_surface_width;
unsigned b8g8r8a8=src_ptr[src_x];
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
*dest_ptr++=b8g8r8a8;
}
}
}
return;
}
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_surface_format);
unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
// One-to-one copy or scaling?
if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
// Generate the next mip level while copying the current surface?
if (generate_mip_level) {
WWASSERT(src_surface_format!=WW3D_FORMAT_P8); // Paletted textures can't be mipmapped
if (dest_surface_width==1) {
unsigned char* dest_ptr=dest_surface;
unsigned char* src_ptr=src_surface;
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_surface_format,src_palette,src_palette_bpp);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8);
}
else {
for (unsigned y=0;y<dest_surface_height/2;++y) {
unsigned char* dest_ptr=dest_surface+2*y*dest_surface_pitch;
unsigned char* src_ptr=src_surface+y*2*src_surface_pitch;
unsigned char* mip_ptr=src_surface+y*src_surface_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<dest_surface_width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=src_bpp) {
// Read four pixels from the source
Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
// Recolor if necessary
if (has_hsv_shift) {
Recolor(b8g8r8a8_00,hsv_shift);
Recolor(b8g8r8a8_01,hsv_shift);
Recolor(b8g8r8a8_10,hsv_shift);
Recolor(b8g8r8a8_11,hsv_shift);
}
// Write the four pixels to the destination
Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
// Write combined four pixels to the destination mip map level
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
Write_B8G8R8A8(mip_ptr,src_surface_format,b8g8r8a8);
}
}
}
}
else {
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
const unsigned char* src_ptr=src_surface+y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp);
}
}
}
}
}
else {
// For now do only point-sampling
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
unsigned src_y=y*src_surface_height/dest_surface_height;
const unsigned char* src_ptr=src_surface+src_y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
unsigned src_x=x*src_surface_width/dest_surface_width;
src_x*=src_bpp;
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
unsigned src_x=x*src_surface_width/dest_surface_width;
src_x*=src_bpp;
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp);
}
}
}
}
}

View File

@@ -0,0 +1,484 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BITMAPHANDLER_H
#define BITMAPHANDLER_H
#include "always.h"
#include "ww3dformat.h"
#include "vector3.h"
#include "colorspace.h"
void Bitmap_Assert(bool condition);
class BitmapHandlerClass
{
public:
// Read pixel at given address
WWINLINE static void Read_B8G8R8A8(
unsigned char* argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
// Read pixel at given address
WWINLINE static void Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
// Read pixel from surface
WWINLINE static void Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
int x,
int y,
int width,
int height,
const unsigned char* palette,
unsigned palette_bpp);
WWINLINE static void Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* argb);
WWINLINE static void Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned& argb);
WWINLINE static void Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
WWINLINE static void Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp,
const Vector3& hsv_shift);
WWINLINE static unsigned Combine_A8R8G8B8(
unsigned bgra1,
unsigned bgra2,
unsigned bgra3,
unsigned bgra4);
static void Create_Mipmap_B8G8R8A8(
unsigned char* dest_surface,
unsigned dest_surface_pitch,
unsigned char* src_surface,
unsigned src_surface_pitch,
unsigned width,
unsigned height);
static void Copy_Image_Generate_Mipmap(
unsigned width,
unsigned height,
unsigned char* dest_surface,
unsigned dest_pitch,
WW3DFormat dest_format,
unsigned char* src_surface,
unsigned src_pitch,
WW3DFormat src_format,
unsigned char* mip_surface,
unsigned mip_pitch,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
static void Copy_Image(
unsigned char* dest_surface,
unsigned dest_surface_width,
unsigned dest_surface_height,
unsigned dest_surface_pitch,
WW3DFormat dest_surface_format,
unsigned char* src_surface,
unsigned src_surface_width,
unsigned src_surface_height,
unsigned src_surface_pitch,
WW3DFormat src_surface_format,
const unsigned char* src_palette,
unsigned src_palette_bpp,
bool generate_mip_level,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
};
// ----------------------------------------------------------------------------
//
// Read color value of given type in BGRA (D3D) byte order. Regarless
// of the source format the color value is converted to 32-bit format.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned char* argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
switch (src_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)argb=*(unsigned*)src_ptr;
break;
case WW3D_FORMAT_R8G8B8:
*argb++=src_ptr[0];
*argb++=src_ptr[1];
*argb++=src_ptr[2];
*argb++=0xff;
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
*argb++=((tmp&0x000f)<<4);
*argb++=((tmp&0x00f0));
*argb++=((tmp&0x0f00)>>4);
*argb++=((tmp&0xf000)>>8);
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
argb[3]=tmp&0x8000 ? 0xff : 0x0;
argb[2]=(tmp>>7)&0xf8;
argb[1]=(tmp>>2)&0xf8;
argb[0]=(tmp<<3)&0xf8;
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
argb[3]=0xff;
argb[2]=(tmp>>8)&0xf8;
argb[1]=(tmp>>3)&0xfc;
argb[0]=(tmp<<3)&0xf8;
}
break;
case WW3D_FORMAT_R3G3B2:
{
unsigned char tmp=*src_ptr;
argb[3]=0xff;
argb[2]=tmp&0xe0;
argb[1]=(tmp<<3)&0xe0;
argb[0]=(tmp<<6)&0xc0;
}
break;
case WW3D_FORMAT_L8:
{
unsigned char tmp=*src_ptr++;
*argb++=tmp;
*argb++=tmp;
*argb++=tmp;
*argb++=0xff;
}
break;
case WW3D_FORMAT_A8:
{
*argb++=0;
*argb++=0;
*argb++=0;
*argb++=*src_ptr++;
}
break;
case WW3D_FORMAT_P8:
{
unsigned char index=*src_ptr++;
switch (palette_bpp) {
case 4:
*argb++=palette[palette_bpp*index+3];
*argb++=palette[palette_bpp*index+2];
*argb++=palette[palette_bpp*index+1];
*argb++=palette[palette_bpp*index+0];
break;
case 3:
*argb++=palette[palette_bpp*index+2];
*argb++=palette[palette_bpp*index+1];
*argb++=palette[palette_bpp*index+0];
*argb++=0xff;
break;
case 2:
case 1:
default:
Bitmap_Assert(0);
break;
}
}
break;
case WW3D_FORMAT_DXT1:
case WW3D_FORMAT_DXT2:
case WW3D_FORMAT_DXT3:
case WW3D_FORMAT_DXT4:
case WW3D_FORMAT_DXT5:
default: Bitmap_Assert(0); break;
}
}
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
Read_B8G8R8A8((unsigned char*)&argb,src_ptr,src_format,palette,palette_bpp);
}
// Read pixel from surface
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
int x,
int y,
int width,
int height,
const unsigned char* palette,
unsigned palette_bpp)
{
if (x<0 || y<0 || x>=width || y>=height) {
argb=0;
return;
}
unsigned bpp=Get_Bytes_Per_Pixel(src_format);
Read_B8G8R8A8(
argb,
src_ptr+bpp*x+width*bpp*y,
src_format,
palette,
palette_bpp);
}
// ----------------------------------------------------------------------------
//
// Write color value of given type in BGRA (D3D) byte order. The source value
// is always 32 bit and it is converted to defined destination format.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* argb)
{
switch (dest_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)dest_ptr=*(unsigned*)argb;
break;
case WW3D_FORMAT_R8G8B8:
*dest_ptr++=*argb++;
*dest_ptr++=*argb++;
*dest_ptr++=*argb++;
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp;
tmp=((argb[3])&0xf0)<<8;
tmp|=((argb[2])&0xf0)<<4;
tmp|=((argb[1])&0xf0);
tmp|=((argb[0])&0xf0)>>4;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp;
tmp=argb[3] ? 0x8000 : 0x0;
tmp|=((argb[2])&0xf8)<<7;
tmp|=((argb[1])&0xf8)<<2;
tmp|=((argb[0])&0xf8)>>3;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp;
tmp=((argb[2])&0xf8)<<8;
tmp|=((argb[1])&0xfc)<<3;
tmp|=((argb[0])&0xf8)>>3;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_R3G3B2:
{
unsigned char tmp;
tmp=((argb[2])&0xe0);
tmp|=((argb[1])&0xe0)>>3;
tmp|=((argb[0])&0xc0)>>6;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_L8:
{
// CIE Req. 709: Y709 = 0.2125R + 0.7154G + 0.0721B
unsigned char tmp = (unsigned char) ( (
((unsigned int)argb[0] * (unsigned int)0x1275) + // 0.0721B
((unsigned int)argb[1] * (unsigned int)0xB725) + // 0.7154G (rounded up so FF, FF, FF becomes FF)
((unsigned int)argb[2] * (unsigned int)0x3666) // 0.2125R
) >> 16);
*dest_ptr++=tmp;
}
break;
case WW3D_FORMAT_A8:
{
*dest_ptr++=*argb++;
}
break;
case WW3D_FORMAT_DXT1:
case WW3D_FORMAT_DXT2:
case WW3D_FORMAT_DXT3:
case WW3D_FORMAT_DXT4:
case WW3D_FORMAT_DXT5:
case WW3D_FORMAT_P8: // Paletted destination not supported
default: Bitmap_Assert(0); break;
}
}
WWINLINE void BitmapHandlerClass::Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned& argb)
{
Write_B8G8R8A8(dest_ptr,dest_format,(unsigned char*)&argb);
}
// ----------------------------------------------------------------------------
//
// Copy pixel. Perform color space conversion if needed. The source and
// destination are always D3D-style BGRA.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
// Color space conversion needed?
if (dest_format==src_format) {
switch (dest_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)dest_ptr=*(unsigned*)src_ptr;
break;
case WW3D_FORMAT_R8G8B8:
*dest_ptr++=src_ptr[0];
*dest_ptr++=src_ptr[1];
*dest_ptr++=src_ptr[2];
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x000f)<<12)|((tmp&0x00f0)<<4)|((tmp&0x0f00)>>4)|((tmp&0xf000)>>12);
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x001f)<<11)|((tmp&0x03e0)<<1)|((tmp&0x7c00)>>9)|((tmp&0x8000)>>15);
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x001f)<<11)|(tmp&0x07e0)|((tmp&0xf800)>>11);
}
break;
case WW3D_FORMAT_R3G3B2:
case WW3D_FORMAT_L8:
case WW3D_FORMAT_A8: *dest_ptr++=*src_ptr++;
break;
case WW3D_FORMAT_P8: // Paletted destinations not supported
default: Bitmap_Assert(0); break;
}
}
else {
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_format,palette,palette_bpp);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8);
}
}
// ----------------------------------------------------------------------------
//
// Copy pixel with HSV shift. The source and destination are always D3D-style BGRA.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp,
const Vector3& hsv_shift)
{
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_format,palette,palette_bpp);
Recolor(b8g8r8a8,hsv_shift);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8);
}
WWINLINE unsigned BitmapHandlerClass::Combine_A8R8G8B8(
unsigned bgra1,
unsigned bgra2,
unsigned bgra3,
unsigned bgra4)
{
bgra1&=0xfcfcfcfc;
bgra2&=0xfcfcfcfc;
bgra3&=0xfcfcfcfc;
bgra4&=0xfcfcfcfc;
bgra1>>=2;
bgra2>>=2;
bgra3>>=2;
bgra4>>=2;
bgra1+=bgra2;
bgra3+=bgra4;
bgra1+=bgra3;
return bgra1;
}
#endif

View File

@@ -0,0 +1,299 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando/G *
* *
* $Archive:: /Commando/Code/ww3d2/bmp2d.cpp $*
* *
* $Org Author:: Jani_p $*
* *
* $Author:: Kenny_m $*
* *
* $Modtime:: 08/05/02 10:44a $*
* *
* $Revision:: 12 $*
* *
* 08/05/02 KM Texture class redesign
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bmp2d.h"
#include "pot.h"
#include "ww3d.h"
#include "texture.h"
#include "surfaceclass.h"
#include "assetmgr.h"
#include "textureloader.h"
#include "ww3dformat.h"
Bitmap2DObjClass::Bitmap2DObjClass
(
const char *filename,
float screen_x,
float screen_y,
bool center,
bool additive,
bool colorizable,
int usable_width,
int usable_height,
bool ignore_alpha
)
: DynamicScreenMeshClass(2, 4)
{
int resw, resh, resbits;
bool windowed;
// find the resolution (for centering and pixel to pixel translation)
WW3D::Get_Device_Resolution(resw, resh, resbits, windowed);
// This should be the correct way to do things
// but other code expects an aspect ratio of 1.0
// Hector Yee 2/22/01
// Set_Aspect(resh/(float)resw);
// load up the surfaces file name
TextureClass *tex = WW3DAssetManager::Get_Instance()->Get_Texture(filename, MIP_LEVELS_1);
if (!tex->Is_Initialized())
TextureLoader::Request_Foreground_Loading(tex);
SurfaceClass *surface = tex->Get_Surface_Level(0);
if (!surface) {
surface = NEW_REF(SurfaceClass, (32, 32, Get_Valid_Texture_Format(WW3D_FORMAT_R8G8B8,true)));
}
SurfaceClass::SurfaceDescription sd;
surface->Get_Description(sd);
if (usable_width == -1)
usable_width = sd.Width;
if (usable_height == -1)
usable_height = sd.Height;
// if we requested the image to be centered around a point adjust the
// coordinates accordingly.
if (center) {
screen_x -= ((float)usable_width / resw) / 2;
screen_y -= ((float)usable_height / resh) / 2;
}
// The image will be broken down into square textures. The size of these
// textures will be the smallest POT (power of two) which is equal or
// greater than the smaller dimension of the image. Also, the pieces can
// never be larger than 256 texels because some rendering devices don't
// support textures larger than that.
int surf_w = usable_width;
int surf_h = usable_height;
int piece = Find_POT(MIN(surf_w, surf_h));
piece = MIN(piece, 256);
// now take the image in question and break it down into
// "piece"x"piece"-pixel polygons and calculate the number of textures
// based from those calculations.
int mw = (surf_w & (piece - 1)) ? (surf_w / piece)+1 : (surf_w /piece);
int mh = (surf_h & (piece - 1)) ? (surf_h / piece)+1 : (surf_h /piece);
// for every square texture it takes four vertexes to express the two
// polygons.
Resize(mw * mh *2, mw * mh * 4);
// Set shader to additive if requested, else alpha or opaque depending on
// whether the texture has an alpha channel. Sorting is always set so that
// sortbias can be used to determine occlusion between the various 2D
// elements.
ShaderClass shader;
if (additive) {
shader = ShaderClass::_PresetAdditive2DShader;
} else {
if (ignore_alpha == false && Has_Alpha(sd.Format)) {
shader = ShaderClass::_PresetAlpha2DShader;
} else {
shader = ShaderClass::_PresetOpaque2DShader;
}
}
Enable_Sort();
// If we want to be able to colorize this bitmap later (by setting
// emissive color for the vertex material, or via a vertex emissive color
// array) we need to enable the primary gradient in the shader (it is
// disabled in the 2D presets), and set an appropriate vertex material.
if (colorizable) {
shader.Set_Primary_Gradient(ShaderClass::GRADIENT_MODULATE);
VertexMaterialClass *vertex_material = NEW_REF( VertexMaterialClass, ());
vertex_material->Set_Ambient(0.0f, 0.0f, 0.0f);
vertex_material->Set_Diffuse(0.0f, 0.0f, 0.0f);
vertex_material->Set_Specular(0.0f, 0.0f, 0.0f);
vertex_material->Set_Emissive(1.0f, 1.0f, 1.0f);
Set_Vertex_Material(vertex_material, true);
vertex_material->Release_Ref();
}
// Set desired shader.
Set_Shader(shader);
// loop through the rows and columns of the image and make valid
// textures from the pieces.
for (int lpy = 0, tlpy=0; lpy < mh; lpy++, tlpy += piece) {
for (int lpx = 0, tlpx = 0; lpx < mw; lpx++, tlpx += piece) {
// figure the desired width and height of the texture (max "piece")
int iw = MIN(piece, usable_width - (tlpx));
int ih = MIN(piece, usable_height - (tlpy));
int pot = MAX(Find_POT(iw), Find_POT(ih));
// create the texture and turn MIP-mapping off.
SurfaceClass *piece_surface=NEW_REF(SurfaceClass,(pot,pot,sd.Format));
piece_surface->Copy(0,0,tlpx,tlpy,pot,pot,surface);
TextureClass *piece_texture =NEW_REF(TextureClass,(piece_surface,MIP_LEVELS_1));
piece_texture->Get_Filter().Set_U_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
piece_texture->Get_Filter().Set_V_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
REF_PTR_RELEASE(piece_surface);
// calculate our actual texture coordinates based on the difference between
// the width and height of the texture and the width and height the font
// occupys.
float tw = (float)iw / (float)pot;
float th = (float)ih / (float)pot;
// convert image width and image height to normalized values
float vw = (float)iw / (float)resw;
float vh = (float)ih / (float)resh;
// figure out the screen space x and y positions of the object in question.
float x = screen_x + (((float)tlpx) / (float)resw);
float y = screen_y + (((float)tlpy) / (float)resh);
Set_Texture(piece_texture);
Begin_Tri_Strip();
Vertex( x, y, 0, 0, 0);
Vertex( x + vw, y, 0, tw, 0);
Vertex( x, y + vh, 0, 0, th);
Vertex( x + vw, y + vh, 0, tw, th);
End_Tri_Strip();
// release our reference to the texture
REF_PTR_RELEASE(piece_texture);
}
}
REF_PTR_RELEASE(tex);
REF_PTR_RELEASE(surface);
Set_Dirty();
}
Bitmap2DObjClass::Bitmap2DObjClass
(
TextureClass *texture,
float screen_x,
float screen_y,
bool center,
bool additive,
bool colorizable,
bool ignore_alpha
)
: DynamicScreenMeshClass(2, 4)
{
int resw, resh, resbits;
bool windowed;
// find the resolution (for centering and pixel to pixel translation)
WW3D::Get_Device_Resolution(resw, resh, resbits, windowed);
// This should be the correct way to do things
// but other code expects an aspect ratio of 1.0
// Hector Yee 2/22/01
//Set_Aspect(resh/(float)resw);
// Find the dimensions of the texture:
// SurfaceClass::SurfaceDescription sd;
// texture->Get_Level_Description(sd);
if (!texture->Is_Initialized())
TextureLoader::Request_Foreground_Loading(texture);
// convert image width and image height to normalized values
float vw = (float) texture->Get_Width() / (float)resw;
float vh = (float) texture->Get_Height() / (float)resh;
// if we requested the image to be centered around a point adjust the
// coordinates accordingly.
if (center) {
screen_x -= vw / 2;
screen_y -= vh / 2;
}
// Set shader to additive if requested, else alpha or opaque depending on whether the texture
// has an alpha channel. Sorting is never set - if you wish to sort these types of objects you
// should use static sort levels (note that static sort levels are not implemented for these
// objects yet, but it should be very simple to do).
ShaderClass shader;
if (additive) {
shader = ShaderClass::_PresetAdditive2DShader;
} else {
if (ignore_alpha == false && Has_Alpha(texture->Get_Texture_Format())) {
shader = ShaderClass::_PresetAlpha2DShader;
} else {
shader = ShaderClass::_PresetOpaque2DShader;
}
}
Disable_Sort();
// If we want to be able to colorize this bitmap later (by setting
// emissive color for the vertex material, or via a vertex emissive color
// array) we need to enable the primary gradient in the shader (it is
// disabled in the 2D presets), and set an appropriate vertex material.
if (colorizable) {
shader.Set_Primary_Gradient(ShaderClass::GRADIENT_MODULATE);
VertexMaterialClass *vertex_material = NEW_REF( VertexMaterialClass, ());
vertex_material->Set_Ambient(0.0f, 0.0f, 0.0f);
vertex_material->Set_Diffuse(0.0f, 0.0f, 0.0f);
vertex_material->Set_Specular(0.0f, 0.0f, 0.0f);
vertex_material->Set_Emissive(1.0f, 1.0f, 1.0f);
Set_Vertex_Material(vertex_material, true);
vertex_material->Release_Ref();
}
// Set desired shader.
Set_Shader(shader);
// Set texture to requested texture:
Set_Texture(texture);
Begin_Tri_Strip();
Vertex( screen_x, screen_y, 0, 0, 0);
Vertex( screen_x + vw, screen_y, 0, 1.0, 0);
Vertex( screen_x, screen_y + vh, 0, 0, 1.0);
Vertex( screen_x + vw, screen_y + vh, 0, 1.0, 1.0);
End_Tri_Strip();
Set_Dirty();
}
RenderObjClass * Bitmap2DObjClass::Clone(void) const
{
return NEW_REF( Bitmap2DObjClass, (*this));
}

View File

@@ -0,0 +1,59 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando/G *
* *
* $Archive:: /Commando/Code/ww3d2/bmp2d.h $*
* *
* $Author:: Hector_y $*
* *
* $Modtime:: 2/21/01 1:31p $*
* *
* $Revision:: 3 $*
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BMP2D_H
#define BMP2D_H
#include "dynamesh.h"
class Bitmap2DObjClass : public DynamicScreenMeshClass
{
public:
Bitmap2DObjClass(const char *filename, float norm_x, float norm_y,
bool center, bool additive, bool colorizable = false, int width = -1, int height = -1, bool ignore_alpha = false);
Bitmap2DObjClass(TextureClass *texture, float norm_x, float norm_y,
bool center, bool additive, bool colorizable = false, bool ignore_alpha = false);
Bitmap2DObjClass( const Bitmap2DObjClass & src) : DynamicScreenMeshClass(src) {}
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_BITMAP2D; }
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/boxrobj.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 10/11/01 2:24p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BOXROBJ_H
#define BOXROBJ_H
#include "always.h"
#include "rendobj.h"
#include "w3d_file.h"
#include "shader.h"
#include "proto.h"
#include "obbox.h"
class VertexMaterialClass;
/**
** BoxRenderObjClass: base class for AABox and OBBox collision boxes
**
** NOTE: these render objects were designed from the start to be used for
** collision boxes. They are designed to normally never render unless you
** set the display mask and then, all boxes of that type will render.
** The display mask is 'AND'ed with the collision bits in the base render
** object class to determine if the box should be rendered. WW3D provides
** an interface for setting this mask in your app.
**
** NOTE2: AABoxRenderObjClass is an axis-aligned box which will be positioned
** at a world-space offset (its local center) from the origin of the transform.
** This is done because AABoxes are used for rotationally invariant collision
** detection so we don't want the boxes to move around as the object rotates.
** For this reason, any AABoxes you use in a hierarchical model should be attached
** to the root and be constructed symmetrically...
**
** NOTE3: OBBoxRenderObjClass is an oriented box which is aligned with its transform
** but can have a center point that is offest from the transform's origin.
**
*/
class BoxRenderObjClass : public RenderObjClass
{
public:
BoxRenderObjClass(void);
BoxRenderObjClass(const W3dBoxStruct & def);
BoxRenderObjClass(const BoxRenderObjClass & src);
BoxRenderObjClass & operator = (const BoxRenderObjClass &);
virtual int Get_Num_Polys(void) const;
virtual const char * Get_Name(void) const;
virtual void Set_Name(const char * name);
void Set_Color(const Vector3 & color);
void Set_Opacity(float opacity) { Opacity = opacity; }
static void Init(void);
static void Shutdown(void);
static void Set_Box_Display_Mask(int mask);
static int Get_Box_Display_Mask(void);
void Set_Local_Center_Extent(const Vector3 & center,const Vector3 & extent);
void Set_Local_Min_Max(const Vector3 & min,const Vector3 & max);
const Vector3 & Get_Local_Center(void) { return ObjSpaceCenter; }
const Vector3 & Get_Local_Extent(void) { return ObjSpaceExtent; }
protected:
virtual void update_cached_box(void) = 0;
void render_box(RenderInfoClass & rinfo,const Vector3 & center,const Vector3 & extent);
void vis_render_box(SpecialRenderInfoClass & rinfo,const Vector3 & center,const Vector3 & extent);
char Name[2*W3D_NAME_LEN];
Vector3 Color;
Vector3 ObjSpaceCenter;
Vector3 ObjSpaceExtent;
float Opacity;
static bool IsInitted;
static int DisplayMask;
};
inline void BoxRenderObjClass::Set_Local_Center_Extent(const Vector3 & center,const Vector3 & extent)
{
ObjSpaceCenter = center;
ObjSpaceExtent = extent;
update_cached_box();
}
inline void BoxRenderObjClass::Set_Local_Min_Max(const Vector3 & min,const Vector3 & max)
{
ObjSpaceCenter = (max + min) / 2.0f;
ObjSpaceExtent = (max - min) / 2.0f;
update_cached_box();
}
/*
** AABoxRenderObjClass -- RenderObject for axis-aligned collision boxes.
*/
class AABoxRenderObjClass : public W3DMPO, public BoxRenderObjClass
{
W3DMPO_GLUE(AABoxRenderObjClass)
public:
AABoxRenderObjClass(void);
AABoxRenderObjClass(const W3dBoxStruct & def);
AABoxRenderObjClass(const AABoxRenderObjClass & src);
AABoxRenderObjClass(const AABoxClass & box);
AABoxRenderObjClass & operator = (const AABoxRenderObjClass &);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// AABoxRenderObjClass Interface
/////////////////////////////////////////////////////////////////////////////
const AABoxClass & Get_Box(void);
protected:
virtual void update_cached_box(void);
AABoxClass CachedBox;
};
inline const AABoxClass & AABoxRenderObjClass::Get_Box(void)
{
Validate_Transform();
update_cached_box();
return CachedBox;
}
/*
** OBBoxRenderObjClass - render object for oriented collision boxes
*/
class OBBoxRenderObjClass : public W3DMPO, public BoxRenderObjClass
{
W3DMPO_GLUE(OBBoxRenderObjClass)
public:
OBBoxRenderObjClass(void);
OBBoxRenderObjClass(const W3dBoxStruct & def);
OBBoxRenderObjClass(const OBBoxRenderObjClass & src);
OBBoxRenderObjClass(const OBBoxClass & box);
OBBoxRenderObjClass & operator = (const OBBoxRenderObjClass &);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// OBBoxRenderObjClass Interface
/////////////////////////////////////////////////////////////////////////////
OBBoxClass & Get_Box(void);
protected:
virtual void update_cached_box(void);
OBBoxClass CachedBox;
};
/*
** Loader for boxes
*/
class BoxLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_BOX; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
// ----------------------------------------------------------------------------
/*
** Prototype for Box objects
*/
class BoxPrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(BoxPrototypeClass)
public:
BoxPrototypeClass(W3dBoxStruct box);
virtual const char * Get_Name(void) const;
virtual int Get_Class_ID(void) const;
virtual RenderObjClass * Create(void);
virtual void DeleteSelf() { delete this; }
private:
W3dBoxStruct Definition;
};
/*
** Instance of the loader which the asset manager installs
*/
extern BoxLoaderClass _BoxLoader;
#endif

View File

@@ -0,0 +1,241 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/BW_Render.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 2/06/01 10:57a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bw_render.h"
#include "vp.h"
#include <string.h>
BW_Render::Buffer::Buffer(unsigned char* buffer_, int scale_)
:
buffer(buffer_),
scale(scale_),
minv(3),
maxv(scale_-3)
{
}
BW_Render::Buffer::~Buffer()
{
}
void BW_Render::Buffer::Set_H_Line(int start_x, int end_x, int y)
{
if (y<minv || y>=maxv || end_x<minv || start_x>=maxv) return;
if (start_x<minv) start_x=minv;
if (end_x>=maxv) end_x=maxv-1;
unsigned char* ptr=buffer+scale*y+start_x;
int w=end_x-start_x;
if (w) {
::memset(ptr,0x00,w);
/* // Blurring (test)
*(ptr-1)&=0x80;
*(ptr-2)&=0xc0;
*(ptr+w)&=0x80;
*(ptr+w+1)&=0xc0;
for (int a=0;a<w;++a) {
*(ptr-scale+a)&=0xc0;
*(ptr+scale+a)&=0xc0;
}
*/
}
}
void BW_Render::Buffer::Fill(unsigned char c)
{
memset(buffer,c,scale*scale);
}
// ------------------------------------------------------------------------------
BW_Render::BW_Render(unsigned char* buffer, int buffer_scale)
:
pixel_buffer(buffer,buffer_scale)
{
}
BW_Render::~BW_Render()
{
}
void BW_Render::Fill(unsigned char c)
{
pixel_buffer.Fill(c);
}
// Sets the vertex coordinate buffer location and scales the vertex locations to the kjkj
void BW_Render::Set_Vertex_Locations(Vector2* vertices_,int count)
{
vertices=vertices_;
float half_scale=pixel_buffer.Scale()*0.5f;
VectorProcessorClass::MulAdd(
reinterpret_cast<float*>(vertices),
half_scale,
half_scale,
count*2);
}
// --------------------------------------------------------------------
static inline bool Cull(
const Vector2& c1,
const Vector2& c2,
const Vector2& c3)
{
float x1=c2[0]-c1[0];
float y1=c2[1]-c1[1];
float x2=c3[0]-c1[0];
float y2=c3[1]-c1[1];
float r=x1*y2-x2*y1;
if (r<0) return false;
return true;
}
void BW_Render::Render_Triangle_Strip(const unsigned long* indices,int index_count)
{
index_count-=2;
bool b=false;
for (int n=0;n<index_count;++n) {
b=!b;
int idx_1=indices[0];
int idx_2=indices[1];
int idx_3=indices[2];
indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])==b) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BW_Render::Render_Triangles(const unsigned long* indices,int index_count)
{
index_count/=3;
for (int n=0;n<index_count;++n) {
int idx_1=*indices++;
int idx_2=*indices++;
int idx_3=*indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BW_Render::Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci)
{
float x_left=xcf[0];
float x_right=x_left;
int ycnt=yci[1]-yci[0];
int y=yci[0];
if (ycnt) {
float x_step_1=(xcf[1]-xcf[0])/float(ycnt);
float x_step_2=(xcf[2]-xcf[0])/float(yci[2]-y);
if (x_step_1>x_step_2) {
float t=x_step_1;
x_step_1=x_step_2;
x_step_2=t;
}
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
else {
if (xcf[0]<xcf[1]) {
x_left=xcf[0];
x_right=xcf[1];
}
else {
x_right=xcf[0];
x_left=xcf[1];
}
}
ycnt=yci[2]-yci[1];
y=yci[1];
if (ycnt) {
float one_per_ycnt=1.0f/float(ycnt);
float x_step_1=(xcf[2]-x_left)*one_per_ycnt;
float x_step_2=(xcf[2]-x_right)*one_per_ycnt;
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
}

View File

@@ -0,0 +1,85 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/bw_render.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 3/26/01 4:53p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BW_RENDER_H__
#define BW_RENDER_H__
#include "always.h"
#include "vector2.h"
#include "vector3.h"
#include "vector3i.h"
class BW_Render
{
// Internal pixel buffer used by the triangle renderer
// The buffer is not allocated or freed by this class.
class Buffer
{
unsigned char* buffer;
int scale;
int minv;
int maxv;
public:
Buffer(unsigned char* buffer, int scale);
~Buffer();
void Set_H_Line(int start_x, int end_x, int y);
void Fill(unsigned char c);
inline int Scale() const { return scale; }
} pixel_buffer;
Vector2* vertices;
void Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci);
public:
BW_Render(unsigned char* buffer, int scale);
~BW_Render();
void Fill(unsigned char c);
void Set_Vertex_Locations(Vector2* vertices,int count); // Warning! Contents are modified!
void Render_Triangles(const unsigned long* indices,int index_count);
void Render_Triangle_Strip(const unsigned long* indices,int index_count);
};
#endif // BW_RENDER_H__

View File

@@ -0,0 +1,242 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/bwrender.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 4/04/01 10:14a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bwrender.h"
#include "vp.h"
#include <string.h>
BWRenderClass::Buffer::Buffer(unsigned char* buffer_, int scale_)
:
buffer(buffer_),
scale(scale_),
minv(3),
maxv(scale_-3)
{
}
BWRenderClass::Buffer::~Buffer()
{
}
void BWRenderClass::Buffer::Set_H_Line(int start_x, int end_x, int y)
{
if (y<minv || y>=maxv || end_x<minv || start_x>=maxv) return;
if (start_x<minv) start_x=minv;
if (end_x>=maxv) end_x=maxv-1;
unsigned char* ptr=buffer+scale*y+start_x;
int w=end_x-start_x;
if (w) {
::memset(ptr,0x00,w);
/* // Blurring (test)
*(ptr-1)&=0x80;
*(ptr-2)&=0xc0;
*(ptr+w)&=0x80;
*(ptr+w+1)&=0xc0;
for (int a=0;a<w;++a) {
*(ptr-scale+a)&=0xc0;
*(ptr+scale+a)&=0xc0;
}
*/
}
}
void BWRenderClass::Buffer::Fill(unsigned char c)
{
memset(buffer,c,scale*scale);
}
// ------------------------------------------------------------------------------
BWRenderClass::BWRenderClass(unsigned char* buffer, int buffer_scale)
:
pixel_buffer(buffer,buffer_scale)
{
}
BWRenderClass::~BWRenderClass()
{
}
void BWRenderClass::Fill(unsigned char c)
{
pixel_buffer.Fill(c);
}
// Sets the vertex coordinate buffer location and scales the vertex locations to the kjkj
void BWRenderClass::Set_Vertex_Locations(Vector2* vertices_,int count)
{
vertices=vertices_;
float half_scale=pixel_buffer.Scale()*0.5f;
VectorProcessorClass::MulAdd(
reinterpret_cast<float*>(vertices),
half_scale,
half_scale,
count*2);
}
// --------------------------------------------------------------------
static inline bool Cull(
const Vector2& c1,
const Vector2& c2,
const Vector2& c3)
{
float x1=c2[0]-c1[0];
float y1=c2[1]-c1[1];
float x2=c3[0]-c1[0];
float y2=c3[1]-c1[1];
float r=x1*y2-x2*y1;
if (r<0) return false;
return true;
}
void BWRenderClass::Render_Triangle_Strip(const unsigned long* indices,int index_count)
{
index_count-=2;
bool b=false;
for (int n=0;n<index_count;++n) {
b=!b;
int idx_1=indices[0];
int idx_2=indices[1];
int idx_3=indices[2];
indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])==b) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BWRenderClass::Render_Triangles(const unsigned long* indices,int index_count)
{
index_count/=3;
for (int n=0;n<index_count;++n) {
int idx_1=*indices++;
int idx_2=*indices++;
int idx_3=*indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BWRenderClass::Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci)
{
float x_left=xcf[0];
float x_right=x_left;
int ycnt=yci[1]-yci[0];
int y=yci[0];
if (ycnt) {
float x_step_1=(xcf[1]-xcf[0])/float(ycnt);
float x_step_2=(xcf[2]-xcf[0])/float(yci[2]-y);
if (x_step_1>x_step_2) {
float t=x_step_1;
x_step_1=x_step_2;
x_step_2=t;
}
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
else {
if (xcf[0]<xcf[1]) {
x_left=xcf[0];
x_right=xcf[1];
}
else {
x_right=xcf[0];
x_left=xcf[1];
}
}
ycnt=yci[2]-yci[1];
y=yci[1];
if (ycnt) {
float one_per_ycnt=1.0f/float(ycnt);
float x_step_1=(xcf[2]-x_left)*one_per_ycnt;
float x_step_2=(xcf[2]-x_right)*one_per_ycnt;
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/bwrender.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 4/04/01 10:36a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BWRENDER_H
#define BWRENDER_H
#include "always.h"
#include "vector2.h"
#include "vector3.h"
#include "vector3i.h"
/**
** BWRenderClass
** This class implements a simple black-and-white triangle rasterizer which
** can be used to generate shadow textures. It is faster than a general purpose
** software renderer due to the fact that no z-buffering or sorting is needed and
** texturing isn't supported.
** (gth) 04/02/2001 - I'm going to add render-to-texture code to Renegade so this
** class may be obsolete.
*/
class BWRenderClass
{
// Internal pixel buffer used by the triangle renderer
// The buffer is not allocated or freed by this class.
class Buffer
{
unsigned char* buffer;
int scale;
int minv;
int maxv;
public:
Buffer(unsigned char* buffer, int scale);
~Buffer();
void Set_H_Line(int start_x, int end_x, int y);
void Fill(unsigned char c);
inline int Scale() const { return scale; }
} pixel_buffer;
Vector2* vertices;
void Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci);
public:
BWRenderClass(unsigned char* buffer, int scale);
~BWRenderClass();
void Fill(unsigned char c);
void Set_Vertex_Locations(Vector2* vertices,int count); // Warning! Contents are modified!
void Render_Triangles(const unsigned long* indices,int index_count);
void Render_Triangle_Strip(const unsigned long* indices,int index_count);
};
#endif //BWRENDER_H

View File

@@ -0,0 +1,830 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/camera.cpp $*
* *
* Org Author:: Greg_h *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 24 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CameraClass::CameraClass -- constructor *
* CameraClass::CameraClass -- copy constructor *
* CameraClass::operator == -- assignment operator *
* CameraClass::~CameraClass -- destructor *
* CameraClass::Clone -- virtual copy constructor *
* CameraClass::Add -- add the camera to the world? *
* CameraClass::Remove -- Remove the camera from the world? *
* CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere *
* CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box *
* CameraClass::Set_Transform -- set the transform of the camera *
* CameraClass::Set_Position -- Set the position of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_Animation -- Set the animation state of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_View_Plane -- control over the view plane *
* CameraClass::Set_View_Plane -- set the viewplane using fov angles *
* CameraClass::Set_Focal_Length -- set the view plane using focal length and aspect ratio *
* CameraClass::Get_View_Plane -- get the corners of the current view plane *
* CameraClass::Project -- project a point from ws to the view plane *
* CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera spac *
* CameraClass::Un_Project -- "unproject" a point from the view plane to world space *
* CameraClass::Transform_To_View_Space -- transforms the given world space point to camera *
* CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space *
* CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane *
* CameraCLass::Update_Frustum -- updates the frustum parameters *
* CameraClass::Cull_Box -- tests whether the given box can be culled *
* CameraClass::Device_To_View_Space -- converts the given device coordinate to view space *
* CameraClass::Device_To_World_Space -- converts given device coord to world space *
* CameraClass::Camera_Push -- pushes the camera's parameters into the given GERD *
* CameraClass::Camera_Pop -- pops the camera's parameters from the given GERD *
* CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera *
* CameraClass::Apply_D3D_State -- sets the D3D states controlled by the camera *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "camera.h"
#include "ww3d.h"
#include "matrix4.h"
#include "dx8wrapper.h"
/***********************************************************************************************
* CameraClass::CameraClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::CameraClass(void) :
Projection(PERSPECTIVE),
Viewport(Vector2(0,0),Vector2(1,1)), // pixel viewport to render into
AspectRatio(4.0f/3.0f),
ZNear(1.0f), // near clip plane distance
ZFar(1000.0f), // far clip plane distance
ZBufferMin(0.0f), // smallest value we'll write into the z-buffer
ZBufferMax(1.0f), // largest value we'll write into the z-buffer
FrustumValid(false)
{
Set_Transform(Matrix3D(1));
Set_View_Plane(DEG_TO_RADF(50.0f));
}
/***********************************************************************************************
* CameraClass::CameraClass -- copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
* 4/13/2001 hy : added in copy code for new member functions *
*=============================================================================================*/
CameraClass::CameraClass(const CameraClass & src) :
RenderObjClass(src),
Projection(src.Projection),
Viewport(src.Viewport),
ViewPlane(src.ViewPlane),
ZNear(src.ZNear),
ZFar(src.ZFar),
FrustumValid(src.FrustumValid),
Frustum(src.Frustum),
NearClipBBox(src.NearClipBBox),
ProjectionTransform(src.ProjectionTransform),
CameraInvTransform(src.CameraInvTransform),
AspectRatio(src.AspectRatio),
ZBufferMin(src.ZBufferMin),
ZBufferMax(src.ZBufferMax)
{
// just being paraniod in case any parent class doesn't completely copy the entire state...
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::operator == -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass & CameraClass::operator = (const CameraClass & that)
{
if (this != &that) {
RenderObjClass::operator = (that);
Projection = that.Projection;
Viewport = that.Viewport;
ViewPlane = that.ViewPlane;
ZNear = that.ZNear;
ZFar = that.ZFar;
FrustumValid = that.FrustumValid;
Frustum = that.Frustum;
NearClipBBox = that.NearClipBBox;
ProjectionTransform = that.ProjectionTransform;
CameraInvTransform = that.CameraInvTransform;
// just being paraniod in case any parent class doesn't completely copy the entire state...
FrustumValid = false;
}
return * this;
}
/***********************************************************************************************
* CameraClass::~CameraClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::~CameraClass(void)
{
}
/***********************************************************************************************
* CameraClass::Clone -- virtual copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
RenderObjClass * CameraClass::Clone(void) const
{
return NEW_REF( CameraClass, (*this) );
}
/***********************************************************************************************
* CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
sphere.Center.Set(0,0,0);
sphere.Radius = ZFar; // could optimize this but its not really used.
}
/***********************************************************************************************
* CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
box.Center.Set(0,0,0);
box.Extent.Set(ZFar,ZFar,ZFar); // could optimize this but its not really used.
}
/***********************************************************************************************
* CameraClass::Set_Transform -- set the transform of the camera *
* *
* This is over-ridden to invalidate the cached frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_Transform(const Matrix3D &m)
{
RenderObjClass::Set_Transform(m);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_Position -- Set the position of the camera *
* *
* This is overriden to invalidate the cached frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_Position(const Vector3 &v)
{
RenderObjClass::Set_Position(v);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_View_Plane -- control over the view plane *
* *
* INPUT: *
* min - x,y of the upper left corner of the view rectangle (dist is assumed to be 1.0) *
* max - x,y of the lower right corner of the view rectangle *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_View_Plane(const Vector2 & vmin,const Vector2 & vmax)
{
ViewPlane.Min = vmin;
ViewPlane.Max = vmax;
AspectRatio = (vmax.X - vmin.X) / (vmax.Y - vmin.Y);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_View_Plane -- set the viewplane using fov angles *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_View_Plane(float hfov,float vfov)
{
float width_half = tan(hfov/2.0);
float height_half = 0.0f;
if (vfov == -1) {
height_half = (1.0f / AspectRatio) * width_half; // use the aspect ratio
} else {
height_half = tan(vfov/2.0);
AspectRatio = width_half / height_half; // or, initialize the aspect ratio
}
ViewPlane.Min.Set(-width_half,-height_half);
ViewPlane.Max.Set(width_half,height_half);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/29/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Set_Aspect_Ratio(float width_to_height)
{
AspectRatio = width_to_height;
ViewPlane.Min.Y = ViewPlane.Min.X / AspectRatio;
ViewPlane.Max.Y = ViewPlane.Max.X / AspectRatio;
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Get_View_Plane -- get the corners of the current view plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_View_Plane(Vector2 & set_min,Vector2 & set_max) const
{
set_min = ViewPlane.Min;
set_max = ViewPlane.Max;
}
/***********************************************************************************************
* CameraClass::Project -- project a point from ws to the view plane *
* *
* INPUT: *
* dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 *
* ws_point - input point in world space *
* *
* OUTPUT: *
* ProjectionResType indicating whether the point was in the frustum *
* *
* WARNINGS: *
* When the input is behind the near clip plane, 0,0,0 is returned. *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::ProjectionResType CameraClass::Project(Vector3 & dest,const Vector3 & ws_point) const
{
Update_Frustum();
Vector3 cam_point;
Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&cam_point);
if (cam_point.Z > -ZNear) {
dest.Set(0,0,0);
return OUTSIDE_NEAR_CLIP;
}
Vector4 view_point = ProjectionTransform * cam_point;
float oow = 1.0f / view_point.W;
dest.X = view_point.X * oow;
dest.Y = view_point.Y * oow;
dest.Z = view_point.Z * oow;
if (dest.Z > 1.0f) {
return OUTSIDE_FAR_CLIP;
}
if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) {
return OUTSIDE_FRUSTUM;
}
return INSIDE_FRUSTUM;
}
/***********************************************************************************************
* CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera space *
* *
* INPUT: *
* dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 *
* cam_point - input point in camera space *
* *
* OUTPUT: *
* ProjectionResType indicating whether the point was in the frustum *
* *
* WARNINGS: *
* When the input is behind the near clip plane, 0,0,0 is returned. *
* *
* HISTORY: *
* 11/17/2000 gth : Created. *
*=============================================================================================*/
CameraClass::ProjectionResType
CameraClass::Project_Camera_Space_Point(Vector3 & dest,const Vector3 & cam_point) const
{
Update_Frustum();
if ( cam_point.Z > -ZNear + WWMATH_EPSILON) {
dest.Set(0,0,0);
return OUTSIDE_NEAR_CLIP;
}
Vector4 view_point = ProjectionTransform * cam_point;
float oow = 1.0f / view_point.W;
dest.X = view_point.X * oow;
dest.Y = view_point.Y * oow;
dest.Z = view_point.Z * oow;
if (dest.Z > 1.0f) {
return OUTSIDE_FAR_CLIP;
}
if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) {
return OUTSIDE_FRUSTUM;
}
return INSIDE_FRUSTUM;
}
/***********************************************************************************************
* CameraClass::Un_Project -- "unproject" a point from the view plane to world space *
* *
* The given point is assumed to be on the view_plane at z=-1. The 3D world space point that *
* this represents will be returned in dest. *
* *
* INPUT: *
* dest - will be filled in with the 3D world space point that the given point represents *
* view_point - point on the view_plane to be un-projected *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Un_Project(Vector3 & dest,const Vector2 & view_point) const
{
/*
** map view_point.X from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X
** map view_point.Y from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X
*/
float vpdx = ViewPlane.Max.X - ViewPlane.Min.X;
float vpdy = ViewPlane.Max.Y - ViewPlane.Min.Y;
Vector3 point;
point.X = ViewPlane.Min.X + vpdx * (view_point.X + 1.0f) * 0.5f;
point.Y = ViewPlane.Min.Y + vpdy * (view_point.Y + 1.0f) * 0.5f;
point.Z = -1.0f;
Matrix3D::Transform_Vector(Transform,point,&dest);
}
/***********************************************************************************************
* CameraClass::Transform_To_View_Space -- transforms the given world space point to camera sp *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/22/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Transform_To_View_Space(Vector3 & dest,const Vector3 & ws_point) const
{
Update_Frustum();
Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&dest);
}
/***********************************************************************************************
* CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/22/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Rotate_To_View_Space(Vector3 & dest,const Vector3 & ws_vector) const
{
Update_Frustum();
Matrix3D::Rotate_Vector(CameraInvTransform,ws_vector,&dest);
}
/***********************************************************************************************
* CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/25/99 GTH : Created. *
*=============================================================================================*/
const OBBoxClass & CameraClass::Get_Near_Clip_Bounding_Box(void) const
{
Update_Frustum();
return NearClipBBox;
}
/***********************************************************************************************
* CameraClass::Cull_Box -- tests whether the given box can be culled *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
bool CameraClass::Cull_Box(const AABoxClass & box) const
{
const FrustumClass & frustum = Get_Frustum();
return CollisionMath::Overlap_Test(frustum,box) == CollisionMath::OUTSIDE;
}
/***********************************************************************************************
* CameraClass::Update_Frustum -- updates the frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Update_Frustum(void) const
{
if (FrustumValid) return;
Vector2 vpmin,vpmax;
float znear,zfar;
float znear_dist,zfar_dist;
Matrix3D cam_mat = Get_Transform();
Get_View_Plane(vpmin, vpmax); // Normalized view plane at a depth of 1.0
Get_Clip_Planes(znear_dist, zfar_dist);
// Forward is negative Z in our viewspace coordinate system.
znear = -znear_dist;
zfar = -zfar_dist;
// Update the frustum
FrustumValid = true;
Frustum.Init(cam_mat,vpmin,vpmax,znear,zfar);
ViewSpaceFrustum.Init(Matrix3D(1),vpmin,vpmax,znear,zfar);
// Update the OBB around the near clip rectangle
#ifdef ALLOW_TEMPORARIES
NearClipBBox.Center = cam_mat * Vector3(0,0,znear);
#else
cam_mat.mulVector3(Vector3(0,0,znear), NearClipBBox.Center);
#endif
NearClipBBox.Extent.X = (vpmax.X - vpmin.X) * (-znear) * 0.5f; // (near_clip_x / |znear|) == (vpmin.X / 1.0f)...
NearClipBBox.Extent.Y = (vpmax.Y - vpmin.Y) * (-znear) * 0.5f;
NearClipBBox.Extent.Z = 0.01f;
NearClipBBox.Basis.Set(cam_mat);
// Update the inverse camera matrix
Transform.Get_Inverse(CameraInvTransform);
// Update the projection matrix
if (Projection == PERSPECTIVE) {
ProjectionTransform.Init_Perspective( vpmin.X*znear_dist, vpmax.X*znear_dist,
vpmin.Y*znear_dist, vpmax.Y*znear_dist,
znear_dist, zfar_dist );
} else {
ProjectionTransform.Init_Ortho( vpmin.X,vpmax.X,vpmin.Y,vpmax.Y,znear_dist,zfar_dist);
}
}
/***********************************************************************************************
* CameraClass::Device_To_View_Space -- converts the given device coordinate to view space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Device_To_View_Space(const Vector2 & device_coord,Vector3 * set_view)
{
int res_width;
int res_height;
int res_bits;
bool windowed;
WW3D::Get_Render_Target_Resolution(res_width,res_height,res_bits,windowed);
// convert the device coordinates into normalized device coordinates:
Vector2 ndev;
ndev.X = device_coord.X / (float)res_width;
ndev.Y = device_coord.Y / (float)res_height;
// view space rectangle which corresponds to the viewport
Vector2 vs_min;
Vector2 vs_max;
Get_View_Plane(vs_min,vs_max);
// mapping from the viewport coordinates to view space coordinates
set_view->X = vs_min.X + (ndev.X - Viewport.Min.X) * (vs_max.X - vs_min.X) / (Viewport.Width());
set_view->Y = vs_max.Y - (ndev.Y - Viewport.Min.Y) * (vs_max.Y - vs_min.Y) / (Viewport.Height());
set_view->Z = -1.0f;
}
/***********************************************************************************************
* CameraClass::Device_To_World_Space -- converts given device coord to world space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Device_To_World_Space(const Vector2 & device_coord,Vector3 * world_coord)
{
Vector3 vs;
Device_To_View_Space(device_coord,&vs);
Matrix3D::Transform_Vector(Transform,vs,world_coord);
}
/***********************************************************************************************
* CameraClass::Apply -- sets the D3D states controlled by the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/29/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Apply(void)
{
Update_Frustum();
int width,height,bits;
bool windowed;
WW3D::Get_Render_Target_Resolution(width,height,bits,windowed);
D3DVIEWPORT8 vp;
vp.X = (DWORD)(Viewport.Min.X * (float)width);
vp.Y = (DWORD)(Viewport.Min.Y * (float)height);
vp.Width = (DWORD)((Viewport.Max.X - Viewport.Min.X) * (float)width);
vp.Height = (DWORD)((Viewport.Max.Y - Viewport.Min.Y) * (float)height);
vp.MinZ = ZBufferMin;
vp.MaxZ = ZBufferMax;
DX8Wrapper::Set_Viewport(&vp);
Matrix4x4 d3dprojection;
Get_D3D_Projection_Matrix(&d3dprojection);
DX8Wrapper::Set_Projection_Transform_With_Z_Bias(d3dprojection,ZNear,ZFar);
DX8Wrapper::Set_Transform(D3DTS_VIEW,CameraInvTransform);
}
void CameraClass::Set_Clip_Planes(float znear,float zfar)
{
FrustumValid = false;
ZNear = znear;
ZFar = zfar;
}
void CameraClass::Get_Clip_Planes(float & znear,float & zfar) const
{
znear = ZNear;
zfar = ZFar;
}
float CameraClass::Get_Horizontal_FOV(void) const
{
float width = ViewPlane.Max.X - ViewPlane.Min.X;
return 2*WWMath::Atan2(width,2.0);
}
float CameraClass::Get_Vertical_FOV(void) const
{
float height = ViewPlane.Max.Y - ViewPlane.Min.Y;
return 2*WWMath::Atan2(height,2.0);
}
float CameraClass::Get_Aspect_Ratio(void) const
{
return AspectRatio;
}
void CameraClass::Get_Projection_Matrix(Matrix4x4 * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = ProjectionTransform;
}
void CameraClass::Get_D3D_Projection_Matrix(Matrix4x4 * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = ProjectionTransform;
/*
** We need to flip the handed-ness of the projection matrix and
** move the z-range to 0<z<1 rather than -1<z<1
*/
float oozdiff = 1.0 / (ZFar - ZNear);
if (Projection == PERSPECTIVE) {
(*set_tm)[2][2] = -(ZFar) * oozdiff;
(*set_tm)[2][3] = -(ZFar*ZNear) * oozdiff;
} else {
(*set_tm)[2][2] = -oozdiff;
(*set_tm)[2][3] = -ZNear * oozdiff;
}
}
void CameraClass::Get_View_Matrix(Matrix3D * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = CameraInvTransform;
}
const Matrix4x4 & CameraClass::Get_Projection_Matrix(void)
{
Update_Frustum();
return ProjectionTransform;
}
const Matrix3D & CameraClass::Get_View_Matrix(void)
{
Update_Frustum();
return CameraInvTransform;
}
void CameraClass::Convert_Old(Vector3 &pos)
{
pos.X=(pos.X+1)/2;
pos.Y=(pos.Y+1)/2;
}
float CameraClass::Compute_Projected_Sphere_Radius(float dist,float radius)
{
Vector4 result = ProjectionTransform * Vector4(radius,0.0f,dist,1.0f);
return result.X / result.W;
}

View File

@@ -0,0 +1,456 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/camera.h $*
* *
* Org Author:: Greg_h *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 14 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CameraClass::Get_Frustum -- returns the frustum of the camera *
* CameraClass::Get_Frustum_Planes -- returns pointer to the array of frustum planes *
* CameraClass::Get_Frustum_Corners -- returns pointer to the array of frustum corners *
* CameraClass::Get_View_Space_Frustum -- returns the view-space frustum for this camera *
* CameraClass::Get_View_Space_Frustum_Planes -- returns the view space clip planes for this *
* CameraClass::Get_View_Space_Frustum_Corners -- returns the corners of the view space frus *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CAMERA_H
#define CAMERA_H
#include "always.h"
#include "rendobj.h"
#include "plane.h"
#include "frustum.h"
#include "obbox.h"
#include "vector2.h"
#include "matrix4.h"
#include "colmath.h"
class RenderInfoClass;
/**
** ViewportClass
** This class is used to define a "normalized" screen space rectangle for the
** camera to render into. A viewport which filled the entire screen would be
** (0,0) - (1,1) with 0,0 being the upper left and 1,1 being the lower right.
*/
class ViewportClass
{
public:
ViewportClass(void) : Min(0,0), Max(1,1) { }
ViewportClass(const Vector2 & min,const Vector2 & max) : Min(min), Max(max) { }
ViewportClass(const ViewportClass & vp) : Min(vp.Min), Max(vp.Max) { }
float Width(void) const { return Max.X - Min.X; }
float Height(void) const { return Max.Y - Min.Y; }
Vector2 Min;
Vector2 Max;
};
/**
** CameraClass
** This object controls how vertices are transformed from world-space to view
** space, the parameters of the perspective projection, and the viewport
** on screen that the result is mapped into.
**
** Cameras are not "rendered" and do not need to be "added" to a scene. A
** CameraClass is passed into the WW3D::Render(...) function. The reason
** they are render objects is so that they can be inserted onto the bone of
** some animation and move with the animation...
**
** For all of the projection functions (Matrix4x4, ProjectorClass (used by
** decals and texture projections), and CameraClass) I followed the OpenGL
** convention of passing positive distances for your clip planes even though
** in a right-handed coordinate system your z values are negative after
** transformation to camera space. So Set_Clip_Planes expects positive distances
** to your near and far clip planes.
**
** (gth) We should probably separate CameraClass from RenderObjClass... In the
** case where a camera is using a transform from an animation; the programmer
** is usually requesting the transform and plugging it into the camera anyway.
*/
class CameraClass : public RenderObjClass
{
public:
enum ProjectionType
{
PERSPECTIVE = 0,
ORTHO
};
enum ProjectionResType
{
INSIDE_FRUSTUM,
OUTSIDE_FRUSTUM,
OUTSIDE_NEAR_CLIP,
OUTSIDE_FAR_CLIP,
};
CameraClass(void);
CameraClass(const CameraClass & src);
CameraClass & operator = (const CameraClass &);
virtual ~CameraClass(void);
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_CAMERA; }
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering, cameras don't "render"
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo) { }
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
// Cameras cache their frustum description, this is invalidated whenever
// the transform/position is changed
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Bounding Volumes
/////////////////////////////////////////////////////////////////////////////
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
///////////////////////////////////////////////////////////////////////////
// Camera parameter control
///////////////////////////////////////////////////////////////////////////
// Depth of the scene.
float Get_Depth(void) const;
// Setting the projection type
void Set_Projection_Type(ProjectionType ptype);
ProjectionType Get_Projection_Type(void);
// Setting the clipping ranges in world space distances
void Set_Clip_Planes(float znear,float zfar);
void Get_Clip_Planes(float & znear,float & zfar) const;
// Setting the zbuffer range used during rendering. (Added to allow subdividing the z-buffer. -MW).
void Set_Zbuffer_Range(float znear,float zfar) {ZBufferMin = znear;ZBufferMax=zfar;}
void Get_Zbuffer_Range(float & znear,float & zfar) const {znear=ZBufferMin;zfar=ZBufferMax;}
// Methods for setting the View Plane.
// NOTE: View plane is always at a distance of 1.0 from the eye.
void Set_View_Plane(const Vector2 & min,const Vector2 & max);
void Set_View_Plane(float hfov,float vfov = -1);
void Set_Aspect_Ratio(float width_to_height);
// Methods for querying the View Plane settings.
void Get_View_Plane(Vector2 & set_min,Vector2 & set_max) const;
float Get_Horizontal_FOV(void) const;
float Get_Vertical_FOV(void) const;
float Get_Aspect_Ratio(void) const;
// Access to the projection matrices for this camera
void Get_Projection_Matrix(Matrix4x4 * set_tm);
void Get_D3D_Projection_Matrix(Matrix4x4 * set_tm);
void Get_View_Matrix(Matrix3D * set_tm);
const Matrix4x4 & Get_Projection_Matrix(void);
const Matrix3D & Get_View_Matrix(void);
// Projecting and Un-Projecting a point
ProjectionResType Project(Vector3 & dest,const Vector3 & ws_point) const;
ProjectionResType Project_Camera_Space_Point(Vector3 & dest,const Vector3 & cam_point) const;
void Un_Project(Vector3 & dest,const Vector2 & view_point) const;
void Transform_To_View_Space(Vector3 & dest,const Vector3 & ws_point) const;
void Rotate_To_View_Space(Vector3 & dest,const Vector3 & ws_vector) const;
// Viewport control
void Set_Viewport(const Vector2 & min,const Vector2 & max);
void Get_Viewport(Vector2 & set_min,Vector2 & set_max) const;
const ViewportClass & Get_Viewport(void) const;
void Set_Depth_Range(float zstart = 0.0f,float zend = 1.0f);
void Get_Depth_Range(float * set_zstart,float * set_zend) const;
// Culling for various bounding volumes. These functions will return true if the
// given primitive is culled (i.e. it is *outside* the view frustum)
bool Cull_Sphere(const SphereClass & sphere) const;
bool Cull_Sphere_On_Frustum_Sides(const SphereClass & sphere) const;
bool Cull_Box(const AABoxClass & box) const;
// Various properties of the camera's frustum: These funcitons return a
// pointer to the internal storage of the descriptions. there will be
// 6 frustum planes, 8 corner points, see the implementations of these
// functions for definitions on which points/planes are associated with
// each index. Better yet, just use the Frustum object.
const FrustumClass & Get_Frustum(void) const;
const PlaneClass * Get_Frustum_Planes(void) const;
const Vector3 * Get_Frustum_Corners(void) const;
const FrustumClass & Get_View_Space_Frustum(void) const;
const PlaneClass * Get_View_Space_Frustum_Planes(void) const;
const Vector3 * Get_View_Space_Frustum_Corners(void) const;
const OBBoxClass & Get_Near_Clip_Bounding_Box(void) const;
// Methods for transforming/projecting points between various coordinate systems
// associated with this camera.
// "Device Space" - pixel coordinate
// "View Space" - 3D space where the view point is at 0,0,0 and the view plane is at z=-1.0
// "World Space" - 3D world coordinate system.
void Device_To_View_Space(const Vector2 & device_coord,Vector3 * view_coord);
void Device_To_World_Space(const Vector2 & device_coord,Vector3 * world_coord);
float Compute_Projected_Sphere_Radius(float dist,float radius);
// apply this camera's settings into d3d.
void Apply(void);
// utility class to convert to old space of 0..1
static void Convert_Old(Vector3 &pos);
protected:
void Update_Frustum(void) const;
ProjectionType Projection; // projection type, orthographic or perspective
ViewportClass Viewport; // pixel viewport to render into
ViewportClass ViewPlane; // corners of a slice through the frustum at z=-1.0
float AspectRatio; // aspect ratio of the camera, width / height
float ZNear; // near clip plane distance
float ZFar; // far clip plane distance
float ZBufferMin; // smallest value we'll write into the z-buffer (usually 0.0)
float ZBufferMax; // largest value we'll write into the z-buffer (usually 1.0)
mutable bool FrustumValid;
mutable FrustumClass Frustum; // world-space frustum and clip planes
mutable FrustumClass ViewSpaceFrustum; // view-space frustum and clip planes
mutable OBBoxClass NearClipBBox; // obbox which bounds the near clip plane
mutable Matrix4x4 ProjectionTransform;
mutable Matrix3D CameraInvTransform;
};
inline float CameraClass::Get_Depth(void) const
{
return ZFar;
}
inline void CameraClass::Set_Projection_Type(ProjectionType ptype)
{
FrustumValid = false;
Projection = ptype;
}
inline CameraClass::ProjectionType CameraClass::Get_Projection_Type(void)
{
return Projection;
}
inline void CameraClass::Set_Viewport(const Vector2 & min,const Vector2 & max)
{
Viewport.Min = min; Viewport.Max = max;
FrustumValid = false;
}
inline void CameraClass::Get_Viewport(Vector2 & set_min,Vector2 & set_max) const
{
set_min = Viewport.Min;
set_max = Viewport.Max;
}
inline void CameraClass::Set_Depth_Range(float zmin,float zmax)
{
ZBufferMin = zmin;
ZBufferMax = zmax;
}
inline void CameraClass::Get_Depth_Range(float * set_zmin,float * set_zmax) const
{
if (set_zmin != NULL) {
*set_zmin = ZBufferMin;
}
if (set_zmax != NULL) {
*set_zmax = ZBufferMax;
}
}
inline const ViewportClass & CameraClass::Get_Viewport(void) const
{
return Viewport;
}
inline bool CameraClass::Cull_Sphere(const SphereClass & sphere) const
{
const FrustumClass & frustum = Get_Frustum();
return CollisionMath::Overlap_Test(frustum,sphere) == CollisionMath::OUTSIDE;
}
inline bool CameraClass::Cull_Sphere_On_Frustum_Sides(const SphereClass & sphere) const
{
const FrustumClass & frustum = Get_Frustum();
const PlaneClass * planes = frustum.Planes;
bool is_visible = true;
for (int i = 1; i < 5; i++) {
is_visible = is_visible && (CollisionMath::Overlap_Test(planes[i],sphere) & (CollisionMath::INSIDE|CollisionMath::BOTH));
}
return !is_visible;
}
/***********************************************************************************************
* CameraClass::Get_Frustum -- returns the frustum of the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/99 GTH : Created. *
*=============================================================================================*/
inline const FrustumClass &
CameraClass::Get_Frustum(void) const
{
Update_Frustum();
return Frustum;
}
/***********************************************************************************************
* CameraClass::Get_Frustum_Planes -- returns pointer to the array of frustum planes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
inline const PlaneClass *
CameraClass::Get_Frustum_Planes(void) const
{
const FrustumClass & frustum = Get_Frustum();
return frustum.Planes;
}
/***********************************************************************************************
* CameraClass::Get_Frustum_Corners -- returns pointer to the array of frustum corners *
* *
* The camera frustum corner FrustumCorners are defined in the following order *
* The first four points lie on the near clipping plane: *
* upper left 0, upper right 1, lower left 2, lower right 3. *
* The last four points lie on the far clipping plane, numbered analogous fashion. *
* upper left 4, upper right 5, lower left 6, lower right 7. *
* (remember: the camera space has x going to the right, y up and z backwards). *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
inline const Vector3 *
CameraClass::Get_Frustum_Corners(void) const
{
const FrustumClass & frustum = Get_Frustum();
return frustum.Corners;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum -- returns the view-space frustum for this camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const FrustumClass & CameraClass::Get_View_Space_Frustum(void) const
{
Update_Frustum();
return ViewSpaceFrustum;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum_Planes -- returns the view space clip planes for this c *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const PlaneClass * CameraClass::Get_View_Space_Frustum_Planes(void) const
{
const FrustumClass & frustum = Get_View_Space_Frustum();
return frustum.Planes;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum_Corners -- returns the corners of the view space frustu *
* *
* The camera frustum corner FrustumCorners are defined in the following order *
* The first four points lie on the near clipping plane: *
* upper left 0, upper right 1, lower left 2, lower right 3. *
* The last four points lie on the far clipping plane, numbered analogous fashion. *
* upper left 4, upper right 5, lower left 6, lower right 7. *
* (remember: camera space has x going to the right, y up and z backwards). *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const Vector3 * CameraClass::Get_View_Space_Frustum_Corners(void) const
{
const FrustumClass & frustum = Get_View_Space_Frustum();
return frustum.Corners;
}
#endif

View File

@@ -0,0 +1,65 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/classid.h $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 3/29/01 1:13a $*
* *
* $Revision:: 2 $*
* *
*-------------------------------------------------------------------------*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CLASSID_H
#define CLASSID_H
#include "always.h"
/*
** enum of all the WW3D class IDs.
*/
enum
{
ID_INDIRECT_TEXTURE_CLASS = 0x10000, // IndirectTextureClass "texture.h"
ID_VARIABLE_TEXTURE_CLASS, // VariableTextureClass "texture.h"
ID_FILE_LIST_TEXTURE_CLASS, // FileListTextureClass "texture.h"
ID_RESIZEABLE_TEXTURE_INSTANCE_CLASS, // ResizeableTextureInstanceClass "texture.h"
ID_ANIM_TEXTURE_INSTANCE_CLASS, // AnimTextureInstanceClass "texture.h"
ID_MANUAL_ANIM_TEXTURE_INSTANCE_CLASS, // ManualAnimTextureInstanceClass "texture.h"
ID_TIME_ANIM_TEXTURE_INSTANCE_CLASS, // TimeAnimTextureInstanceClass "texture.h"
ID_POINT_GROUP_CLASS, // PointGroupClass "pointgr.h"
ID_MESH_MODEL_CLASS, // MeshModelClass "mesh.cpp"
ID_CACHED_TEXTURE_FILE_CLASS, // CachedTextureFileClass "assetmgr.cpp"
ID_STREAMING_TEXTURE_CLASS, // StreamingTextureClass "texture.h"
ID_STREAMING_TEXTURE_INSTANCE_CLASS, // StreamingTextureInstanceClass "texture.h"
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/collect.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLLECT_H
#define COLLECT_H
#include "rendobj.h"
#include "composite.h"
#include "vector.h"
#include "proto.h"
#include "w3d_file.h"
#include "wwstring.h"
#include "proxy.h"
class CollectionDefClass;
class SnapPointsClass;
/*
** CollectionClass
** This is a render object which contains a collection of render objects.
*/
class CollectionClass : public CompositeRenderObjClass
{
public:
CollectionClass(void);
CollectionClass(const CollectionDefClass & def);
CollectionClass(const CollectionClass & src);
CollectionClass & CollectionClass::operator = (const CollectionClass &);
virtual ~CollectionClass(void);
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual int Get_Num_Polys(void) const;
/////////////////////////////////////////////////////////////////////////////
// Proxy interface
/////////////////////////////////////////////////////////////////////////////
virtual int Get_Proxy_Count (void) const;
virtual bool Get_Proxy (int index, ProxyClass &proxy) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual int Get_Num_Sub_Objects(void) const;
virtual RenderObjClass * Get_Sub_Object(int index) const;
virtual int Add_Sub_Object(RenderObjClass * subobj);
virtual int Remove_Sub_Object(RenderObjClass * robj);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Collision Detection, Ray Tracing
/////////////////////////////////////////////////////////////////////////////
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Bounding Volumes
/////////////////////////////////////////////////////////////////////////////
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Attributes, Options, Properties, etc
/////////////////////////////////////////////////////////////////////////////
virtual int Snap_Point_Count(void);
virtual void Get_Snap_Point(int index,Vector3 * set);
virtual void Scale(float scale);
virtual void Scale(float scalex, float scaley, float scalez);
virtual void Update_Obj_Space_Bounding_Volumes(void);
protected:
void Free(void);
void Update_Sub_Object_Transforms(void);
DynamicVectorClass <ProxyClass> ProxyList;
DynamicVectorClass <RenderObjClass *> SubObjects;
SnapPointsClass * SnapPoints;
SphereClass BoundSphere;
AABoxClass BoundBox;
};
/*
** CollectionLoaderClass
** Loader for collection objects
*/
class CollectionLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type(void) { return W3D_CHUNK_COLLECTION; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
extern CollectionLoaderClass _CollectionLoader;
#endif

View File

@@ -0,0 +1,161 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Colorspace *
* *
* $Archive:: $*
* *
* Original Author:: Hector Yee *
* *
* $Author:: $*
* *
* $Modtime:: $*
* *
* $Revision:: $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLORSPACE_H
#define COLORSPACE_H
#include "dx8wrapper.h"
#include <wwmath.h>
void RGB_To_HSV(Vector3 &hsv,const Vector3 &rgb);
void HSV_To_RGB(Vector3 &rgb, const Vector3 &hsv);
void Recolor(Vector3 &rgb, const Vector3 &hsv_shift);
//---------------------------------------------------------------------
// Color Conversions
//---------------------------------------------------------------------
inline void RGB_To_HSV(Vector3 &hsv,const Vector3 &rgb)
// modified from Foley et al. page 592
// converts rgb[0..1] to h [0,360), s and v in [0,1]
// negative h values are to signify undefined
{
float max=WWMath::Max(rgb.X,rgb.Y);
max=WWMath::Max(max,rgb.Z);
float min=WWMath::Min(rgb.X,rgb.Y);
min=WWMath::Min(min,rgb.Z);
// value
hsv.Z=max;
// saturation
hsv.Y=(max!=0.0f)?((max-min)/max):0.0f;
if (hsv.Y==0.0f) hsv.X=-1.0f;
else
{
float delta=max-min;
if (rgb.X==max)
hsv.X=(rgb.Y-rgb.Z)/delta;
else if (rgb.Y==max)
hsv.X=2.0f+ (rgb.Z-rgb.X)/delta;
else if (rgb.Z==max)
hsv.X=4.0f+ (rgb.X-rgb.Y)/delta;
hsv.X*=60.0f;
if (hsv.X<0.0f) hsv.X+=360.0f;
}
}
inline void HSV_To_RGB(Vector3 &rgb, const Vector3 &hsv)
{
float h=hsv.X;
float s=hsv.Y;
float v=hsv.Z;
if (hsv.Y==0.0f) {
rgb.Set(v,v,v);
} else {
float f,p,q,t;
int i;
if (h==360.0f) h=0.0f;
h/=60.0f;
i=WWMath::Floor(h);
f=h-i;
p=v*(1.0f-s);
q=v*(1.0f-(s*f));
t=v*(1.0f-(s*(1.0f-f)));
switch (i) {
case 0:
rgb.Set(v,t,p);
break;
case 1:
rgb.Set(q,v,p);
break;
case 2:
rgb.Set(p,v,t);
break;
case 3:
rgb.Set(p,q,v);
break;
case 4:
rgb.Set(t,p,v);
break;
case 5:
rgb.Set(v,p,q);
break;
}
}
}
inline void Recolor(Vector3 &rgb, const Vector3 &hsv_shift)
{
Vector3 hsv;
RGB_To_HSV(hsv,rgb);
// If the Hue has the "undefined flag" (a negative value), this means that the color is pure
// monochrome. In this case do not shift the hue (it is undefined) or the saturation (it is 0
// so it cannot be decreased, and increasing it would cause the undefined hue to actually
// become visible). In this case, we only modify the value.
if (hsv.X<0.0f) hsv+=Vector3(0.0f,0.0f,hsv_shift.Z);
else hsv+=hsv_shift;
// angular mod
if (hsv.X<0.0f) hsv.X+=360.0f;
if (hsv.X>360.0f) hsv.X-=360.0f;
// clamp saturation and value
hsv.Y=WWMath::Clamp(hsv.Y,0.0f,1.0f);
hsv.Z=WWMath::Clamp(hsv.Z,0.0f,1.0f);
HSV_To_RGB(rgb,hsv);
}
inline void Recolor(unsigned& rgba, const Vector3 &hsv_shift)
{
Vector4 rgba_v = DX8Wrapper::Convert_Color(rgba);
Recolor((Vector3&)rgba_v, hsv_shift);
rgba = DX8Wrapper::Convert_Color(rgba_v);
}
#endif

View File

@@ -0,0 +1,375 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/coltest.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/07/01 10:26a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "coltest.h"
AABoxCollisionTestClass::AABoxCollisionTestClass(const AABoxCollisionTestClass & that) :
CollisionTestClass(that),
Box(that.Box),
Move(that.Move),
SweepMin(that.SweepMin),
SweepMax(that.SweepMax)
{
}
AABoxCollisionTestClass::AABoxCollisionTestClass(const AABoxClass & aabox,const Vector3 & move,CastResultStruct * res,int collision_type) :
CollisionTestClass(res,collision_type),
Box(aabox),
Move(move)
{
SweepMin = Box.Center - Box.Extent;
SweepMax = Box.Center + Box.Extent;
Vector3 endmin = Box.Center + move - Box.Extent;
Vector3 endmax = Box.Center + move + Box.Extent;
if (endmax.X > SweepMax.X) SweepMax.X = endmax.X;
if (endmax.Y > SweepMax.Y) SweepMax.Y = endmax.Y;
if (endmax.Z > SweepMax.Z) SweepMax.Z = endmax.Z;
if (endmin.X < SweepMin.X) SweepMin.X = endmin.X;
if (endmin.Y < SweepMin.Y) SweepMin.Y = endmin.Y;
if (endmin.Z < SweepMin.Z) SweepMin.Z = endmin.Z;
}
bool AABoxCollisionTestClass::Cull(const AABoxClass & box)
{
// const float MOVE_THRESHOLD = 2.0f;
// if (WWMath::Fabs(Move.X) + WWMath::Fabs(Move.Y) + WWMath::Fabs(Move.Z) > MOVE_THRESHOLD) {
// CastResultStruct res;
// return !Box.Cast_To_Box(Move,box,&res);
// } else {
Vector3 min_corner;
Vector3 max_corner;
Vector3::Subtract(box.Center,box.Extent,&min_corner);
Vector3::Add(box.Center,box.Extent,&max_corner);
if ((SweepMin.X > max_corner.X) || (SweepMax.X < min_corner.X)) {
return true;
}
if ((SweepMin.Y > max_corner.Y) || (SweepMax.Y < min_corner.Y)) {
return true;
}
if ((SweepMin.Z > max_corner.Z) || (SweepMax.Z < min_corner.Z)) {
return true;
}
return false;
// }
}
void AABoxCollisionTestClass::Rotate(ROTATION_TYPE rotation)
{
#ifndef NDEBUG
int i;
Matrix3D tm(1);
switch(rotation) {
case ROTATE_NONE:
break;
case ROTATE_Z90:
tm = Matrix3D::RotateZ90;
break;
case ROTATE_Z180:
tm = Matrix3D::RotateZ180;
break;
case ROTATE_Z270:
tm = Matrix3D::RotateZ270;
break;
}
#ifdef ALLOW_TEMPORARIES
Vector3 realcenter = tm * Box.Center;
#else
Vector3 realcenter;
tm.mulVector3(Box.Center, realcenter);
#endif
Vector3 pts[8];
Vector3 & min = SweepMin;
Vector3 & max = SweepMax;
pts[0].Set(min.X,min.Y,min.Z);
pts[1].Set(min.X,max.Y,min.Z);
pts[2].Set(max.X,max.Y,min.Z);
pts[3].Set(max.X,min.Y,min.Z);
pts[4].Set(min.X,min.Y,max.Z);
pts[5].Set(min.X,max.Y,max.Z);
pts[6].Set(max.X,max.Y,max.Z);
pts[7].Set(max.X,min.Y,max.Z);
// for (i=0; i<8; i++) {
// pts[i] = tm * pts[i];
// }
tm.mulVector3Array(pts, 8);
Vector3 realmin = pts[0];
Vector3 realmax = pts[0];
for (i=1; i<8; i++) {
if (realmin.X >= pts[i].X) realmin.X = pts[i].X;
if (realmin.Y >= pts[i].Y) realmin.Y = pts[i].Y;
if (realmin.Z >= pts[i].Z) realmin.Z = pts[i].Z;
if (realmax.X <= pts[i].X) realmax.X = pts[i].X;
if (realmax.Y <= pts[i].Y) realmax.Y = pts[i].Y;
if (realmax.Z <= pts[i].Z) realmax.Z = pts[i].Z;
}
#endif
// rotate the test by the desired rotation about the Z axis, special cased for
// 90 degree rotations about Z. arbitrary rotations cause the axis aligned
// box to not be aligned any more :-)
float tmp,minx,miny,maxx,maxy;
switch(rotation) {
case ROTATE_NONE:
break;
case ROTATE_Z90:
// rotate the center point and the move vector
tmp = Box.Center.X; Box.Center.X = -Box.Center.Y; Box.Center.Y = tmp;
tmp = Move.X; Move.X = -Move.Y; Move.Y = tmp;
// swap x and y for the extent
tmp = Box.Extent.X; Box.Extent.X = Box.Extent.Y; Box.Extent.Y = tmp;
// update sweep bounding box
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = -maxy;
SweepMin.Y = minx;
SweepMax.X = -miny;
SweepMax.Y = maxx;
break;
case ROTATE_Z180:
// rotate center and move vector
Box.Center.X = -Box.Center.X;
Box.Center.Y = -Box.Center.Y;
Move.X = -Move.X;
Move.Y = -Move.Y;
// update min/max boxes
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = -maxx;
SweepMin.Y = -maxy;
SweepMax.X = -minx;
SweepMax.Y = -miny;
break;
case ROTATE_Z270:
// rotate center and move.
tmp = Box.Center.X; Box.Center.X = Box.Center.Y; Box.Center.Y = -tmp;
tmp = Move.X; Move.X = Move.Y; Move.Y = -tmp;
// update extent (x and y axis swap)
tmp = Box.Extent.X; Box.Extent.X = Box.Extent.Y; Box.Extent.Y = tmp;
// update min/max boxes
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = miny;
SweepMin.Y = -maxx;
SweepMax.X = maxy;
SweepMax.Y = -minx;
break;
}
#ifndef NDEBUG
assert((Box.Center - realcenter).Length() < 0.001f);
assert((SweepMin - realmin).Length() < 0.001f);
assert((SweepMax - realmax).Length() < 0.001f);
#endif
}
void AABoxCollisionTestClass::Transform(const Matrix3D & tm)
{
// NOTE: this function will expand the box to enclose the rotated
// form of the original box. In practice, this function was only
// implemented to double-check the results of the Translate and Rotate
// functions.
int i;
Vector3 tmpcenter = Box.Center;
Vector3 tmpextent = Box.Extent;
tm.Transform_Center_Extent_AABox(tmpcenter,tmpextent,&Box.Center,&Box.Extent);
Move = tm.Rotate_Vector(Move);
Vector3 pts[8];
Vector3 & min = SweepMin;
Vector3 & max = SweepMax;
pts[0].Set(min.X,min.Y,min.Z);
pts[1].Set(min.X,max.Y,min.Z);
pts[2].Set(max.X,max.Y,min.Z);
pts[3].Set(max.X,min.Y,min.Z);
pts[4].Set(min.X,min.Y,max.Z);
pts[5].Set(min.X,max.Y,max.Z);
pts[6].Set(max.X,max.Y,max.Z);
pts[7].Set(max.X,min.Y,max.Z);
// for (i=0; i<8; i++) {
// pts[i] = tm * pts[i];
// }
tm.mulVector3Array(pts, 8);
Vector3 realmin = pts[0];
Vector3 realmax = pts[0];
for (i=1; i<8; i++) {
if (realmin.X >= pts[i].X) realmin.X = pts[i].X;
if (realmin.Y >= pts[i].Y) realmin.Y = pts[i].Y;
if (realmin.Z >= pts[i].Z) realmin.Z = pts[i].Z;
if (realmax.X <= pts[i].X) realmax.X = pts[i].X;
if (realmax.Y <= pts[i].Y) realmax.Y = pts[i].Y;
if (realmax.Z <= pts[i].Z) realmax.Z = pts[i].Z;
}
SweepMin = realmin;
SweepMax = realmax;
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const OBBoxClass & obbox,
const Vector3 & move,
CastResultStruct * res,
int type
) :
CollisionTestClass(res,type),
Box(obbox),
Move(move)
{
Vector3 max_extent;
max_extent.X = WWMath::Fabs(Box.Basis[0][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[0][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[0][2] * Box.Extent.Z) + 0.01f;
max_extent.Y = WWMath::Fabs(Box.Basis[1][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[1][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[1][2] * Box.Extent.Z) + 0.01f;
max_extent.Z = WWMath::Fabs(Box.Basis[2][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[2][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[2][2] * Box.Extent.Z) + 0.01f;
SweepMin = Box.Center - max_extent;
SweepMax = Box.Center + max_extent;
Vector3 endmin = Box.Center + move - max_extent;
Vector3 endmax = Box.Center + move + max_extent;
if (endmax.X > SweepMax.X) SweepMax.X = endmax.X;
if (endmax.Y > SweepMax.Y) SweepMax.Y = endmax.Y;
if (endmax.Z > SweepMax.Z) SweepMax.Z = endmax.Z;
if (endmin.X < SweepMin.X) SweepMin.X = endmin.X;
if (endmin.Y < SweepMin.Y) SweepMin.Y = endmin.Y;
if (endmin.Z < SweepMin.Z) SweepMin.Z = endmin.Z;
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass(const OBBoxCollisionTestClass & that) :
CollisionTestClass(that),
Box(that.Box),
Move(that.Move),
SweepMin(that.SweepMin),
SweepMax(that.SweepMax)
{
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const OBBoxCollisionTestClass & that,
const Matrix3D & tm
) :
CollisionTestClass(that)
{
tm.Transform_Min_Max_AABox(that.SweepMin,that.SweepMax,&SweepMin,&SweepMax);
Matrix3D::Rotate_Vector(tm,that.Move,&Move);
OBBoxClass::Transform(tm,that.Box,&Box);
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const AABoxCollisionTestClass & that,
const Matrix3D & tm
) :
CollisionTestClass(that)
{
tm.Transform_Min_Max_AABox(that.SweepMin,that.SweepMax,&SweepMin,&SweepMax);
Matrix3D::Rotate_Vector(tm,that.Move,&Move);
Matrix3D::Transform_Vector(tm,that.Box.Center,&(Box.Center));
Box.Extent = that.Box.Extent;
Box.Basis = tm; // copies the 3x3 rotation portion of the transform
}
bool OBBoxCollisionTestClass::Cull(const AABoxClass & box)
{
Vector3 min_corner;
Vector3 max_corner;
Vector3::Subtract(box.Center,box.Extent,&min_corner);
Vector3::Add(box.Center,box.Extent,&max_corner);
if ((SweepMin.X > max_corner.X) || (SweepMax.X < min_corner.X)) {
return true;
}
if ((SweepMin.Y > max_corner.Y) || (SweepMax.Y < min_corner.Y)) {
return true;
}
if ((SweepMin.Z > max_corner.Z) || (SweepMax.Z < min_corner.Z)) {
return true;
}
return false;
}

View File

@@ -0,0 +1,317 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /VSS_Sync/ww3d2/coltest.h $*
* *
* Org Author:: Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 07/01/02 12:45p $*
* *
* $Revision:: 5 $*
* *
* 07/01/02 KM Coltype enum change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLTEST_H
#define COLTEST_H
#include "always.h"
#include "castres.h"
#include "lineseg.h"
#include "aabox.h"
#include "obbox.h"
#include "tri.h"
#include "colmath.h"
#include "coltype.h"
class RenderObjClass;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CollisionTestClass
//
// Each type of collision test will have an associated class which
// ties together all of the information necessary for the test.
// These classes also provide a perfect place to add any information
// which can be pre-calculated.
//
// The base class: CollisionTestClass simply contains a pointer to
// the user's CastResultStruct which will contain the results of
// the collision test. I store a pointer to a result structure
// because in some cases, new CollisionTestClasses are created in
// the process of checking (e.g. if the test needs to be transformed
// into another coordinate system) and I did not want to be
// constantly copying the result structure around. So, the user
// must allocate a result structure (usually on the stack) and keep it
// until the CollisionTestClass is discarded.
//
// Every CollisionTestClass should have the following functions:
//
// bool Cull(const Vector3 & min,const Vector3 & max);
// bool Cull(const AABoxClass & box);
// bool Cast_To_Triangle(const TriClass & tri);
//
// These are not virtual because I don't want to pay the price of virtual function
// calls at the point in the code where these are used. It may be possible to
// write template functions if we use these exact function prototpyes for all
// collision test classes though.
//
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CollisionTestClass
{
public:
CollisionTestClass(CastResultStruct * res,int collision_type);
CollisionTestClass(const CollisionTestClass & that);
public:
CastResultStruct * Result;
int CollisionType;
RenderObjClass * CollidedRenderObj;
};
inline CollisionTestClass::CollisionTestClass(CastResultStruct * res,int collision_type) :
Result(res),
CollisionType(collision_type),
CollidedRenderObj(NULL)
{
}
inline CollisionTestClass::CollisionTestClass(const CollisionTestClass & that) :
Result(that.Result),
CollisionType(that.CollisionType),
CollidedRenderObj(that.CollidedRenderObj)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RayCollisionTestClass
//
// Contains a linesegment to be tested and of course the inherited
// pointer to a CollisionStruct for the result
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
class RayCollisionTestClass : public CollisionTestClass
{
public:
RayCollisionTestClass(const LineSegClass & ray,CastResultStruct * res,int collision_type = COLL_TYPE_0,bool check_translucent=false, bool check_hidden=false);
RayCollisionTestClass(const RayCollisionTestClass & raytest,const Matrix3D & tm);
bool Cull(const Vector3 & min,const Vector3 & max);
bool Cull(const AABoxClass & box);
bool Cast_To_Triangle(const TriClass & tri);
public:
LineSegClass Ray;
bool CheckTranslucent;
bool CheckHidden;
private:
// not implemented
RayCollisionTestClass(const RayCollisionTestClass &);
RayCollisionTestClass & operator = (const RayCollisionTestClass &);
};
inline RayCollisionTestClass::RayCollisionTestClass(const LineSegClass & ray,CastResultStruct * res,int collision_type,bool check_translucent, bool check_hidden) :
CollisionTestClass(res,collision_type),
Ray(ray),
CheckTranslucent(check_translucent),
CheckHidden(check_hidden)
{
}
inline RayCollisionTestClass::RayCollisionTestClass(const RayCollisionTestClass & raytest,const Matrix3D & tm) :
CollisionTestClass(raytest),
Ray(raytest.Ray,tm),
CheckTranslucent(raytest.CheckTranslucent),
CheckHidden(raytest.CheckHidden)
{
}
inline bool RayCollisionTestClass::Cull(const Vector3 & min,const Vector3 & max)
{
return (CollisionMath::Overlap_Test(min,max,Ray) == CollisionMath::POS);
}
inline bool RayCollisionTestClass::Cull(const AABoxClass & box)
{
return (CollisionMath::Overlap_Test(box,Ray) == CollisionMath::POS);
}
inline bool RayCollisionTestClass::Cast_To_Triangle(const TriClass & tri)
{
return CollisionMath::Collide(Ray,tri,Result);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AABoxCollisionTestClass
//
// Contains an Axis Aligned Box and a movement vector to be tested
// for collision. Also adds Min and Max vectors both for the initial
// box and for the volume swept out by the box.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
class AABoxCollisionTestClass : public CollisionTestClass
{
public:
AABoxCollisionTestClass(const AABoxClass & aabox,const Vector3 & move,CastResultStruct * res,int collision_type = COLL_TYPE_0);
AABoxCollisionTestClass(const AABoxCollisionTestClass & that);
enum ROTATION_TYPE
{
ROTATE_NONE = 0,
ROTATE_Z90,
ROTATE_Z180,
ROTATE_Z270
};
bool Cull(const Vector3 & min,const Vector3 & max);
bool Cull(const AABoxClass & box);
bool Cast_To_Triangle(const TriClass & tri);
void Translate(const Vector3 & translation);
void Rotate(ROTATION_TYPE rotation);
void Transform(const Matrix3D & tm);
public:
AABoxClass Box;
Vector3 Move;
Vector3 SweepMin;
Vector3 SweepMax;
private:
// not implemented
AABoxCollisionTestClass & operator = (const AABoxCollisionTestClass &);
};
inline void AABoxCollisionTestClass::Translate(const Vector3 & translation)
{
// translate the test by the desired translation vector
Box.Center += translation;
SweepMin += translation;
SweepMax += translation;
}
inline bool AABoxCollisionTestClass::Cull(const Vector3 & min,const Vector3 & max)
{
if ((SweepMin.X > max.X) || (SweepMax.X < min.X)) {
return true;
}
if ((SweepMin.Y > max.Y) || (SweepMax.Y < min.Y)) {
return true;
}
if ((SweepMin.Z > max.Z) || (SweepMax.Z < min.Z)) {
return true;
}
return false;
}
inline bool AABoxCollisionTestClass::Cast_To_Triangle(const TriClass & tri)
{
return CollisionMath::Collide(Box,Move,tri,Result);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// OBBoxCollisionTestClass
//
// Contains an Oriented Bounding Box and a movement vector to be tested
// for collision. Also adds Min and Max vectors (axis aligned box)
// both for the initial box and for the volume swept out by the box.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
class OBBoxCollisionTestClass : public CollisionTestClass
{
public:
OBBoxCollisionTestClass(const OBBoxClass & obbox,const Vector3 & move,CastResultStruct * res,int type = COLL_TYPE_0);
OBBoxCollisionTestClass(const OBBoxCollisionTestClass & that);
OBBoxCollisionTestClass(const OBBoxCollisionTestClass & that,const Matrix3D & tm);
OBBoxCollisionTestClass(const AABoxCollisionTestClass & that,const Matrix3D & tm);
bool Cull(const Vector3 & min,const Vector3 & max);
bool Cull(const AABoxClass & box);
bool Cast_To_Triangle(const TriClass & tri);
public:
OBBoxClass Box;
Vector3 Move;
Vector3 SweepMin;
Vector3 SweepMax;
private:
// not implemented
OBBoxCollisionTestClass & operator = (const OBBoxCollisionTestClass &);
};
inline bool OBBoxCollisionTestClass::Cull(const Vector3 & min,const Vector3 & max)
{
if ((SweepMin.X > max.X) || (SweepMax.X < min.X)) {
return true;
}
if ((SweepMin.Y > max.Y) || (SweepMax.Y < min.Y)) {
return true;
}
if ((SweepMin.Z > max.Z) || (SweepMax.Z < min.Z)) {
return true;
}
return false;
}
inline bool OBBoxCollisionTestClass::Cast_To_Triangle(const TriClass & tri)
{
return CollisionMath::Collide(Box,Move,tri,Vector3(0,0,0),Result);
}
#endif

View File

@@ -0,0 +1,94 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/coltype.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 07/01/02 12:45p $*
* *
* $Revision:: 2 $*
* *
* 07/01/02 KM Coltype enum change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLTYPE_H
#define COLTYPE_H
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Collision 'Types'
//
// This enum defines the collision type bit-field that is used in render object
// collision detection.
//
// The collision type field in a collision or intersection test is used as a
// low-level collision mask. It will be 'AND'ed with the collision type of
// the render object and will ignore the object unless the result is
// non-zero. In Commando, we use this to implement separate collision
// representations for "physical" collisions versus "projectile"
// collisions. I.e. we use a very simple mesh for the character's
// physical collision and a more complex set of meshes for checking whether
// a bullet hits a person. This masking system is not meant to be a general
// "collision grouping" system. You should use a higher level system for doing
// things like making bullets ignore each other, etc.
//
// One more wrinkle to the system: The collision type in the render obj
// will always have the LSB set (COLL_TYPE_ALL) so that you can always
// do queries against every piece of geometry in a render obj if desired.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum
{
COLL_TYPE_ALL = 0x01, // perform this test against *EVERYTHING*
COLL_TYPE_0 = 0x02, // perform this test against type 0 collision objects
COLL_TYPE_1 = 0x04, // perform this test against type 1 collision objects
COLL_TYPE_2 = 0x08,
COLL_TYPE_3 = 0x10,
COLL_TYPE_4 = 0x20,
COLL_TYPE_5 = 0x40,
COLL_TYPE_6 = 0x80,
COLL_TYPE_PHYSICAL = COLL_TYPE_0, // physics collisions
COLL_TYPE_PROJECTILE = COLL_TYPE_1, // projectile collisions
COLL_TYPE_VIS = COLL_TYPE_2, // "vis node" detection
COLL_TYPE_CAMERA = COLL_TYPE_3, // camera collision (99% should match physical setting)
COLL_TYPE_VEHICLE = COLL_TYPE_4, // vehicles will collide with physical and this.
};
#endif

View File

@@ -0,0 +1,561 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/composite.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 11/27/01 12:45a $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CompositeRenderObjClass::CompositeRenderObjClass -- Constructor *
* CompositeRenderObjClass::CompositeRenderObjClass -- copy constructor *
* CompositeRenderObjClass::~CompositeRenderObjClass -- Destructor *
* CompositeRenderObjClass::operator -- assignment operator *
* CompositeRenderObjClass::Restart -- Recursively call Restart on all sub-objects *
* CompositeRenderObjClass::Get_Name -- returns the name of this render object *
* CompositeRenderObjClass::Set_Name -- sets the name of this render object *
* CompositeRenderObjClass::Set_Base_Model_Name -- sets the "base-model-name" *
* CompositeRenderObjClass::Get_Num_Polys -- returns the number of polys *
* CompositeRenderObjClass::Notify_Added -- notify all sub-objects that they were added *
* CompositeRenderObjClass::Notify_Removed -- notifies all subobjs they were removed from th *
* CompositeRenderObjClass::Cast_Ray -- cast a ray against this object *
* CompositeRenderObjClass::Cast_AABox -- cast a swept AABox against this object *
* CompositeRenderObjClass::Cast_OBBox -- cast a swept OBBox against this object *
* CompositeRenderObjClass::Intersect_AABox -- intersect this object with an AABox *
* CompositeRenderObjClass::Intersect_OBBox -- intersect this object with an OBBox *
* CompositeRenderObjClass::Create_Decal -- create a decal on this object *
* CompositeRenderObjClass::Delete_Decal -- remove a logical decal from this object *
* CompositeRenderObjClass::Update_Obj_Space_Bounding_Volumes -- updates the object-space BV *
* CompositeRenderObjClass::Set_User_Data -- set the userdata pointer *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "composite.h"
#include "wwdebug.h"
#include <stdlib.h>
#include <string.h>
/***********************************************************************************************
* CompositeRenderObjClass::CompositeRenderObjClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
CompositeRenderObjClass::CompositeRenderObjClass(void)
{
}
/***********************************************************************************************
* CompositeRenderObjClass::CompositeRenderObjClass -- copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
CompositeRenderObjClass::CompositeRenderObjClass(const CompositeRenderObjClass & that)
{
Set_Name(that.Get_Name());
Set_Base_Model_Name(that.Get_Base_Model_Name());
}
/***********************************************************************************************
* CompositeRenderObjClass::~CompositeRenderObjClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
CompositeRenderObjClass::~CompositeRenderObjClass(void)
{
}
/***********************************************************************************************
* CompositeRenderObjClass::operator -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
CompositeRenderObjClass & CompositeRenderObjClass::operator = (const CompositeRenderObjClass & that)
{
Set_Name(that.Get_Name());
Set_Base_Model_Name(that.Get_Base_Model_Name());
return *this;
}
/***********************************************************************************************
* CompositeRenderObjClass::Restart -- Recursively call Restart on all sub-objects *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/30/2001 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Restart(void)
{
for (int ni = 0; ni < Get_Num_Sub_Objects(); ni++) {
RenderObjClass * robj = Get_Sub_Object(ni);
WWASSERT(robj);
robj->Restart();
robj->Release_Ref();
}
}
/***********************************************************************************************
* CompositeRenderObjClass::Get_Name -- returns the name of this render object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
const char * CompositeRenderObjClass::Get_Name(void) const
{
return Name;
}
/***********************************************************************************************
* CompositeRenderObjClass::Set_Name -- sets the name of this render object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Set_Name(const char * name)
{
Name=name;
}
/***********************************************************************************************
* CompositeRenderObjClass::Set_Base_Model_Name -- sets the "base-model-name" *
* *
* The base-model-name was needed by the aggregate code. Ask Patrick Smith about it :-) *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Set_Base_Model_Name(const char *name)
{
// NULL is a legal value for BaseModelName. Unfortunately,
// StringClass::operator= does not modify the string when
// assigning NULL, so we explicitly handle that case here.
if (name != 0) {
BaseModelName = name;
} else {
BaseModelName = "";
}
}
/***********************************************************************************************
* CompositeRenderObjClass::Get_Num_Polys -- returns the number of polys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
int CompositeRenderObjClass::Get_Num_Polys(void) const
{
int count = 0;
for (int ni = 0; ni < Get_Num_Sub_Objects(); ni++) {
RenderObjClass * robj = Get_Sub_Object(ni);
WWASSERT(robj);
count += robj->Get_Num_Polys();
robj->Release_Ref();
}
return count;
}
/***********************************************************************************************
* CompositeRenderObjClass::Notify_Added -- notify all sub-objects that they were added *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Notify_Added(SceneClass * scene)
{
RenderObjClass::Notify_Added(scene);
for (int ni = 0; ni < Get_Num_Sub_Objects(); ni++) {
RenderObjClass * robj = Get_Sub_Object(ni);
WWASSERT(robj);
robj->Notify_Added(scene);
robj->Release_Ref();
}
}
/***********************************************************************************************
* CompositeRenderObjClass::Notify_Removed -- notifies all subobjs they were removed from the *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Notify_Removed(SceneClass * scene)
{
for (int ni = 0; ni < Get_Num_Sub_Objects(); ni++) {
RenderObjClass * robj = Get_Sub_Object(ni);
WWASSERT(robj);
robj->Notify_Removed(scene);
robj->Release_Ref();
}
RenderObjClass::Notify_Removed(scene);
}
/***********************************************************************************************
* CompositeRenderObjClass::Cast_Ray -- cast a ray against this object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
bool CompositeRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
{
bool res = false;
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
res |= robj->Cast_Ray(raytest);
robj->Release_Ref();
}
return res;
}
/***********************************************************************************************
* CompositeRenderObjClass::Cast_AABox -- cast a swept AABox against this object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
bool CompositeRenderObjClass::Cast_AABox(AABoxCollisionTestClass & boxtest)
{
bool res = false;
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
res |= robj->Cast_AABox(boxtest);
robj->Release_Ref();
}
return res;
}
/***********************************************************************************************
* CompositeRenderObjClass::Cast_OBBox -- cast a swept OBBox against this object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
bool CompositeRenderObjClass::Cast_OBBox(OBBoxCollisionTestClass & boxtest)
{
bool res = false;
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
res |= robj->Cast_OBBox(boxtest);
robj->Release_Ref();
}
return res;
}
/***********************************************************************************************
* CompositeRenderObjClass::Intersect_AABox -- intersect this object with an AABox *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
bool CompositeRenderObjClass::Intersect_AABox(AABoxIntersectionTestClass & boxtest)
{
bool res = false;
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
res |= robj->Intersect_AABox(boxtest);
robj->Release_Ref();
}
return res;
}
/***********************************************************************************************
* CompositeRenderObjClass::Intersect_OBBox -- intersect this object with an OBBox *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
bool CompositeRenderObjClass::Intersect_OBBox(OBBoxIntersectionTestClass & boxtest)
{
bool res = false;
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
res |= robj->Intersect_OBBox(boxtest);
robj->Release_Ref();
}
return res;
}
/***********************************************************************************************
* CompositeRenderObjClass::Create_Decal -- create a decal on this object *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Create_Decal(DecalGeneratorClass * generator)
{
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
robj->Create_Decal(generator);
robj->Release_Ref();
}
}
/***********************************************************************************************
* CompositeRenderObjClass::Delete_Decal -- remove a logical decal from this object *
* *
* This internally removes all decals with the given ID. The ID comes from the generator *
* which was used to create the decals. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Delete_Decal(uint32 decal_id)
{
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
robj->Delete_Decal(decal_id);
robj->Release_Ref();
}
}
/***********************************************************************************************
* CompositeRenderObjClass::Update_Obj_Space_Bounding_Volumes -- updates the object-space BVs *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Update_Obj_Space_Bounding_Volumes(void)
{
int i;
RenderObjClass * robj = NULL;
// if we don't have any sub objects, just set default bounds
if (Get_Num_Sub_Objects() <= 0) {
ObjSphere.Init(Vector3(0,0,0),0);
ObjBox.Center.Set(0,0,0);
ObjBox.Extent.Set(0,0,0);
return;
}
AABoxClass obj_aabox;
MinMaxAABoxClass box;
SphereClass sphere;
// loop through all sub-objects, combining their object-space bounding spheres and boxes.
robj = Get_Sub_Object(0);
WWASSERT(robj);
robj->Get_Obj_Space_Bounding_Sphere(ObjSphere);
robj->Get_Obj_Space_Bounding_Box(obj_aabox);
robj->Release_Ref();
box.Init(obj_aabox);
for (i=1; i<Get_Num_Sub_Objects(); i++) {
robj = Get_Sub_Object(i);
WWASSERT(robj);
robj->Get_Obj_Space_Bounding_Sphere(sphere);
robj->Get_Obj_Space_Bounding_Box(obj_aabox);
ObjSphere.Add_Sphere(sphere);
box.Add_Box(obj_aabox);
robj->Release_Ref();
}
ObjBox.Init(box);
Invalidate_Cached_Bounding_Volumes();
// Now update the object space bounding volumes of this object's container:
RenderObjClass *container = Get_Container();
if (container) container->Update_Obj_Space_Bounding_Volumes();
}
/***********************************************************************************************
* CompositeRenderObjClass::Set_User_Data -- set the userdata *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void CompositeRenderObjClass::Set_User_Data(void *value, bool recursive)
{
RenderObjClass::Set_User_Data(value);
if (recursive) {
for (int i=0; i<Get_Num_Sub_Objects(); i++) {
RenderObjClass * robj = Get_Sub_Object(i);
WWASSERT(robj);
robj->Set_User_Data(value,recursive);
robj->Release_Ref();
}
}
}
const char * CompositeRenderObjClass::Get_Base_Model_Name (void) const
{
if (BaseModelName.Is_Empty()) {
return NULL;
}
return BaseModelName;
}

View File

@@ -0,0 +1,99 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/composite.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 11/25/01 12:25p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COMPOSITE_H
#define COMPOSITE_H
#include "rendobj.h"
#include "wwstring.h"
/*
** CompositeRenderObjClass
** The sole purpose of this class is to encapsulate some of the chores that all
** "composite" (contain sub objects) render objects have to do. Typically all
** of the functions are implemented through the existing sub-object interface
** so there is still no assumption on how you store/organize your sub-objects.
*/
class CompositeRenderObjClass : public RenderObjClass
{
public:
CompositeRenderObjClass(void);
CompositeRenderObjClass(const CompositeRenderObjClass & that);
virtual ~CompositeRenderObjClass(void);
CompositeRenderObjClass & operator = (const CompositeRenderObjClass & that);
virtual void Restart(void);
virtual const char * Get_Name(void) const;
virtual void Set_Name(const char * name);
virtual const char * Get_Base_Model_Name (void) const;
virtual void Set_Base_Model_Name (const char *name);
virtual int Get_Num_Polys(void) const;
virtual void Notify_Added(SceneClass * scene);
virtual void Notify_Removed(SceneClass * scene);
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
virtual void Create_Decal(DecalGeneratorClass * generator);
virtual void Delete_Decal(uint32 decal_id);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const { sphere = ObjSphere; }
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const { box = ObjBox; }
virtual void Update_Obj_Space_Bounding_Volumes(void);
virtual void Set_User_Data(void *value, bool recursive = false);
protected:
StringClass Name; // name of the render object
StringClass BaseModelName; // name of the original render obj (before aggregation)
SphereClass ObjSphere; // object-space bounding sphere
AABoxClass ObjBox; // object-space bounding box
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,398 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DAZZLE_H
#define DAZZLE_H
#include "always.h"
#include "vector3.h"
#include "matrix3d.h"
#include "rendobj.h"
#include "wwstring.h"
#include "proto.h"
#include "w3derr.h"
#include "shader.h"
#include "matrix4.h"
class CameraClass;
class DazzleVisibilityClass;
struct VertexFormatXYZNDUV2;
class DazzleInitClass
{
public:
unsigned type;
bool use_camera_translation;
StringClass primary_texture_name;
StringClass secondary_texture_name;
StringClass lensflare_name;
float halo_intensity;
float halo_intensity_pow;
float halo_scale_x;
float halo_scale_y;
float dazzle_size_pow;
float dazzle_intensity_pow;
float dazzle_intensity;
float dazzle_area;
float dazzle_direction_area;
Vector3 dazzle_direction;
Vector3 dazzle_test_color;
Vector3 dazzle_color;
Vector3 halo_color;
float dazzle_scale_x;
float dazzle_scale_y;
float fadeout_start;
float fadeout_end;
float size_optimization_limit;
float history_weight;
float radius;
float blink_period;
float blink_on_time;
};
class LensflareInitClass
{
public:
LensflareInitClass()
:
flare_locations(0),
flare_sizes(0),
flare_colors(0),
flare_uv(0)
{
}
LensflareInitClass(const LensflareInitClass& lic)
:
type(lic.type),
texture_name(lic.texture_name),
flare_count(lic.flare_count),
flare_locations(0),
flare_sizes(0),
flare_colors(0),
flare_uv(0)
{
if (flare_count) {
flare_locations=W3DNEWARRAY float[flare_count];
memcpy(flare_locations,lic.flare_locations,sizeof(float)*flare_count);
flare_sizes=W3DNEWARRAY float[flare_count];
memcpy(flare_sizes,lic.flare_sizes,sizeof(float)*flare_count);
flare_colors=W3DNEWARRAY Vector3[flare_count];
memcpy(flare_colors,lic.flare_colors,sizeof(Vector3)*flare_count);
flare_uv=W3DNEWARRAY Vector4[flare_count];
memcpy(flare_uv,lic.flare_uv,sizeof(Vector4)*flare_count);
}
}
~LensflareInitClass()
{
delete[] flare_locations;
delete[] flare_sizes;
delete[] flare_colors;
delete[] flare_uv;
}
unsigned type;
StringClass texture_name;
int flare_count;
float* flare_locations;
float* flare_sizes;
Vector3* flare_colors;
Vector4* flare_uv;
};
class DazzleRenderObjClass;
class DazzleLayerClass;
class DazzleTypeClass
{
friend DazzleRenderObjClass;
friend DazzleLayerClass;
TextureClass* primary_texture;
TextureClass* secondary_texture;
DazzleInitClass ic;
float fadeout_end_sqr;
float fadeout_start_sqr;
StringClass name;
unsigned dazzle_test_color_integer;
unsigned dazzle_test_mask_integer;
unsigned lensflare_id;
ShaderClass dazzle_shader;
ShaderClass halo_shader;
float radius;
DazzleTypeClass(const DazzleInitClass& is);
virtual ~DazzleTypeClass();
public:
virtual void Calculate_Intensities(
float& dazzle_intensity,
float& dazzle_size,
float& halo_intensity,
const Vector3& camera_dir,
const Vector3& dazzle_dir,
const Vector3& dir_to_dazzle,
float distance) const;
void Set_Dazzle_Shader(const ShaderClass& s); // Set shader for the dazzle type
void Set_Halo_Shader(const ShaderClass& s); // Set shader for the dazzle type
TextureClass* Get_Dazzle_Texture();
TextureClass* Get_Halo_Texture();
};
// The DazzleLayerClass is for all the dazzles being rendered with a given
// group of camera settings: for example, different scenes may use different
// z-buffer settings and in such a case each scene should have a dazzle layer
// associated with it. (In some special cases a scene may have more than one
// dazzle layer). A dazzle layer contains visible and invisible lists for
// each dazzle type. During rendering each dazzle is put on the correct list
// (a "current dazzle layer" static variable is set before rendering the
// appropriate scenes to ensure this). After all scenes are rendered, the
// dazzle layers are rendered one by one with the correct camera settings.
// NOTE: dazzle layers must be constructed AFTER all the dazzle types have
// been initialized, since the constructor needs to know how many dazzle types
// there are.
class DazzleLayerClass {
friend DazzleRenderObjClass;
public:
DazzleLayerClass(void);
~DazzleLayerClass(void);
// Render all dazzles in this layer (DazzleRenderObj::Render() only sets visibility)
void Render(CameraClass* camera);
private:
virtual int Get_Visible_Item_Count(unsigned int type) const; // Return visible item count
// virtual void Get_Visible_Item_Locations(unsigned int type, Vector3* locations) const; // Copy locations of visible items to buffer
virtual void Clear_Visible_List(unsigned int type);
// We have an array of visible lists (one for each dazzle type).
DazzleRenderObjClass** visible_lists;
};
class LensflareTypeClass
{
friend DazzleLayerClass;
friend DazzleRenderObjClass;
TextureClass* texture;
LensflareInitClass lic;
StringClass name;
LensflareTypeClass(const LensflareInitClass& is);
virtual ~LensflareTypeClass();
public:
TextureClass* Get_Texture();
void Generate_Vertex_Buffers(
VertexFormatXYZNDUV2* vertex,
int& vertex_count,
float screen_x_scale,
float screen_y_scale,
float dazzle_intensity,
const Vector4& transformed_location);
void Render_Arrays(
const Vector4* vertex_coordinates,
const Vector2* uv_coordinates,
const Vector3* color,
int vertex_count,
int halo_vertex_count,
const Vector2* texture_coordinates);
};
class INIClass;
class DazzleRenderObjClass : public RenderObjClass
{
friend DazzleLayerClass;
DazzleRenderObjClass * succ;
unsigned type;
float current_dazzle_intensity;
float current_dazzle_size;
float current_halo_intensity;
float current_distance;
Vector4 transformed_loc;
Vector3 current_vloc;
Vector3 current_dir;
Vector3 dazzle_color;
Vector3 halo_color;
float lensflare_intensity;
float current_scale;
float visibility;
bool on_list; // This is used to avoid insterting a dazzle into a list twice.
float radius; // Used to cast rays against
unsigned int creation_time;
static bool _dazzle_rendering_enabled;
// static void Draw_Debug_Dazzle(int idx);
void vis_render_dazzle(SpecialRenderInfoClass & rinfo);
void Render_Dazzle(CameraClass* camera);
public:
DazzleRenderObjClass(unsigned type);
DazzleRenderObjClass(const char* type_name);
DazzleRenderObjClass(const DazzleRenderObjClass & src);
DazzleRenderObjClass & operator = (const DazzleRenderObjClass &);
DazzleRenderObjClass* Succ() { return succ; }
const DazzleRenderObjClass* Succ() const { return succ; }
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_DAZZLE; }
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
virtual void Set_Transform(const Matrix3D &m);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
virtual void Scale(float scale) { current_scale*=scale; };
void Set_Dazzle_Color(const Vector3& col) { dazzle_color=col; }
void Set_Halo_Color(const Vector3& col) { halo_color=col; }
void Set_Lensflare_Intensity (float intensity) {lensflare_intensity=intensity;}
unsigned int Get_Dazzle_Type(void) { return type; }
// Usually, a DazzleRenderObj adds itself to the appropriate visible list
// (determined by the current layer) when it is rendered. This does not
// work for dazzles with "camera transform off", since they are located in
// camera space and the standard worldspace visibility algo will give
// unpredictable results for them (they may never have a Render() call).
// So for these dazzles, you need to call Set_Layer() after constructing
// them (this is instead of putting them in a scene). This function adds
// the dazzle to the appropriate visible list. NOTE: It is also called
// internally by the Render() function.
void Set_Layer(DazzleLayerClass *layer);
// Persistant object save-load interface
// Dazzles save their "dazzle-type" and transform
virtual const PersistFactoryClass & Get_Factory (void) const;
// Set the static "current layer" variable. This variable is used in the
// Render() call so that the dazzle knows which list to add itself to if
// it is visible. This function must be called before rendering the
// scene(s) in which the dazzles are present.
static void Set_Current_Dazzle_Layer(DazzleLayerClass *layer);
static void Init_Type(const DazzleInitClass& i);
static void Init_Lensflare(const LensflareInitClass& i);
static void Init_From_INI(const INIClass* ini);
static unsigned Get_Type_ID(const char* name); // Return the ID of type with given name, or INT_MAX if failed
static const char * Get_Type_Name(unsigned int id); // Return the name of the type with the given ID
static DazzleTypeClass* Get_Type_Class(unsigned id); // Return dazzle type class pointer, or NULL if not found
// The pointer is NOT refcounted - all types are deinitialised
// when exiting the level.
static unsigned Get_Lensflare_ID(const char* name); // Return the ID of lensflare with given name, or INT_MAX if failed
static LensflareTypeClass* Get_Lensflare_Class(unsigned id); // Return lensflare type class pointer, or NULL if not found
static void Deinit();
// Install a class derived from DazzleVisibilityClass to add app-specific
// visibility determination. The default behavior will ask the scene which
// the dazzle is a member of to compute its visibility.
static void Install_Dazzle_Visibility_Handler(const DazzleVisibilityClass * visibility_handler);
// Globally disable/enable dazzle rendering
static void Enable_Dazzle_Rendering(bool onoff) { _dazzle_rendering_enabled = onoff; }
static bool Is_Dazzle_Rendering_Enabled(void) { return _dazzle_rendering_enabled; }
};
/**
** DazzleVisibilityClass
** The user should derive a class from DazzleVisibilityClass and implement an app-specific
** dazzle visibility test. Renegade will use ray-casting to determine visibility. The
** default visibility handler will query the scene which the dazzle is contained in.
*/
class DazzleVisibilityClass
{
public:
virtual float Compute_Dazzle_Visibility( RenderInfoClass & rinfo,
DazzleRenderObjClass * dazzle,
const Vector3 & point) const;
};
/**
** DazzlePrototypeClass
** This description object is generated when reading a W3D_CHUNK_DAZZLE. It stores the
** information needed to construct a particular instance of a dazzle. Prototypes are
** stored in the asset manager and used to construct render objects when needed.
*/
class DazzlePrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(DazzlePrototypeClass)
public:
DazzlePrototypeClass(void) : DazzleType(0) { }
virtual const char * Get_Name(void) const { return Name; }
virtual int Get_Class_ID(void) const { return RenderObjClass::CLASSID_DAZZLE; }
virtual RenderObjClass * Create(void);
virtual void DeleteSelf() { delete this; }
WW3DErrorType Load_W3D(ChunkLoadClass & cload);
private:
StringClass Name;
int DazzleType;
};
/**
** DazzleLoaderClass
** An instance of this class is registered with the asset manager and handles loading W3D_CHUNK_DAZZLE.
** It creates DazzlePrototypes from the data in the chunk.
*/
class DazzleLoaderClass : public PrototypeLoaderClass
{
public:
DazzleLoaderClass(void) { }
~DazzleLoaderClass(void) { }
virtual int Chunk_Type(void) { return W3D_CHUNK_DAZZLE; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
extern DazzleLoaderClass _DazzleLoader;
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,283 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 08/06/02 KM Added cube map and volume texture support
#ifndef DDSFILE_H
#define DDSFILE_H
#if defined(_MSC_VER)
#pragma once
#endif
#include "always.h"
#include "ww3dformat.h"
#include "wwstring.h"
#include "vector3.h"
struct IDirect3DSurface8;
struct IDirect3DVolume8;
// ----------------------------------------------------------------------------
//
// This structure represents the old DX7 color key structure. It is needed
// LegacyDDSURFACEDESC which is needed when loading DDS files. DO NOT MODIFY!
//
// ----------------------------------------------------------------------------
struct LegacyDDCOLORKEY
{
unsigned ColorSpaceLowValue;
unsigned ColorSpaceHighValue;
};
// ----------------------------------------------------------------------------
//
// This structure represents the old DX7 CAPS2 structure. It is needed
// LegacyDDSURFACEDESC which is needed when loading DDS files. DO NOT MODIFY!
//
// ----------------------------------------------------------------------------
struct LegacyDDSCAPS2
{
unsigned Caps;
unsigned Caps2;
unsigned Caps3;
unsigned Caps4;
};
// ----------------------------------------------------------------------------
//
// This structure represents the old DX7 pixel format structure. It is needed
// LegacyDDSURFACEDESC which is needed when loading DDS files. DO NOT MODIFY!
//
// ----------------------------------------------------------------------------
struct LegacyDDPIXELFORMAT
{
unsigned Size;
unsigned Flags;
unsigned FourCC;
union
{
unsigned RGBBitCount;
unsigned YUVBitCount;
unsigned ZBufferBitDepth;
unsigned AlphaBitDepth;
unsigned LuminanceBitCount;
unsigned BumpBitCount;
};
union
{
unsigned RBitMask;
unsigned YBitMask;
unsigned StencilBitDepth;
unsigned LuminanceBitMask;
unsigned BumpDuBitMask;
};
union
{
unsigned GBitMask;
unsigned UBitMask;
unsigned ZBitMask;
unsigned BumpDvBitMask;
};
union
{
unsigned BBitMask;
unsigned VBitMask;
unsigned StencilBitMask;
unsigned BumpLuminanceBitMask;
};
union
{
unsigned RGBAlphaBitMask;
unsigned YUVAlphaBitMask;
unsigned LuminanceAlphaBitMask;
unsigned RGBZBitMask;
unsigned YUVZBitMask;
};
};
// ----------------------------------------------------------------------------
//
// This structure represents the old DX7 surface description structure.
// It is needed when loading DDS files. DO NOT MODIFY!
//
// ----------------------------------------------------------------------------
struct LegacyDDSURFACEDESC2 {
unsigned Size;
unsigned Flags;
unsigned Height;
unsigned Width;
union
{
unsigned Pitch;
unsigned LinearSize;
};
union
{
unsigned BackBufferCount;
unsigned Depth; // added depth for volume textures
};
union
{
unsigned MipMapCount;
unsigned RefreshRate;
};
unsigned AlphaBitDepth;
unsigned Reserved;
void* Surface;
union
{
LegacyDDCOLORKEY CKDestOverlay;
unsigned EmptyFaceColor;
};
LegacyDDCOLORKEY CKDestBlt;
LegacyDDCOLORKEY CKSrcOverlay;
LegacyDDCOLORKEY CKSrcBlt;
LegacyDDPIXELFORMAT PixelFormat;
LegacyDDSCAPS2 Caps;
unsigned TextureStage;
};
enum DDSType
{
DDS_TEXTURE,
DDS_CUBEMAP,
DDS_VOLUME
};
// ----------------------------------------------------------------------------
//
// Utility class for loading DDS files. Simply create an instance of the class
// locally, call Load() and use the copy functions to retrieve the surface.
// The class handles conversion of the surface to equal compressed formats
// and all non-compressed formats. the compressed DXTn formats can't be cross-
// converted except for DXT1 which can be converted to DXT2 (this feature is
// needed as the NVidia cards have problems with DXT1).
//
// ----------------------------------------------------------------------------
class DDSFileClass
{
unsigned Width;
unsigned Height;
unsigned Depth;
unsigned FullWidth;
unsigned FullHeight;
unsigned FullDepth;
unsigned MipLevels;
unsigned long DateTime;
unsigned ReductionFactor;
unsigned char* DDSMemory;
WW3DFormat Format;
DDSType Type;
unsigned* LevelSizes;
unsigned* LevelOffsets;
unsigned CubeFaceSize;
LegacyDDSURFACEDESC2 SurfaceDesc;
char Name[256];
static unsigned Calculate_DXTC_Surface_Size(unsigned width, unsigned height, WW3DFormat format);
public:
// You can pass the name in .tga or .dds format, the class will automatically try and load .dds file.
// Note that creating the object will only give you image info - call Load() to load the surfaces.
DDSFileClass(const char* name,unsigned reduction_factor);
~DDSFileClass();
unsigned Get_Width(unsigned level) const;
unsigned Get_Height(unsigned level) const;
unsigned Get_Depth(unsigned level) const;
unsigned Get_Full_Width() const { return FullWidth; } // Get the width of level 0 of non-reduced texture
unsigned Get_Full_Height() const { return FullHeight; } // Get the height of level 0 of non-reduced texture
unsigned Get_Full_Depth() const { return FullDepth; }
unsigned long Get_Date_Time() const { return DateTime; }
unsigned Get_Mip_Level_Count() const { return MipLevels; }
const unsigned char* Get_Memory_Pointer(unsigned level) const;
unsigned Get_Level_Size(unsigned level) const;
WW3DFormat Get_Format() const { return Format; }
DDSType Get_Type() const { return Type; }
// Copy pixels to the destination surface.
void Copy_Level_To_Surface(unsigned level,IDirect3DSurface8* d3d_surface,const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
void Copy_Level_To_Surface(
unsigned level,
WW3DFormat dest_format,
unsigned dest_width,
unsigned dest_height,
unsigned char* dest_surface,
unsigned dest_pitch,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
// cube map
const unsigned char* Get_CubeMap_Memory_Pointer(unsigned face, unsigned level) const;
void Copy_CubeMap_Level_To_Surface
(
unsigned face,
unsigned level,
WW3DFormat dest_format,
unsigned width,
unsigned height,
unsigned char* surf,
unsigned pitch,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f)
);
// volume texture
const unsigned char* Get_Volume_Memory_Pointer(unsigned level) const;
void Copy_Volume_Level_To_Surface
(
unsigned level,
unsigned depth,
WW3DFormat dest_format,
unsigned width,
unsigned height,
unsigned char* vol,
unsigned row_pitch,
unsigned slice_pitch,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f)
);
// Get pixel in A8R8G8B8 format. This isn't the fastest possible way of reading data from DDS.
unsigned Get_Pixel(unsigned level,unsigned x,unsigned y) const;
// Uncompress one 4x4 block from the compressed image.
// Returns: true if block contained alpha, false is not
// Note: Destination can't be DXT or paletted surface!
bool Get_4x4_Block(
unsigned char* dest_ptr, // Destination surface pointer
unsigned dest_pitch, // Destination surface pitch, in bytes
WW3DFormat dest_format, // Destination surface format, A8R8G8B8 is fastest
unsigned level, // DDS mipmap level to copy from
unsigned source_x, // DDS x offset to copy from, must be aligned by 4!
unsigned source_y, // DDS y offset to copy from, must be aligned by 4!
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f)) const;
bool Load();
bool Is_Available() const { return !!LevelSizes; }
};
// ----------------------------------------------------------------------------
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/decalmsh.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/24/01 6:18p $*
* *
* $Revision:: 7 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DECALMSH_H
#define DECALMSH_H
#include "always.h"
#include "bittype.h"
#include "simplevec.h"
#include "vector.h"
#include "vector2.h"
#include "vector3.h"
#include "vector3i.h"
#include "vector4.h"
#include "shader.h"
#include "vertmaterial.h"
#include "meshgeometry.h"
class MeshClass;
class RenderInfoClass;
class DecalGeneratorClass;
class DecalSystemClass;
class OBBoxClass;
class TextureClass;
/**
** DecalMeshClass
** This is a "subordinate" class to MeshModel which simply adds "decal" polygons to the mesh.
** These polygons will always be exact copies of polygons already in the parent mesh.
**
** Design Goals:
** - Each decal can have its own material settings
** - Dynamically growing array of decals
** - Each decal is assigned a unique "logical-decal-id" by the decal manager
** - A decal mesh may be instructed to remove a specified "logical-decal"
**
** DecalMeshClass is an abstract base class from which we derive concrete classes.
*/
class DecalMeshClass : public RefCountClass
{
public:
DecalMeshClass(MeshClass * parent,DecalSystemClass * system);
virtual ~DecalMeshClass(void);
// world_vertex_locs and world_vertex_norms are dynamically updated worldspace vertex data
// which are used by some decal types which cannot use static object geometry (such as decals
// for skins, procedurally generated meshes, etc.)
virtual void Render(void) = 0;
virtual bool Create_Decal( DecalGeneratorClass * generator,
const OBBoxClass & localbox,
SimpleDynVecClass<uint32> & apt,
const DynamicVectorClass<Vector3> * world_vertex_locs = 0) = 0;
virtual bool Delete_Decal(uint32 id) = 0;
virtual int Decal_Count(void) = 0;
virtual uint32 Get_Decal_ID(int decal_index) = 0;
MeshClass * Peek_Parent(void);
DecalSystemClass * Peek_System(void);
DecalMeshClass * Peek_Next_Visible(void) { return NextVisible; }
void Set_Next_Visible(DecalMeshClass * mesh) { NextVisible = mesh; }
protected:
/*
** Members
*/
MeshClass * Parent;
DecalSystemClass * DecalSystem;
DecalMeshClass * NextVisible;
};
/*
** RigidDecalMeshClass: a concrete class derived from DecalMeshClass which is
** used for decals on rigid (non-skin) meshes.
*/
class RigidDecalMeshClass : public DecalMeshClass
{
public:
RigidDecalMeshClass(MeshClass * parent,DecalSystemClass * system);
virtual ~RigidDecalMeshClass(void);
// Rigid decal meshes have static geometry so they do not use world_vertex_locs/norms
virtual void Render(void);
virtual bool Create_Decal( DecalGeneratorClass * generator,
const OBBoxClass & localbox,
SimpleDynVecClass<uint32> & apt,
const DynamicVectorClass<Vector3> * world_vertex_locs = 0);
virtual bool Delete_Decal(uint32 id);
int Decal_Count(void);
uint32 Get_Decal_ID(int decal_index);
protected:
int Process_Material_Run(int start_index);
/*
** Connectivity
*/
SimpleDynVecClass<TriIndex> Polys;
/*
** Geometry
*/
SimpleDynVecClass<Vector3> Verts;
SimpleDynVecClass<Vector3> VertNorms;
/*
** Materials
*/
SimpleDynVecClass<ShaderClass> Shaders;
SimpleDynVecClass<TextureClass *> Textures;
SimpleDynVecClass<VertexMaterialClass *> VertexMaterials;
SimpleDynVecClass<Vector2> TexCoords;
/*
** Decal Organization
*/
struct DecalStruct
{
uint32 DecalID;
uint16 VertexStartIndex;
uint16 VertexCount;
uint16 FaceStartIndex;
uint16 FaceCount;
};
SimpleDynVecClass<DecalStruct> Decals;
};
/*
** SkinDecalMeshClass: a concrete class derived from DecalMeshClass which is
** used for decals on skin meshes.
*/
class SkinDecalMeshClass : public DecalMeshClass
{
public:
SkinDecalMeshClass(MeshClass * parent,DecalSystemClass * system);
virtual ~SkinDecalMeshClass(void);
// Skin decals use world_vertex_locs/norms since they cannot use static geometry
virtual void Render(void);
virtual bool Create_Decal( DecalGeneratorClass * generator,
const OBBoxClass & localbox,
SimpleDynVecClass<uint32> & apt,
const DynamicVectorClass<Vector3> * world_vertex_locs);
virtual bool Delete_Decal(uint32 id);
int Decal_Count(void);
uint32 Get_Decal_ID(int decal_index);
protected:
int Process_Material_Run(int start_index);
/*
** Connectivity
*/
SimpleDynVecClass<TriIndex> Polys;
/*
** Indirected vertex indices (for copying dynamically updated mesh geometry)
*/
SimpleDynVecClass<uint32> ParentVertexIndices;
/*
** Materials
*/
SimpleDynVecClass<ShaderClass> Shaders;
SimpleDynVecClass<TextureClass *> Textures;
SimpleDynVecClass<VertexMaterialClass *> VertexMaterials;
SimpleDynVecClass<Vector2> TexCoords;
/*
** Decal Organization
*/
struct DecalStruct
{
uint32 DecalID;
uint16 VertexStartIndex;
uint16 VertexCount;
uint16 FaceStartIndex;
uint16 FaceCount;
};
SimpleDynVecClass<DecalStruct> Decals;
};
/*
** DecalMeshClass inline functions
*/
inline MeshClass * DecalMeshClass::Peek_Parent(void)
{
return Parent;
}
inline DecalSystemClass * DecalMeshClass::Peek_System(void)
{
return DecalSystem;
}
/*
** RigidDecalMeshClass inline functions
*/
inline int RigidDecalMeshClass::Decal_Count(void)
{
return Decals.Count();
}
inline uint32 RigidDecalMeshClass::Get_Decal_ID(int decal_index)
{
return Decals[decal_index].DecalID;
}
/*
** SkinDecalMeshClass inline functions
*/
inline int SkinDecalMeshClass::Decal_Count(void)
{
return Decals.Count();
}
inline uint32 SkinDecalMeshClass::Get_Decal_ID(int decal_index)
{
return Decals[decal_index].DecalID;
}
#endif //DECALMSH_H

View File

@@ -0,0 +1,489 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/decalsys.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 8 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* DecalSystemClass::DecalSystemClass -- Constructor *
* DecalSystemClass::~DecalSystemClass -- Destructor *
* DecalSystemClass::Lock_Decal_Generator -- returns a DecalGenerator *
* DecalSystemClass::Unlock_Decal_Generator -- Destroys the decal generator *
* DecalSystemClass::Generate_Unique_Global_Decal_Id -- Generates a unique id for a decal *
* DecalGeneratorClass::DecalGeneratorClass -- Constructor *
* DecalGeneratorClass::~DecalGeneratorClass -- Destructor *
* DecalGeneratorClass::Add_Mesh -- Meshes that generate a decal should add themselves *
* DecalGeneratorClass::Get_Mesh_List -- returns the list of meshes *
* DecalGeneratorClass::Set_Mesh_Transform -- sets the current mesh coordinate system *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "decalsys.h"
#include "rendobj.h"
#include "mesh.h"
#include "decalmsh.h"
#include "matrixmapper.h"
#include "texture.h"
uint32 DecalSystemClass::DecalIDGenerator = 0;
/*
** DecalSystemClass Implementation
*/
/***********************************************************************************************
* DecalSystemClass::DecalSystemClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
DecalSystemClass::DecalSystemClass(void)
{
}
/***********************************************************************************************
* DecalSystemClass::~DecalSystemClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
DecalSystemClass::~DecalSystemClass(void)
{
}
/***********************************************************************************************
* DecalSystemClass::Lock_Decal_Generator -- returns a DecalGenerator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
DecalGeneratorClass * DecalSystemClass::Lock_Decal_Generator(void)
{
DecalGeneratorClass * gen = W3DNEW DecalGeneratorClass(Generate_Decal_Id(), this);
return gen;
}
/***********************************************************************************************
* DecalSystemClass::Unlock_Decal_Generator -- Destroys the decal generator *
* *
* Derived classes may take a record of the results of the generator here. Then they can *
* put a cap on the total number of decals in the system or whatever... *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void DecalSystemClass::Unlock_Decal_Generator(DecalGeneratorClass * generator)
{
delete generator;
}
/***********************************************************************************************
* DecalSystemClass::Generate_Unique_Global_Decal_Id -- Generates a unique id for a decal *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Hopefully you won't use more than 4 billion decals at one time... *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
uint32 DecalSystemClass::Generate_Unique_Global_Decal_Id(void)
{
return DecalIDGenerator++;
}
/*
** DecalGeneratorClass Implementation
*/
/***********************************************************************************************
* DecalGeneratorClass::DecalGeneratorClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
DecalGeneratorClass::DecalGeneratorClass(uint32 id,DecalSystemClass * system) :
DecalID(id),
System(system),
BackfaceVal(0.0f),
ApplyToTranslucentMeshes(false),
Material(NULL)
{
Material = NEW_REF(MaterialPassClass,());
WWASSERT(System != NULL);
WWASSERT(Material != NULL);
}
/***********************************************************************************************
* DecalGeneratorClass::~DecalGeneratorClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
DecalGeneratorClass::~DecalGeneratorClass(void)
{
REF_PTR_RELEASE(Material);
}
/***********************************************************************************************
* DecalGeneratorClass::Add_Mesh -- Meshes that generate a decal should add themselves *
* *
* This adds a mesh to the internal list. All meshes that actually add a decal should add *
* themselves to this list so that the DecalSystem can keep track of them. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void DecalGeneratorClass::Add_Mesh(RenderObjClass * mesh)
{
WWASSERT(mesh->Class_ID() == RenderObjClass::CLASSID_MESH);
MeshList.Add(mesh);
}
/***********************************************************************************************
* DecalGeneratorClass::Get_Mesh_List -- returns the list of meshes *
* *
* This is the list of meshes which generated decals for this "logical decal" *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
NonRefRenderObjListClass & DecalGeneratorClass::Get_Mesh_List(void)
{
return MeshList;
}
/***********************************************************************************************
* DecalGeneratorClass::Set_Mesh_Transform -- sets the current mesh coordinate system *
* *
* The coordinate system of the mesh is needed in order to compute texture coordinates for *
* the decal polygons. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/26/00 gth : Created. *
*=============================================================================================*/
void DecalGeneratorClass::Set_Mesh_Transform(const Matrix3D & mesh_transform)
{
/*
** Mobj-texture = Projection * Mwrld-texture * Mobj-wrld
*/
Matrix3D world_to_texture;
Matrix3D tmp;
Matrix4x4 mesh_to_texture;
Transform.Get_Orthogonal_Inverse(world_to_texture);
Matrix3D::Multiply(world_to_texture,mesh_transform,&tmp);
Matrix4x4::Multiply(Projection,tmp,&mesh_to_texture);
/*
** Plug the matrix and texture size into the mapper
*/
if (WW3D::Is_Texturing_Enabled()) {
float texsize = 64.0f;
TextureClass * tex = Material->Peek_Texture();
WWASSERT(tex != NULL);
if (tex) {
// SurfaceClass::SurfaceDescription surface_desc;
// tex->Get_Level_Description(surface_desc);
// texsize = surface_desc.Width;
texsize = tex->Get_Width();
}
Mapper->Set_Texture_Transform(mesh_to_texture,texsize);
}
}
/*
** MultiFixedPoolDecalSystemClass implementation
*/
MultiFixedPoolDecalSystemClass::MultiFixedPoolDecalSystemClass(uint32 num_pools, const uint32 *pool_sizes) :
Pools(0),
PoolCount(num_pools),
Generator_PoolID(0),
Generator_SlotID(0)
{
if (PoolCount)
{
WWASSERT(pool_sizes);
Pools = W3DNEWARRAY LogicalDecalPoolClass [PoolCount];
}
for (uint32 i = 0; i < PoolCount; i++) {
assert(pool_sizes[i]);
Pools[i].Initialize(pool_sizes[i]);
}
}
MultiFixedPoolDecalSystemClass::MultiFixedPoolDecalSystemClass(const MultiFixedPoolDecalSystemClass & that) :
Pools(0),
PoolCount(that.PoolCount),
Generator_PoolID(that.Generator_PoolID),
Generator_SlotID(that.Generator_SlotID)
{
uint32 i;
// Allocate arrays (we dont' copy array contents because those are mesh-specific and will be
// filled when the state is set anyway)
if (PoolCount) Pools = W3DNEWARRAY LogicalDecalPoolClass [PoolCount];
for (i = 0; i < PoolCount; i++) {
Pools[i].Initialize(that.Pools[i].Size);
}
}
MultiFixedPoolDecalSystemClass::~MultiFixedPoolDecalSystemClass(void)
{
if (Pools) {
delete [] Pools;
Pools = 0;
}
}
// This clears the slot in addition to locking the generator, thus preventing any decal id
// collisions (since any decal previously in that slot will have the same id as the new one).
DecalGeneratorClass * MultiFixedPoolDecalSystemClass::Lock_Decal_Generator(void)
{
Clear_Decal_Slot(Generator_PoolID, Generator_SlotID);
return DecalSystemClass::Lock_Decal_Generator();
}
// This will register the decal in the system in the appropriate pool and slot (determined by
// the generator's pool and slot ids), removing any decal which may have been there before.
void MultiFixedPoolDecalSystemClass::Unlock_Decal_Generator(DecalGeneratorClass * generator)
{
find_logical_decal(generator->Get_Decal_ID()).Set(generator);
DecalSystemClass::Unlock_Decal_Generator(generator);
}
// This notifies the system that a mesh which has decals on it was destroyed - therefore we
// need to remove the mesh from our list to avoid dangling pointers.
void MultiFixedPoolDecalSystemClass::Decal_Mesh_Destroyed(uint32 decal_id,DecalMeshClass * mesh)
{
// We must remove this mesh from all lists where it is present. The method is: for each
// decal id present in the decal mesh, find the logical decal and remove the decal mesh from
// its list.
assert(mesh);
MeshClass *parent = mesh->Peek_Parent();
find_logical_decal(decal_id).MeshList.Remove(parent);
}
// Not part of the DecalSystemClass interface - this function removes any decal currently in
// the given slot in the given pool.
void MultiFixedPoolDecalSystemClass::Clear_Decal_Slot(uint32 pool_id, uint32 slot_id)
{
find_logical_decal(pool_id, slot_id).Clear(encode_decal_id(pool_id, slot_id));
}
// This one removes all decals in a given pool.
void MultiFixedPoolDecalSystemClass::Clear_Pool(uint32 pool_id)
{
LogicalDecalPoolClass & pool = Pools[pool_id];
uint32 pool_size = pool.Size;
for (uint32 slot_id = 0; slot_id < pool_size; slot_id++) {
pool.Array[slot_id].Clear(encode_decal_id(pool_id, slot_id));
}
}
// And this one removes all decals in the system.
void MultiFixedPoolDecalSystemClass::Clear_All_Decals(void)
{
for (uint32 pool_id = 0; pool_id < PoolCount; pool_id++) {
LogicalDecalPoolClass & pool = Pools[pool_id];
uint32 pool_size = pool.Size;
for (uint32 slot_id = 0; slot_id < pool_size; slot_id++) {
pool.Array[slot_id].Clear(encode_decal_id(pool_id, slot_id));
}
}
}
// Get a reference to the logical decal at the given pool and slot id (performs range checking)
MultiFixedPoolDecalSystemClass::LogicalDecalClass & MultiFixedPoolDecalSystemClass::find_logical_decal(uint32 pool_id, uint32 slot_id)
{
assert(pool_id < PoolCount);
pool_id = MIN(pool_id, PoolCount);
LogicalDecalPoolClass & pool = Pools[pool_id];
assert(slot_id < pool.Size);
slot_id = MIN(slot_id, pool.Size);
return pool.Array[slot_id];
}
// Get a reference to the logical decal with the given decal id
MultiFixedPoolDecalSystemClass::LogicalDecalClass & MultiFixedPoolDecalSystemClass::find_logical_decal(uint32 decal_id)
{
uint32 pool_id, slot_id;
decode_decal_id(decal_id, pool_id, slot_id);
return find_logical_decal(pool_id, slot_id);
}
/*
** MultiFixedPoolDecalSystemClass::LogicalDecalClass implementation
*/
MultiFixedPoolDecalSystemClass::LogicalDecalClass::LogicalDecalClass(void)
{
}
MultiFixedPoolDecalSystemClass::LogicalDecalClass::~LogicalDecalClass(void)
{
// We don't need to do anything here since the mesh list will get removed automatically and
// the decals themselves don't neccessarily need to be removed because the logical decal is.
// (we don't hold references to the meshes in the mesh list)
}
// Sets the logical decal to the one specified by the given generator (clearing any existing
// decal information)
void MultiFixedPoolDecalSystemClass::LogicalDecalClass::Set(DecalGeneratorClass * generator)
{
Clear(generator->Get_Decal_ID());
/*
** Copy the generators' mesh list into our mesh list
*/
NonRefRenderObjListIterator gen_mesh_it(&(generator->Get_Mesh_List()));
for (gen_mesh_it.First(); !gen_mesh_it.Is_Done(); gen_mesh_it.Next()) {
MeshList.Add(gen_mesh_it.Get_Obj());
}
}
// Just clears any existing logical decal information, leaving the decal empty.
void MultiFixedPoolDecalSystemClass::LogicalDecalClass::Clear(uint32 decal_id)
{
// Remove the decal with this ID from all meshes where it appears
NonRefRenderObjListIterator it(&MeshList);
for (it.First(); !it.Is_Done(); it.Next()) {
it.Get_Obj()->Delete_Decal(decal_id);
}
// Delete list
while (!MeshList.Is_Empty()) {
MeshList.Remove_Head();
}
}
/*
** MultiFixedPoolDecalSystemClass::LogicalDecalPoolClass implementation
*/
MultiFixedPoolDecalSystemClass::LogicalDecalPoolClass::LogicalDecalPoolClass(void) :
Array(0),
Size(0)
{
}
MultiFixedPoolDecalSystemClass::LogicalDecalPoolClass::~LogicalDecalPoolClass(void)
{
if (Array) {
delete [] Array;
Array = 0;
}
}
void MultiFixedPoolDecalSystemClass::LogicalDecalPoolClass::Initialize(uint32 size)
{
if (Array) {
delete [] Array;
Array = 0;
}
Size = size;
assert(Size);
Array = W3DNEWARRAY LogicalDecalClass[Size];
}

View File

@@ -0,0 +1,308 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/decalsys.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 6/29/01 11:30a $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DECALSYS_H
#define DECALSYS_H
#include "always.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "obbox.h"
#include "robjlist.h"
#include "matpass.h"
#include "projector.h"
class DecalGeneratorClass;
class DecalMeshClass;
/**
** DecalSystemClass
** This is a class that manages creation and destruction of decals in the system. It is
** meant to be over-ridden for game-specific behaviors.
**
** Sample Code:
** 1 - Create the generator. The system gives it a unique id and gives you a clean decal generator
**
** DecalGeneratorClass * gen = DecalSystem->Lock_Decal_Generator();
** gen->Set_Transform(tm);
** gen->Set_Projection(proj_tm);
** gen->Set_Bounds(OBBox);
**
** 2 - Apply the generator to any objects that you want it to affect. It wont do anything if it
** does not overlap any polygons in those objects
**
** Scene->Collect_Objects(gen->Get_Bounding_Box(),objectlist);
** for (iterator.First(objectlist); !iterator.Is_Done(); iterator.Next()) {
** iterator.Peek_Object()->Create_Decal(gen);
** }
**
** 3 - Release the generator back to the system. At this point, the system may record which meshes
** actually generated extra decal polygons for future removal. All of this information will be
** tied together with the unique 'decal ID' that was assigned to the generator.
**
** DecalSystem->Unlock_Decal_Generator(gen);
**
*/
class DecalSystemClass
{
public:
DecalSystemClass(void);
virtual ~DecalSystemClass(void);
/*
** Create and release DecalGenerators. Note that this is the point at which the
** decal system can track "logical" decals. The generator will keep an internal list
** of all of the render objects which generated decals which you should copy if you
** want to track them (e.g. if you want to cap the maximum number of active decals and
** kill the old ones...)
*/
virtual DecalGeneratorClass * Lock_Decal_Generator(void);
virtual void Unlock_Decal_Generator(DecalGeneratorClass * generator);
/*
** When a decal-mesh is destroyed, it must inform the DecalSystem. Otherwise, systems
** which track decals can get dangling pointers.
*/
virtual void Decal_Mesh_Destroyed(uint32 decal_id,DecalMeshClass * mesh) { }
protected:
/*
** This generates the decal ID when a generator is created. This decal system reroutes this
** to Generate_Unique_Global_Decal_Id(), but other decal systems may use a different method.
*/
virtual uint32 Generate_Decal_Id(void) { return Generate_Unique_Global_Decal_Id(); }
/*
** Unique ID generation for decals. Not all decal systems have to use
** this method of generating ids. Some may wish to assign the id as the
** array index of the logical id or use some other aritrary method.
*/
static uint32 Generate_Unique_Global_Decal_Id(void);
static uint32 DecalIDGenerator;
};
/**
** DecalGeneratorClass
** This class encapsulates the information needed to generate a decal. It also tracks
** what meshes actually used it to generate new decal polygons.
*/
class DecalGeneratorClass : public ProjectorClass
{
public:
/*
** All meshes that actually generate decal polygons should register themselves in the
** list. Then when the decal generation is finished, this list can be copied so that
** we can come back to those meshes and remove the decals if we want to.
*/
void Add_Mesh(RenderObjClass * mesh);
NonRefRenderObjListClass & Get_Mesh_List(void);
/*
** Decal generator parameters.
** see ProjectorClass for control over the coordinate system, projection, etc
*/
uint32 Get_Decal_ID(void) { return DecalID; }
DecalSystemClass * Peek_Decal_System(void) { return System; }
/*
** Backface rejection thresh-hold. The dot-product between the projection vector and
** the normal of each polygon is taken, if the result is greater than this value the polygon
** is accepted into the decal. Set it to -1 if you want to accept all polygons.
*/
void Set_Backface_Threshhold(float val) { BackfaceVal = val; }
float Get_Backface_Threshhold(void) { return BackfaceVal; }
/*
** Normally, decals are not generated on translucent meshes. This is due to the "floating
** decals" that you can get on things like trees. The user can override this behavior
** through the following interface.
*/
void Apply_To_Translucent_Meshes(bool onoff) { ApplyToTranslucentMeshes = onoff; }
bool Is_Applied_To_Translucent_Meshes(void) { return ApplyToTranslucentMeshes; }
/*
** Material parameters: just grab a pointer the material pass and modify it.
** Remember to release your ref to it when you are done.
*/
MaterialPassClass * Get_Material(void) { WWASSERT(Material != NULL); Material->Add_Ref(); return Material; }
/*
** Decal generation support. Call Set_Mesh_Transform for the mesh you want to add
** a decal to. Then for each vertex, you can call 'Compute_Texture_Coordinate'
*/
void Set_Mesh_Transform(const Matrix3D & tm);
protected:
DecalGeneratorClass(uint32 id,DecalSystemClass * system);
~DecalGeneratorClass(void);
/*
** Logical Decal ID, DecalSystem that this generator is tied to
*/
DecalSystemClass * System;
uint32 DecalID; // unique ID generated by the DecalSystem
/*
** Backface Threshhold, Translucent mesh decal enable
*/
float BackfaceVal;
bool ApplyToTranslucentMeshes;
/*
** Material settings
*/
MaterialPassClass * Material; // material settings for the decal
/*
** Results, list of the meshes which actually generated decal polygons for this logical decal.
*/
NonRefRenderObjListClass MeshList;
friend class DecalSystemClass;
};
/**
** MultiFixedPoolDecalSystemClass: This is for decal systems which use several fixed-size decal
** pools (If more than one pool is used, it is usually to have separate pools for several
** different classes of decals). Note that here the decal IDs are a combination of the pool and
** slot ids. (decal ids only have to be unique within a given decal system)
*/
class MultiFixedPoolDecalSystemClass : public DecalSystemClass
{
public:
MultiFixedPoolDecalSystemClass(uint32 num_pools, const uint32 *pool_sizes);
MultiFixedPoolDecalSystemClass(const MultiFixedPoolDecalSystemClass & that);
virtual ~MultiFixedPoolDecalSystemClass(void);
// This clears the slot in addition to locking the generator, thus preventing any decal id
// collisions (since any decal previously in that slot will have the same id as the new one).
virtual DecalGeneratorClass * Lock_Decal_Generator(void);
// This will register the decal in the system in the appropriate pool and slot (determined by
// the generator's pool and slot ids), removing any decal which may have been there before.
virtual void Unlock_Decal_Generator(DecalGeneratorClass * generator);
// This notifies the system that a mesh which has decals on it was destroyed - therefore we
// need to remove the mesh from our list to avoid dangling pointers.
virtual void Decal_Mesh_Destroyed(uint32 id,DecalMeshClass * mesh);
// Not part of the DecalSystemClass interface - this function removes any decal currently in
// the given slot in the given pool.
void Clear_Decal_Slot(uint32 pool_id, uint32 slot_id);
// This one removes all decals in a given pool.
void Clear_Pool(uint32 pool_id);
// And this one removes all decals in the system.
void Clear_All_Decals(void);
protected:
/*
** This generates the decal ID when a generator is created. This decal system generates the
** decal ID from a pool ID and slot ID which are part of the state of the system so someone
** can set them before calling Lock_Decal_Generator() (which is where this function is called).
** We do it this way to avoid needing to override Lock_Decal_Generator().
*/
virtual uint32 Generate_Decal_Id(void) { return encode_decal_id(Generator_PoolID, Generator_SlotID); }
uint32 Generator_PoolID; // These should be set before calling Lock_Decal_Generator()
uint32 Generator_SlotID; // These should be set before calling Lock_Decal_Generator()
class LogicalDecalClass;
// Get a reference to the logical decal at the given pool and slot id (performs range chacking)
LogicalDecalClass & find_logical_decal(uint32 pool_id, uint32 slot_id);
// Get a reference to the logical decal with the given decal id
LogicalDecalClass & find_logical_decal(uint32 decal_id);
// The decal ids are formed so that the upper 16 bits are equal to the pool id and the lower
// 16 bits are equal to the slot id.
static uint32 encode_decal_id(uint32 pool_id, uint32 slot_id) { return (slot_id & 0xFFFF) | (pool_id << 16); }
static void decode_decal_id(uint32 decal_id, uint32 & pool_id, uint32 & slot_id) { slot_id = decal_id & 0xFFFF; pool_id = decal_id >> 16; }
// A class to store the meshes to which the decal has been applied (so that they can be removed when needed)
class LogicalDecalClass
{
public:
LogicalDecalClass(void);
~LogicalDecalClass(void);
void Set(DecalGeneratorClass * generator);
// Just clears any existing logical decal information, leaving the decal empty.
void Clear(uint32 decal_id);
NonRefRenderObjListClass MeshList;
};
class LogicalDecalPoolClass
{
public:
LogicalDecalPoolClass(void);
~LogicalDecalPoolClass(void);
void Initialize(uint32 size);
LogicalDecalClass * Array;
uint32 Size;
};
LogicalDecalPoolClass * Pools;
uint32 PoolCount;
};
#endif //DECALSYS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/distlod.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 1/25/01 2:46p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DISTLOD_H
#define DISTLOD_H
#include "proto.h"
#include "rendobj.h"
#include "composite.h"
#include "w3derr.h"
class ChunkLoadClass;
struct DistLODNodeDefStruct;
class DistLODDefClass;
/*
** DistLODClass
** Distance based LOD switching. This is a simple LOD object which switches models
** based on the pre-set distances. Note that the models are stored in order of
** descending resolution; i.e. the highest res model will be in index 0.
** Most functions in this class simply pass the call on to all of the LODs.
**
** When the level of detail is changed, the LOD which was being used must be
** notified that it is being removed from the scene and the new LOD must be
** notified that it is being added. This allows us to correctly handle lights
** and particle emitters in LODs...
*/
class DistLODClass : public CompositeRenderObjClass
{
public:
DistLODClass(const DistLODDefClass & desc);
DistLODClass(const DistLODClass & that);
virtual ~DistLODClass(void);
virtual RenderObjClass * Clone(void) const { return W3DNEW DistLODClass(*this); }
virtual int Class_ID(void) const { return CLASSID_DISTLOD; }
virtual int Get_Num_Polys(void) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
// Access each LOD individually through Get_Sub_Object.
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual int Get_Num_Sub_Objects(void) const;
virtual RenderObjClass * Get_Sub_Object(int index) const;
virtual int Add_Sub_Object_To_Bone(RenderObjClass * subobj,int bone_index);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Hierarchical Animation
// Here again, these functions are passed on to all LODs. Each LOD is
// assumed to use the same HTree (or have no HTree, this is so that LODs are
// animation-compatible) so the bone query functions simply pass to the top
// LOD.
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Animation( void );
virtual void Set_Animation( HAnimClass * motion, float frame, int anim_mode = ANIM_MODE_MANUAL);
virtual void Set_Animation( HAnimClass * motion0,float frame0,HAnimClass * motion1,float frame1,float percentage);
virtual void Set_Animation( HAnimComboClass * anim_combo);
virtual HAnimClass * Peek_Animation( void );
virtual int Get_Num_Bones(void);
virtual const char * Get_Bone_Name(int bone_index);
virtual int Get_Bone_Index(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(int boneindex);
virtual void Capture_Bone(int bindex);
virtual void Release_Bone(int bindex);
virtual bool Is_Bone_Captured(int bindex) const;
virtual void Control_Bone(int bindex,const Matrix3D & tm,bool world_space_translation = false);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Collision Detection, Ray Tracing
// Collision tests are performed on the top-level LOD.
/////////////////////////////////////////////////////////////////////////////
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Attributes, Options, Properties, etc
/////////////////////////////////////////////////////////////////////////////
virtual int Get_Num_Snap_Points(void);
virtual void Get_Snap_Point(int index,Vector3 * set);
virtual void Scale(float scale);
virtual void Scale(float scalex, float scaley, float scalez);
/////////////////////////////////////////////////////////////////////////////
// DistLOD Interface
/////////////////////////////////////////////////////////////////////////////
virtual float Get_Switch_Up_Dist(int index) const { return Lods[index].ResUpDist; }
virtual float Get_Switch_Down_Dist(int index) const { return Lods[index].ResDownDist; }
virtual void Set_Switch_Up_Dist(int index, float dist) { Lods[index].ResUpDist = dist; }
virtual void Set_Switch_Down_Dist(int index, float dist) { Lods[index].ResDownDist = dist; }
private:
enum { HIGHEST_LOD = 0 };
void Free(void);
void Update_Lod(const CameraClass & camera);
void Increment_Lod(void);
void Decrement_Lod(void);
struct LODNodeClass
{
RenderObjClass * Model;
float ResUpDist;
float ResDownDist;
};
int LodCount; // how many models are in this LOD object
int CurLod; // which model is currently used for rendering
int VpPushLod; // which model was used for the vp->push (in CurLod changes before the pop)
LODNodeClass * Lods; // one LODNodeClass for each level
};
/*
** Loaders for DistLODClass
*/
class DistLODLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_LODMODEL; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
/*
** DistLODModelDefStruct
** Describes a single model in a Distance-Based LOD Object
*/
struct DistLODNodeDefStruct
{
DistLODNodeDefStruct(void) : Name(NULL),ResDownDist(0.0f),ResUpDist(0.0f) {}
char * Name;
float ResDownDist;
float ResUpDist;
};
/*
** DistLODDefClass
** Description of a Distance-Based LOD Object. These are similar
** to the HModelDef's that are used by the asset manager to construct
** HModels.
*/
class DistLODDefClass
{
public:
DistLODDefClass(void);
DistLODDefClass(const char * name,int lodcount,DistLODNodeDefStruct * lods);
~DistLODDefClass(void);
WW3DErrorType Load_W3D(ChunkLoadClass & cload);
const char * Get_Name(void) const { return Name; }
private:
char * Name;
int LodCount;
DistLODNodeDefStruct * Lods;
void Free(void);
bool read_header(ChunkLoadClass & cload);
bool read_node(ChunkLoadClass & cload,DistLODNodeDefStruct * node);
friend class DistLODClass;
};
/*
** Prototype for Dist-LOD objects
*/
class DistLODPrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(DistLODPrototypeClass)
public:
DistLODPrototypeClass( DistLODDefClass *def ) { Definition = def; }
virtual const char * Get_Name(void) const { return Definition->Get_Name(); }
virtual int Get_Class_ID(void) const { return RenderObjClass::CLASSID_DISTLOD; }
virtual RenderObjClass * Create(void);
virtual void DeleteSelf() { delete this; }
protected:
virtual ~DistLODPrototypeClass(void) { delete Definition; }
private:
DistLODDefClass * Definition;
};
/*
** Instance of the loaders which the asset manager install
*/
extern DistLODLoaderClass _DistLODLoader;
#endif

View File

@@ -0,0 +1,209 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dllist.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 3/21/01 6:18p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DLLIST_H
#define DLLIST_H
template <class T> class DLNodeClass;
template <class T>
class DLListClass
{
friend DLNodeClass<T>;
DLNodeClass<T>* head;
DLNodeClass<T>* tail;
public:
DLListClass() : head(0), tail(0) {}
virtual ~DLListClass() { }
void Add_Head(DLNodeClass<T>* node);
void Remove_Head();
void Add_Tail(DLNodeClass<T>* node);
void Remove_Tail();
T* Head() { return static_cast<T*>(head); }
T* Tail() { return static_cast<T*>(tail); }
const T* Const_Head() const { return static_cast<const T*>(head); }
const T* Const_Tail() const { return static_cast<const T*>(tail); }
};
// Destroy-list will call delete for all nodes when the list is destructed. Note that the class doesn't work
// with undeclared pointer types (destructor has to be known).
template <class T>
class DLDestroyListClass : public DLListClass<T>
{
public:
virtual ~DLDestroyListClass()
{
while (T* t=Head()) {
delete t;
}
}
};
template <class T>
class DLNodeClass : public W3DMPO
{
// nope, this is an ABC
//W3DMPO_GLUE(DLNodeClass)
friend DLListClass<T>;
DLNodeClass<T>* succ;
DLNodeClass<T>* pred;
DLListClass<T>* list;
public:
DLNodeClass() : succ(0), pred(0), list(0) {}
~DLNodeClass() { Remove(); }
void Insert_Before(DLNodeClass<T>* n)
{
list=n->list;
succ=n;
pred=n->pred;
if (n->pred) n->pred->succ=this;
n->pred=this;
if (list->head==n) {
list->head=this;
}
}
void Insert_After(DLNodeClass<T>* n)
{
list=n->list;
pred=n;
succ=n->succ;
if (n->succ) n->succ->pred=this;
n->succ=this;
if (list->tail==n) {
list->tail=this;
}
}
void Remove()
{
if (!list) return;
if (list->Head()==this) {
DLListClass<T>* tmp_list=list;
list=0;
tmp_list->Remove_Head();
return;
}
if (list->Tail()==this) {
DLListClass<T>* tmp_list=list;
list=0;
tmp_list->Remove_Tail();
return;
}
if (succ) succ->pred=pred;
if (pred) pred->succ=succ;
list=0;
}
T* Succ() { return static_cast<T*>(succ); }
T* Pred() { return static_cast<T*>(pred); }
const T* Const_Succ() const { return static_cast<const T*>(succ); }
const T* Const_Pred() const { return static_cast<const T*>(pred); }
DLListClass<T>* List() { return list; }
};
template <class T>
inline void DLListClass<T>::Add_Head(DLNodeClass<T>* n)
{
n->list=this;
if (head) {
n->Insert_Before(head);
head=n;
}
else {
tail=n;
head=n;
n->succ=0;
n->pred=0;
}
}
template <class T>
inline void DLListClass<T>::Add_Tail(DLNodeClass<T>* n)
{
n->list=this;
if (tail) {
n->Insert_After(tail);
tail=n;
}
else {
tail=n;
head=n;
n->succ=0;
n->pred=0;
}
}
template <class T>
inline void DLListClass<T>::Remove_Head()
{
if (!head) return;
DLNodeClass<T>* n=head;
head=head->Succ();
if (!head) tail=head;
else head->pred=0;
n->Remove();
}
template <class T>
inline void DLListClass<T>::Remove_Tail()
{
if (!tail) return;
DLNodeClass<T>* n=tail;
tail=tail->Pred();
if (!tail) head=tail;
else tail->succ=0;
n->Remove();
}
#endif //DLLIST_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : DX8 Caps *
* *
* $Archive:: /Commando/Code/ww3d2/dx8caps.h $*
* *
* Original Author:: Hector Yee *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 24 $*
* *
* 06/27/02 KM Z Format support *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#ifndef DX8CAPS_H
#define DX8CAPS_H
#include "always.h"
#include "ww3dformat.h"
#include <d3d8.h>
class DX8Caps
{
public:
enum DriverVersionStatusType {
DRIVER_STATUS_UNKNOWN,
DRIVER_STATUS_GOOD,
DRIVER_STATUS_OK,
DRIVER_STATUS_BAD
};
enum VendorIdType {
VENDOR_UNKNOWN,
VENDOR_NVIDIA,
VENDOR_ATI,
VENDOR_INTEL,
VENDOR_S3,
VENDOR_POWERVR,
VENDOR_MATROX,
VENDOR_3DFX,
VENDOR_3DLABS,
VENDOR_CIRRUSLOGIC,
VENDOR_RENDITION
};
enum DeviceTypeATI {
DEVICE_ATI_UNKNOWN,
DEVICE_ATI_RAGE_II,
DEVICE_ATI_RAGE_II_PLUS,
DEVICE_ATI_RAGE_IIC_PCI,
DEVICE_ATI_RAGE_IIC_AGP,
DEVICE_ATI_RAGE_128_MOBILITY,
DEVICE_ATI_RAGE_128_MOBILITY_M3,
DEVICE_ATI_RAGE_128_MOBILITY_M4,
DEVICE_ATI_RAGE_128_PRO_ULTRA,
DEVICE_ATI_RAGE_128_4X,
DEVICE_ATI_RAGE_128_PRO_GL,
DEVICE_ATI_RAGE_128_PRO_VR,
DEVICE_ATI_RAGE_128_GL,
DEVICE_ATI_RAGE_128_VR,
DEVICE_ATI_RAGE_PRO,
DEVICE_ATI_RAGE_PRO_MOBILITY,
DEVICE_ATI_MOBILITY_RADEON,
DEVICE_ATI_MOBILITY_RADEON_VE_M6,
DEVICE_ATI_RADEON_VE,
DEVICE_ATI_RADEON_DDR,
DEVICE_ATI_RADEON,
DEVICE_ATI_MOBILITY_R7500,
DEVICE_ATI_R7500,
DEVICE_ATI_R8500
};
enum DeviceType3DLabs {
DEVICE_3DLABS_UNKNOWN,
DEVICE_3DLABS_PERMEDIA,
DEVICE_3DLABS_300SX,
DEVICE_3DLABS_500TX,
DEVICE_3DLABS_DELTA,
DEVICE_3DLABS_MX,
DEVICE_3DLABS_GAMMA,
DEVICE_3DLABS_PERMEDIA2S_ST,
DEVICE_3DLABS_PERMEDIA3,
DEVICE_3DLABS_R3,
DEVICE_3DLABS_PERMEDIA4,
DEVICE_3DLABS_R4,
DEVICE_3DLABS_G2,
DEVICE_3DLABS_OXYGEN_VX1,
DEVICE_3DLABS_TI_P1,
DEVICE_3DLABS_PERMEDIA2
};
enum DeviceTypeNVidia {
DEVICE_NVIDIA_UNKNOWN,
DEVICE_NVIDIA_GEFORCE3,
DEVICE_NVIDIA_QUADRO2_PRO,
DEVICE_NVIDIA_GEFORCE2_GO,
DEVICE_NVIDIA_GEFORCE2_ULTRA,
DEVICE_NVIDIA_GEFORCE2_GTS,
DEVICE_NVIDIA_QUADRO,
DEVICE_NVIDIA_GEFORCE_DDR,
DEVICE_NVIDIA_GEFORCE_256,
DEVICE_NVIDIA_TNT2_ALADDIN,
DEVICE_NVIDIA_TNT2,
DEVICE_NVIDIA_TNT2_ULTRA,
DEVICE_NVIDIA_TNT2_VANTA,
DEVICE_NVIDIA_TNT2_M64,
DEVICE_NVIDIA_TNT,
DEVICE_NVIDIA_RIVA_128,
DEVICE_NVIDIA_TNT_VANTA,
DEVICE_NVIDIA_NV1,
DEVICE_NVIDIA_GEFORCE2_MX,
DEVICE_NVIDIA_GEFORCE4_TI_4600,
DEVICE_NVIDIA_GEFORCE4_TI_4400,
DEVICE_NVIDIA_GEFORCE4_TI,
DEVICE_NVIDIA_GEFORCE4_TI_4200,
DEVICE_NVIDIA_GEFORCE4_MX_460,
DEVICE_NVIDIA_GEFORCE4_MX_440,
DEVICE_NVIDIA_GEFORCE4_MX_420,
DEVICE_NVIDIA_GEFORCE4,
DEVICE_NVIDIA_GEFORCE4_GO_440,
DEVICE_NVIDIA_GEFORCE4_GO_420,
DEVICE_NVIDIA_GEFORCE4_GO_420_32M,
DEVICE_NVIDIA_GEFORCE4_GO_440_64M,
DEVICE_NVIDIA_GEFORCE4_GO,
DEVICE_NVIDIA_GEFORCE3_TI_500,
DEVICE_NVIDIA_GEFORCE3_TI_200,
DEVICE_NVIDIA_GEFORCE2_INTEGRATED,
DEVICE_NVIDIA_GEFORCE2_TI,
DEVICE_NVIDIA_QUADRO2_MXR_EX_GO,
DEVICE_NVIDIA_GEFORCE2_MX_100_200,
DEVICE_NVIDIA_GEFORCE2_MX_400,
DEVICE_NVIDIA_QUADRO_DCC
};
enum DeviceType3Dfx {
DEVICE_3DFX_UNKNOWN,
DEVICE_3DFX_VOODOO_5500_AGP,
DEVICE_3DFX_VOODOO_3,
DEVICE_3DFX_BANSHEE,
DEVICE_3DFX_VOODOO_2,
DEVICE_3DFX_VOODOO_GRAPHICS,
DEVICE_3DFX_VOODOO_RUSH
};
enum DeviceTypeMatrox {
DEVICE_MATROX_UNKNOWN,
DEVICE_MATROX_G550,
DEVICE_MATROX_G400,
DEVICE_MATROX_G200_AGP,
DEVICE_MATROX_G200_PCI,
DEVICE_MATROX_G100_PCI,
DEVICE_MATROX_G100_AGP,
DEVICE_MATROX_MILLENNIUM_II_AGP,
DEVICE_MATROX_MILLENNIUM_II_PCI,
DEVICE_MATROX_MYSTIQUE,
DEVICE_MATROX_MILLENNIUM,
DEVICE_MATROX_PARHELIA,
DEVICE_MATROX_PARHELIA_AGP8X
};
enum DeviceTypePowerVR {
DEVICE_POWERVR_UNKNOWN,
DEVICE_POWERVR_KYRO
};
enum DeviceTypeS3 {
DEVICE_S3_UNKNOWN,
DEVICE_S3_SAVAGE_MX,
DEVICE_S3_SAVAGE_4,
DEVICE_S3_SAVAGE_200
};
enum DeviceTypeIntel {
DEVICE_INTEL_UNKNOWN,
DEVICE_INTEL_810,
DEVICE_INTEL_810E,
DEVICE_INTEL_815
};
DX8Caps(IDirect3D8* direct3d, const D3DCAPS8& caps,WW3DFormat display_format, const D3DADAPTER_IDENTIFIER8& adapter_id);
DX8Caps(IDirect3D8* direct3d, IDirect3DDevice8* D3DDevice,WW3DFormat display_format, const D3DADAPTER_IDENTIFIER8& adapter_id);
static void Shutdown(void);
void Compute_Caps(WW3DFormat display_format, const D3DADAPTER_IDENTIFIER8& adapter_id);
bool Support_TnL() const { return SupportTnL; };
bool Support_DXTC() const { return SupportDXTC; }
bool Support_Gamma() const { return supportGamma; }
bool Support_NPatches() const { return SupportNPatches; }
bool Support_Bump_Envmap() const { return SupportBumpEnvmap; }
bool Support_Bump_Envmap_Luminance() const { return SupportBumpEnvmapLuminance; }
bool Support_ZBias() const { return SupportZBias; }
bool Support_Anisotropic_Filtering() const { return SupportAnisotropicFiltering; }
bool Support_ModAlphaAddClr() const { return SupportModAlphaAddClr; }
bool Support_Dot3() const { return SupportDot3; }
bool Support_PointSprites() const { return SupportPointSprites; }
bool Support_Cubemaps() const { return SupportCubemaps; }
bool Can_Do_Multi_Pass() const { return CanDoMultiPass; }
bool Is_Fog_Allowed() const { return IsFogAllowed; }
bool Is_Valid_Display_Format(int width, int height, WW3DFormat format);
int Get_Max_Textures_Per_Pass() const { return MaxTexturesPerPass; }
// -------------------------------------------------------------------------
//
// Vertex shader support. Version number is split in major and minor, such that 1.0 would
// have 1 as major and 0 as minor version number.
//
// -------------------------------------------------------------------------
int Get_Vertex_Shader_Major_Version() const { return 0xff&(VertexShaderVersion>>8); }
int Get_Vertex_Shader_Minor_Version() const { return 0xff&(VertexShaderVersion); }
int Get_Pixel_Shader_Major_Version() const { return 0xff&(PixelShaderVersion>>8); }
int Get_Pixel_Shader_Minor_Version() const { return 0xff&(PixelShaderVersion); }
int Get_Max_Simultaneous_Textures() const { return MaxSimultaneousTextures;}
bool Support_Texture_Format(WW3DFormat format) const { return SupportTextureFormat[format]; }
bool Support_Render_To_Texture_Format(WW3DFormat format) const { return SupportRenderToTextureFormat[format]; }
bool Support_Depth_Stencil_Format(WW3DZFormat format) const { return SupportDepthStencilFormat[format]; }
D3DCAPS8 const & Get_DX8_Caps() const { return Caps; }
const StringClass& Get_Log() const { return CapsLog; }
const StringClass& Get_Compact_Log() const { return CompactLog; }
unsigned Get_Vendor() const { return VendorId; }
unsigned Get_Device() const { return DeviceId; }
const StringClass& Get_Driver_Name() const { return DriverDLL; }
unsigned Get_Driver_Build_Version() const { return DriverBuildVersion; }
// This will return false if the driver version is known to have problems.
DriverVersionStatusType Get_Driver_Version_Status() { return DriverVersionStatus; }
private:
static VendorIdType Define_Vendor(unsigned vendor_id);
static DeviceTypeATI Get_ATI_Device(unsigned device_id);
static DeviceType3DLabs Get_3DLabs_Device(unsigned device_id);
static DeviceTypeNVidia Get_NVidia_Device(unsigned device_id);
static DeviceType3Dfx Get_3Dfx_Device(unsigned device_id);
static DeviceTypeMatrox Get_Matrox_Device(unsigned device_id);
static DeviceTypePowerVR Get_PowerVR_Device(unsigned device_id);
static DeviceTypeS3 Get_S3_Device(unsigned device_id);
static DeviceTypeIntel Get_Intel_Device(unsigned device_id);
void Init_Caps(IDirect3DDevice8* D3DDevice);
void Check_Texture_Format_Support(WW3DFormat display_format,const D3DCAPS8& caps);
void Check_Render_To_Texture_Support(WW3DFormat display_format,const D3DCAPS8& caps);
void Check_Depth_Stencil_Support(WW3DFormat display_format, const D3DCAPS8& caps);
void Check_Texture_Compression_Support(const D3DCAPS8& caps);
void Check_Bumpmap_Support(const D3DCAPS8& caps);
void Check_Shader_Support(const D3DCAPS8& caps);
void Check_Maximum_Texture_Support(const D3DCAPS8& caps);
void Check_Driver_Version_Status();
void Vendor_Specific_Hacks(const D3DADAPTER_IDENTIFIER8& adapter_id);
int MaxDisplayWidth;
int MaxDisplayHeight;
D3DCAPS8 Caps;
bool SupportTnL;
bool SupportDXTC;
bool supportGamma;
bool SupportNPatches;
bool SupportBumpEnvmap;
bool SupportBumpEnvmapLuminance;
bool SupportTextureFormat[WW3D_FORMAT_COUNT];
bool SupportRenderToTextureFormat[WW3D_FORMAT_COUNT];
bool SupportDepthStencilFormat[WW3D_ZFORMAT_COUNT];
bool SupportZBias;
bool SupportAnisotropicFiltering;
bool SupportModAlphaAddClr;
bool SupportDot3;
bool SupportPointSprites;
bool SupportCubemaps;
bool CanDoMultiPass;
bool IsFogAllowed;
int MaxTexturesPerPass;
int VertexShaderVersion;
int PixelShaderVersion;
int MaxSimultaneousTextures;
unsigned DeviceId;
unsigned DriverBuildVersion;
DriverVersionStatusType DriverVersionStatus;
VendorIdType VendorId;
StringClass DriverDLL;
IDirect3D8* Direct3D; // warning XDK name conflict KJM
StringClass CapsLog;
StringClass CompactLog;
};
#endif

View File

@@ -0,0 +1,103 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8fvf.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 5:06p $*
* *
* $Revision:: 7 $*
* *
* 06/26/02 KM VB Vertex format update for shaders *
* 07/17/02 KM VB Vertex format update for displacement mapping *
* 08/01/02 KM VB Vertex format update for cube mapping *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "dx8fvf.h"
#include "wwstring.h"
#include <D3dx8core.h>
static unsigned Get_FVF_Vertex_Size(unsigned FVF)
{
return D3DXGetFVFVertexSize(FVF);
}
FVFInfoClass::FVFInfoClass(unsigned FVF_, unsigned vertex_size)
:
FVF(FVF_),
fvf_size(FVF!=0 ? Get_FVF_Vertex_Size(FVF) : vertex_size)
{
location_offset=0;
blend_offset=location_offset;
if ((FVF&D3DFVF_XYZ)==D3DFVF_XYZ) blend_offset+=3*sizeof(float);
normal_offset=blend_offset;
if ( ((FVF&D3DFVF_XYZB4)==D3DFVF_XYZB4) &&
((FVF&D3DFVF_LASTBETA_UBYTE4)==D3DFVF_LASTBETA_UBYTE4) ) normal_offset+=3*sizeof(float)+sizeof(DWORD);
diffuse_offset=normal_offset;
if ((FVF&D3DFVF_NORMAL)==D3DFVF_NORMAL) diffuse_offset+=3*sizeof(float);
specular_offset=diffuse_offset;
if ((FVF&D3DFVF_DIFFUSE)==D3DFVF_DIFFUSE) specular_offset+=sizeof(DWORD);
texcoord_offset[0]=specular_offset;
if ((FVF&D3DFVF_SPECULAR)==D3DFVF_SPECULAR) texcoord_offset[0]+=sizeof(DWORD);
for (unsigned int i=1; i<D3DDP_MAXTEXCOORD; i++)
{
texcoord_offset[i]=texcoord_offset[i-1];
if ((int(FVF)&D3DFVF_TEXCOORDSIZE1(i-1))==D3DFVF_TEXCOORDSIZE1(i-1)) texcoord_offset[i]+=sizeof(float);
else if ((int(FVF)&D3DFVF_TEXCOORDSIZE2(i-1))==D3DFVF_TEXCOORDSIZE2(i-1)) texcoord_offset[i]+=2*sizeof(float);
else if ((int(FVF)&D3DFVF_TEXCOORDSIZE3(i-1))==D3DFVF_TEXCOORDSIZE3(i-1)) texcoord_offset[i]+=3*sizeof(float);
else if ((int(FVF)&D3DFVF_TEXCOORDSIZE4(i-1))==D3DFVF_TEXCOORDSIZE4(i-1)) texcoord_offset[i]+=4*sizeof(float);
}
}
void FVFInfoClass::Get_FVF_Name(StringClass& fvfname) const
{
switch (Get_FVF()) {
case DX8_FVF_XYZ: fvfname="D3DFVF_XYZ"; break;
case DX8_FVF_XYZN: fvfname="D3DFVF_XYZ|D3DFVF_NORMAL"; break;
case DX8_FVF_XYZNUV1: fvfname="D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1"; break;
case DX8_FVF_XYZNUV2: fvfname="D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2"; break;
case DX8_FVF_XYZNDUV1: fvfname="D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1|D3DFVF_DIFFUSE"; break;
case DX8_FVF_XYZNDUV2: fvfname="D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2|D3DFVF_DIFFUSE"; break;
case DX8_FVF_XYZDUV1: fvfname="D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_DIFFUSE"; break;
case DX8_FVF_XYZDUV2: fvfname="D3DFVF_XYZ|D3DFVF_TEX2|D3DFVF_DIFFUSE"; break;
case DX8_FVF_XYZUV1: fvfname="D3DFVF_XYZ|D3DFVF_TEX1"; break;
case DX8_FVF_XYZUV2: fvfname="D3DFVF_XYZ|D3DFVF_TEX2"; break;
case DX8_FVF_XYZNDUV1TG3 : fvfname="(D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX4|D3DFVF_TEXCOORDSIZE2(0)|D3DFVF_TEXCOORDSIZE3(1)|D3DFVF_TEXCOORDSIZE3(2)|D3DFVF_TEXCOORDSIZE3(3))"; break;
case DX8_FVF_XYZNUV2DMAP : fvfname="(D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX3|D3DFVF_TEXCOORDSIZE1(0)|D3DFVF_TEXCOORDSIZE4(1)|D3DFVF_TEXCOORDSIZE2(2))"; break;
case DX8_FVF_XYZNDCUBEMAP : fvfname="(D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1|D3DFVFTEXCOORDSIZE3(0)"; break;
default: fvfname="Unknown!";
}
}

View File

@@ -0,0 +1,290 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8fvf.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 5:06p $*
* *
* $Revision:: 7 $*
* *
* 06/26/02 KM VB Vertex format update for shaders *
* 07/17/02 KM VB Vertex format update for displacement mapping *
* 08/01/02 KM VB Vertex format update for cube mapping *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8_FVF_H
#define DX8_FVF_H
#include "always.h"
#include <d3d8.h>
#ifdef WWDEBUG
#include "wwdebug.h"
#endif
class StringClass;
enum {
DX8_FVF_XYZ = D3DFVF_XYZ,
DX8_FVF_XYZN = D3DFVF_XYZ|D3DFVF_NORMAL,
DX8_FVF_XYZNUV1 = D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
DX8_FVF_XYZNUV2 = D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2,
DX8_FVF_XYZNDUV1 = D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1|D3DFVF_DIFFUSE,
DX8_FVF_XYZNDUV2 = D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2|D3DFVF_DIFFUSE,
DX8_FVF_XYZDUV1 = D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_DIFFUSE,
DX8_FVF_XYZDUV2 = D3DFVF_XYZ|D3DFVF_TEX2|D3DFVF_DIFFUSE,
DX8_FVF_XYZUV1 = D3DFVF_XYZ|D3DFVF_TEX1,
DX8_FVF_XYZUV2 = D3DFVF_XYZ|D3DFVF_TEX2,
DX8_FVF_XYZNDUV1TG3 = (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX4|D3DFVF_TEXCOORDSIZE2(0)|D3DFVF_TEXCOORDSIZE3(1)|D3DFVF_TEXCOORDSIZE3(2)|D3DFVF_TEXCOORDSIZE3(3)),
DX8_FVF_XYZNUV2DMAP = (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE4(1) | D3DFVF_TEXCOORDSIZE2(2) ),
DX8_FVF_XYZNDCUBEMAP = D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE //|D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE3(0)
};
// ----------------------------------------------------------------------------
//
// Util structures for vertex buffer handling. Cast the void pointer returned
// by the vertex buffer to one of these structures.
//
// ----------------------------------------------------------------------------
struct VertexFormatXYZ
{
float x;
float y;
float z;
};
struct VertexFormatXYZNUV1
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
float u1;
float v1;
};
struct VertexFormatXYZNUV2
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
float u1;
float v1;
float u2;
float v2;
};
struct VertexFormatXYZN
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
};
struct VertexFormatXYZNDUV1
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
unsigned diffuse;
float u1;
float v1;
};
struct VertexFormatXYZNDUV2
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
unsigned diffuse;
float u1;
float v1;
float u2;
float v2;
};
struct VertexFormatXYZDUV1
{
float x;
float y;
float z;
unsigned diffuse;
float u1;
float v1;
};
struct VertexFormatXYZDUV2
{
float x;
float y;
float z;
unsigned diffuse;
float u1;
float v1;
float u2;
float v2;
};
struct VertexFormatXYZUV1
{
float x;
float y;
float z;
float u1;
float v1;
};
struct VertexFormatXYZUV2
{
float x;
float y;
float z;
float u1;
float v1;
float u2;
float v2;
};
// todo KJM compress
struct VertexFormatXYZNDUV1TG3
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
unsigned diffuse;
float u1;
float v1;
float Sx;
float Sy;
float Sz;
float Tx;
float Ty;
float Tz;
float SxTx;
float SxTy;
float SxTz;
};
// displacement mapping format
struct VertexFormatXYZNUV2DMAP
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
float T1x;
float T1y;
float T1z;
float T1w;
float T2x;
float T2y;
};
// cube map format (texcoords are normally generated)
struct VertexFormatXYZNDCUBEMAP
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
unsigned diffuse;
// float u1;
// float v1;
// float w1;
};
// FVF info class can be created for any legal FVF. It constructs information
// of offsets to various elements in the vertex buffer.
class FVFInfoClass : public W3DMPO
{
W3DMPO_GLUE(FVFInfoClass)
mutable unsigned FVF;
mutable unsigned fvf_size;
unsigned location_offset;
unsigned normal_offset;
unsigned blend_offset;
unsigned texcoord_offset[D3DDP_MAXTEXCOORD];
unsigned diffuse_offset;
unsigned specular_offset;
public:
FVFInfoClass(unsigned FVF, unsigned vertex_size=0);
inline unsigned Get_Location_Offset() const { return location_offset; }
inline unsigned Get_Normal_Offset() const { return normal_offset; }
#ifdef WWDEBUG
inline unsigned Get_Tex_Offset(unsigned int n) const { WWASSERT(n<D3DDP_MAXTEXCOORD); return texcoord_offset[n]; }
#else
inline unsigned Get_Tex_Offset(unsigned int n) const { return texcoord_offset[n]; }
#endif
inline unsigned Get_Diffuse_Offset() const { return diffuse_offset; }
inline unsigned Get_Specular_Offset() const { return specular_offset; }
inline unsigned Get_FVF() const { return FVF; }
inline unsigned Get_FVF_Size() const { return fvf_size; }
void Get_FVF_Name(StringClass& fvfname) const; // For debug purposes
// for enabling vertex shaders
inline void Set_FVF(unsigned fvf) const { FVF=fvf; }
inline void Set_FVF_Size(unsigned size) const { fvf_size=size; }
};
#endif

View File

@@ -0,0 +1,551 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8indexbuffer.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/09/01 3:12p $*
* *
* $Revision:: 26 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//#define INDEX_BUFFER_LOG
#include "dx8indexbuffer.h"
#include "dx8wrapper.h"
#include "dx8caps.h"
#include "sphere.h"
#include "thread.h"
#include "wwmemlog.h"
#define DEFAULT_IB_SIZE 5000
static bool _DynamicSortingIndexArrayInUse=false;
static SortingIndexBufferClass* _DynamicSortingIndexArray;
static unsigned short _DynamicSortingIndexArraySize=0;
static unsigned short _DynamicSortingIndexArrayOffset=0;
static bool _DynamicDX8IndexBufferInUse=false;
static DX8IndexBufferClass* _DynamicDX8IndexBuffer=NULL;
static unsigned short _DynamicDX8IndexBufferSize=DEFAULT_IB_SIZE;
static unsigned short _DynamicDX8IndexBufferOffset=0;
static int _IndexBufferCount;
static int _IndexBufferTotalIndices;
static int _IndexBufferTotalSize;
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
IndexBufferClass::IndexBufferClass(unsigned type_, unsigned short index_count_)
:
index_count(index_count_),
type(type_),
engine_refs(0)
{
WWASSERT(type==BUFFER_TYPE_DX8 || type==BUFFER_TYPE_SORTING);
WWASSERT(index_count);
_IndexBufferCount++;
_IndexBufferTotalIndices+=index_count;
_IndexBufferTotalSize+=index_count*sizeof(unsigned short);
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("New IB, %d indices, size %d bytes\n",index_count,index_count*sizeof(unsigned short)));
WWDEBUG_SAY(("Total IB count: %d, total %d indices, total size %d bytes\n",
_IndexBufferCount,
_IndexBufferTotalIndices,
_IndexBufferTotalSize));
#endif
}
IndexBufferClass::~IndexBufferClass()
{
_IndexBufferCount--;
_IndexBufferTotalIndices-=index_count;
_IndexBufferTotalSize-=index_count*sizeof(unsigned short);
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("Delete IB, %d indices, size %d bytes\n",index_count,index_count*sizeof(unsigned short)));
WWDEBUG_SAY(("Total IB count: %d, total %d indices, total size %d bytes\n",
_IndexBufferCount,
_IndexBufferTotalIndices,
_IndexBufferTotalSize));
#endif
}
unsigned IndexBufferClass::Get_Total_Buffer_Count()
{
return _IndexBufferCount;
}
unsigned IndexBufferClass::Get_Total_Allocated_Indices()
{
return _IndexBufferTotalIndices;
}
unsigned IndexBufferClass::Get_Total_Allocated_Memory()
{
return _IndexBufferTotalSize;
}
void IndexBufferClass::Add_Engine_Ref() const
{
engine_refs++;
}
void IndexBufferClass::Release_Engine_Ref() const
{
engine_refs--;
WWASSERT(engine_refs>=0);
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void IndexBufferClass::Copy(unsigned int* indices,unsigned first_index,unsigned count)
{
WWASSERT(indices);
if (first_index) {
DX8IndexBufferClass::AppendLockClass l(this,first_index,count);
unsigned short* inds=l.Get_Index_Array();
for (unsigned v=0;v<count;++v) {
*inds++=unsigned short(*indices++);
}
}
else {
DX8IndexBufferClass::WriteLockClass l(this);
unsigned short* inds=l.Get_Index_Array();
for (unsigned v=0;v<count;++v) {
*inds++=unsigned short(*indices++);
}
}
}
// ----------------------------------------------------------------------------
void IndexBufferClass::Copy(unsigned short* indices,unsigned first_index,unsigned count)
{
WWASSERT(indices);
if (first_index) {
DX8IndexBufferClass::AppendLockClass l(this,first_index,count);
unsigned short* inds=l.Get_Index_Array();
for (unsigned v=0;v<count;++v) {
*inds++=*indices++;
}
}
else {
DX8IndexBufferClass::WriteLockClass l(this);
unsigned short* inds=l.Get_Index_Array();
for (unsigned v=0;v<count;++v) {
*inds++=*indices++;
}
}
}
// ----------------------------------------------------------------------------
//
//
// ----------------------------------------------------------------------------
IndexBufferClass::WriteLockClass::WriteLockClass(IndexBufferClass* index_buffer_, int flags) : index_buffer(index_buffer_)
{
DX8_THREAD_ASSERT();
WWASSERT(index_buffer);
WWASSERT(!index_buffer->Engine_Refs());
index_buffer->Add_Ref();
switch (index_buffer->Type()) {
case BUFFER_TYPE_DX8:
DX8_Assert();
DX8_ErrorCode(static_cast<DX8IndexBufferClass*>(index_buffer)->Get_DX8_Index_Buffer()->Lock(
0,
index_buffer->Get_Index_Count()*sizeof(WORD),
(unsigned char**)&indices,
flags));
break;
case BUFFER_TYPE_SORTING:
indices=static_cast<SortingIndexBufferClass*>(index_buffer)->index_buffer;
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
//
//
// ----------------------------------------------------------------------------
IndexBufferClass::WriteLockClass::~WriteLockClass()
{
DX8_THREAD_ASSERT();
switch (index_buffer->Type()) {
case BUFFER_TYPE_DX8:
DX8_Assert();
DX8_ErrorCode(static_cast<DX8IndexBufferClass*>(index_buffer)->index_buffer->Unlock());
break;
case BUFFER_TYPE_SORTING:
break;
default:
WWASSERT(0);
break;
}
index_buffer->Release_Ref();
}
// ----------------------------------------------------------------------------
IndexBufferClass::AppendLockClass::AppendLockClass(IndexBufferClass* index_buffer_,unsigned start_index, unsigned index_range)
:
index_buffer(index_buffer_)
{
DX8_THREAD_ASSERT();
WWASSERT(start_index+index_range<=index_buffer->Get_Index_Count());
WWASSERT(index_buffer);
WWASSERT(!index_buffer->Engine_Refs());
index_buffer->Add_Ref();
switch (index_buffer->Type()) {
case BUFFER_TYPE_DX8:
DX8_Assert();
DX8_ErrorCode(static_cast<DX8IndexBufferClass*>(index_buffer)->index_buffer->Lock(
start_index*sizeof(unsigned short),
index_range*sizeof(unsigned short),
(unsigned char**)&indices,
0));
break;
case BUFFER_TYPE_SORTING:
indices=static_cast<SortingIndexBufferClass*>(index_buffer)->index_buffer+start_index;
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
IndexBufferClass::AppendLockClass::~AppendLockClass()
{
DX8_THREAD_ASSERT();
switch (index_buffer->Type()) {
case BUFFER_TYPE_DX8:
DX8_Assert();
DX8_ErrorCode(static_cast<DX8IndexBufferClass*>(index_buffer)->index_buffer->Unlock());
break;
case BUFFER_TYPE_SORTING:
break;
default:
WWASSERT(0);
break;
}
index_buffer->Release_Ref();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
DX8IndexBufferClass::DX8IndexBufferClass(unsigned short index_count_,UsageType usage)
:
IndexBufferClass(BUFFER_TYPE_DX8,index_count_)
{
DX8_THREAD_ASSERT();
WWASSERT(index_count);
unsigned usage_flags=
D3DUSAGE_WRITEONLY|
((usage&USAGE_DYNAMIC) ? D3DUSAGE_DYNAMIC : 0)|
((usage&USAGE_NPATCHES) ? D3DUSAGE_NPATCHES : 0)|
((usage&USAGE_SOFTWAREPROCESSING) ? D3DUSAGE_SOFTWAREPROCESSING : 0);
if (!DX8Wrapper::Get_Current_Caps()->Support_TnL()) {
usage_flags|=D3DUSAGE_SOFTWAREPROCESSING;
}
HRESULT ret=DX8Wrapper::_Get_D3D_Device8()->CreateIndexBuffer(
sizeof(WORD)*index_count,
usage_flags,
D3DFMT_INDEX16,
(usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
&index_buffer);
if (SUCCEEDED(ret)) {
return;
}
WWDEBUG_SAY(("Index buffer creation failed, trying to release assets...\n"));
// Vertex buffer creation failed, so try releasing least used textures and flushing the mesh cache.
// Free all textures that haven't been used in the last 5 seconds
TextureClass::Invalidate_Old_Unused_Textures(5000);
// Invalidate the mesh cache
WW3D::_Invalidate_Mesh_Cache();
// Try again...
ret=DX8Wrapper::_Get_D3D_Device8()->CreateIndexBuffer(
sizeof(WORD)*index_count,
usage_flags,
D3DFMT_INDEX16,
(usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
&index_buffer);
if (SUCCEEDED(ret)) {
WWDEBUG_SAY(("...Index buffer creation succesful\n"));
}
// If it still fails it is fatal
DX8_ErrorCode(ret);
}
// ----------------------------------------------------------------------------
DX8IndexBufferClass::~DX8IndexBufferClass()
{
index_buffer->Release();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
SortingIndexBufferClass::SortingIndexBufferClass(unsigned short index_count_)
:
IndexBufferClass(BUFFER_TYPE_SORTING,index_count_)
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(index_count);
index_buffer=W3DNEWARRAY unsigned short[index_count];
}
// ----------------------------------------------------------------------------
SortingIndexBufferClass::~SortingIndexBufferClass()
{
delete[] index_buffer;
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
DynamicIBAccessClass::DynamicIBAccessClass(unsigned short type_, unsigned short index_count_)
:
IndexCount(index_count_),
IndexBuffer(0),
Type(type_)
{
WWASSERT(Type==BUFFER_TYPE_DYNAMIC_DX8 || Type==BUFFER_TYPE_DYNAMIC_SORTING);
if (Type==BUFFER_TYPE_DYNAMIC_DX8) {
Allocate_DX8_Dynamic_Buffer();
}
else {
Allocate_Sorting_Dynamic_Buffer();
}
}
DynamicIBAccessClass::~DynamicIBAccessClass()
{
REF_PTR_RELEASE(IndexBuffer);
if (Type==BUFFER_TYPE_DYNAMIC_DX8) {
_DynamicDX8IndexBufferInUse=false;
_DynamicDX8IndexBufferOffset+=IndexCount;
}
else {
_DynamicSortingIndexArrayInUse=false;
_DynamicSortingIndexArrayOffset+=IndexCount;
}
}
void DynamicIBAccessClass::_Deinit()
{
WWASSERT ((_DynamicDX8IndexBuffer == NULL) || (_DynamicDX8IndexBuffer->Num_Refs() == 1));
REF_PTR_RELEASE(_DynamicDX8IndexBuffer);
_DynamicDX8IndexBufferInUse=false;
_DynamicDX8IndexBufferSize=DEFAULT_IB_SIZE;
_DynamicDX8IndexBufferOffset=0;
WWASSERT ((_DynamicSortingIndexArray == NULL) || (_DynamicSortingIndexArray->Num_Refs() == 1));
REF_PTR_RELEASE(_DynamicSortingIndexArray);
_DynamicSortingIndexArrayInUse=false;
_DynamicSortingIndexArraySize=0;
_DynamicSortingIndexArrayOffset=0;
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
DynamicIBAccessClass::WriteLockClass::WriteLockClass(DynamicIBAccessClass* ib_access_)
:
DynamicIBAccess(ib_access_)
{
DX8_THREAD_ASSERT();
DynamicIBAccess->IndexBuffer->Add_Ref();
switch (DynamicIBAccess->Get_Type()) {
case BUFFER_TYPE_DYNAMIC_DX8:
WWASSERT(DynamicIBAccess);
// WWASSERT(!dynamic_dx8_index_buffer->Engine_Refs());
DX8_Assert();
DX8_ErrorCode(
static_cast<DX8IndexBufferClass*>(DynamicIBAccess->IndexBuffer)->Get_DX8_Index_Buffer()->Lock(
DynamicIBAccess->IndexBufferOffset*sizeof(WORD),
DynamicIBAccess->Get_Index_Count()*sizeof(WORD),
(unsigned char**)&Indices,
!DynamicIBAccess->IndexBufferOffset ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE));
break;
case BUFFER_TYPE_DYNAMIC_SORTING:
Indices=static_cast<SortingIndexBufferClass*>(DynamicIBAccess->IndexBuffer)->index_buffer;
Indices+=DynamicIBAccess->IndexBufferOffset;
break;
default:
WWASSERT(0);
break;
}
}
DynamicIBAccessClass::WriteLockClass::~WriteLockClass()
{
DX8_THREAD_ASSERT();
switch (DynamicIBAccess->Get_Type()) {
case BUFFER_TYPE_DYNAMIC_DX8:
DX8_Assert();
DX8_ErrorCode(static_cast<DX8IndexBufferClass*>(DynamicIBAccess->IndexBuffer)->Get_DX8_Index_Buffer()->Unlock());
break;
case BUFFER_TYPE_DYNAMIC_SORTING:
break;
default:
WWASSERT(0);
break;
}
DynamicIBAccess->IndexBuffer->Release_Ref();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DynamicIBAccessClass::Allocate_DX8_Dynamic_Buffer()
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(!_DynamicDX8IndexBufferInUse);
_DynamicDX8IndexBufferInUse=true;
// If requesting more indices than dynamic index buffer can fit, delete the ib
// and adjust the size to the new count.
if (IndexCount>_DynamicDX8IndexBufferSize) {
REF_PTR_RELEASE(_DynamicDX8IndexBuffer);
_DynamicDX8IndexBufferSize=IndexCount;
if (_DynamicDX8IndexBufferSize<DEFAULT_IB_SIZE) _DynamicDX8IndexBufferSize=DEFAULT_IB_SIZE;
}
// Create a new vb if one doesn't exist currently
if (!_DynamicDX8IndexBuffer) {
unsigned usage=DX8IndexBufferClass::USAGE_DYNAMIC;
if (DX8Wrapper::Get_Current_Caps()->Support_NPatches()) {
usage|=DX8IndexBufferClass::USAGE_NPATCHES;
}
_DynamicDX8IndexBuffer=NEW_REF(DX8IndexBufferClass,(
_DynamicDX8IndexBufferSize,
(DX8IndexBufferClass::UsageType)usage));
_DynamicDX8IndexBufferOffset=0;
}
// Any room at the end of the buffer?
if (((unsigned)IndexCount+_DynamicDX8IndexBufferOffset)>_DynamicDX8IndexBufferSize) {
_DynamicDX8IndexBufferOffset=0;
}
REF_PTR_SET(IndexBuffer,_DynamicDX8IndexBuffer);
IndexBufferOffset=_DynamicDX8IndexBufferOffset;
}
void DynamicIBAccessClass::Allocate_Sorting_Dynamic_Buffer()
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(!_DynamicSortingIndexArrayInUse);
_DynamicSortingIndexArrayInUse=true;
unsigned new_index_count=_DynamicSortingIndexArrayOffset+IndexCount;
WWASSERT(new_index_count<65536);
if (new_index_count>_DynamicSortingIndexArraySize) {
REF_PTR_RELEASE(_DynamicSortingIndexArray);
_DynamicSortingIndexArraySize=new_index_count;
if (_DynamicSortingIndexArraySize<DEFAULT_IB_SIZE) _DynamicSortingIndexArraySize=DEFAULT_IB_SIZE;
}
if (!_DynamicSortingIndexArray) {
_DynamicSortingIndexArray=NEW_REF(SortingIndexBufferClass,(_DynamicSortingIndexArraySize));
_DynamicSortingIndexArrayOffset=0;
}
REF_PTR_SET(IndexBuffer,_DynamicSortingIndexArray);
IndexBufferOffset=_DynamicSortingIndexArrayOffset;
}
void DynamicIBAccessClass::_Reset(bool frame_changed)
{
_DynamicSortingIndexArrayOffset=0;
if (frame_changed) _DynamicDX8IndexBufferOffset=0;
}
unsigned short DynamicIBAccessClass::Get_Default_Index_Count(void)
{
return _DynamicDX8IndexBufferSize;
}
/*Added so that VisualC++ doesn't remove our try/catch blocks around index buffer access.
This is needed because of a Windows 2000 Kernal bug as explained in the DX 9.0b readme file.*/
int IndexBufferExceptionFunc(void)
{
int b=1;
b += _IndexBufferTotalIndices;
return b;
}

View File

@@ -0,0 +1,210 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8indexbuffer.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 7/10/01 12:27p $*
* *
* $Revision:: 12 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8INDEXBUFFER_H
#define DX8INDEXBUFFER_H
#include "always.h"
#include "wwdebug.h"
#include "refcount.h"
#include "sphere.h"
class DX8Wrapper;
class SortingRendererClass;
struct IDirect3DIndexBuffer8;
class DX8IndexBufferClass;
class SortingIndexBufferClass;
// ----------------------------------------------------------------------------
class IndexBufferClass : public W3DMPO, public RefCountClass
{
// nope, it's an ABC
//W3DMPO_GLUE(IndexBufferClass)
protected:
virtual ~IndexBufferClass();
public:
IndexBufferClass(unsigned type, unsigned short index_count);
void Copy(unsigned int* indices,unsigned start_index,unsigned index_count);
void Copy(unsigned short* indices,unsigned start_index,unsigned index_count);
inline unsigned short Get_Index_Count() const { return index_count; }
inline unsigned Type() const { return type; }
void Add_Engine_Ref() const;
void Release_Engine_Ref() const;
inline unsigned Engine_Refs() const { return engine_refs; }
class WriteLockClass
{
IndexBufferClass* index_buffer;
unsigned short* indices;
public:
WriteLockClass(IndexBufferClass* index_buffer, int flags=0);
~WriteLockClass();
unsigned short* Get_Index_Array() { return indices; }
};
class AppendLockClass
{
IndexBufferClass* index_buffer;
unsigned short* indices;
public:
AppendLockClass(IndexBufferClass* index_buffer,unsigned start_index, unsigned index_range);
~AppendLockClass();
unsigned short* Get_Index_Array() { return indices; }
};
static unsigned Get_Total_Buffer_Count();
static unsigned Get_Total_Allocated_Indices();
static unsigned Get_Total_Allocated_Memory();
protected:
mutable int engine_refs;
unsigned short index_count; // number of indices
unsigned type;
};
// HY 2/14/01
// Created
class DynamicIBAccessClass : public W3DMPO
{
W3DMPO_GLUE(DynamicIBAccessClass)
friend DX8Wrapper;
friend SortingRendererClass;
unsigned Type;
unsigned short IndexCount;
unsigned short IndexBufferOffset;
IndexBufferClass* IndexBuffer;
void Allocate_Sorting_Dynamic_Buffer();
void Allocate_DX8_Dynamic_Buffer();
public:
DynamicIBAccessClass(unsigned short type, unsigned short index_count);
~DynamicIBAccessClass();
unsigned Get_Type() const { return Type; }
unsigned short Get_Index_Count() const { return IndexCount; }
// Call at the end of the execution, or at whatever time you wish to release
// the recycled dynamic index buffer.
static void _Deinit();
static void _Reset(bool frame_changed);
static unsigned short Get_Default_Index_Count(void); ///<current size of dynamic index buffer
// To lock the index buffer, create instance of this write class locally.
// The buffer is automatically unlocked when you exit the scope.
class WriteLockClass
{
DynamicIBAccessClass* DynamicIBAccess;
unsigned short* Indices;
public:
WriteLockClass(DynamicIBAccessClass* ib_access);
~WriteLockClass();
unsigned short* Get_Index_Array() { return Indices; }
};
friend WriteLockClass;
};
/**
** DX8IndexBufferClass
** This class wraps a DX8 index buffer.
*/
class DX8IndexBufferClass : public IndexBufferClass
{
W3DMPO_GLUE(DX8IndexBufferClass)
friend IndexBufferClass::WriteLockClass;
friend IndexBufferClass::AppendLockClass;
public:
enum UsageType {
USAGE_DEFAULT=0,
USAGE_DYNAMIC=1,
USAGE_SOFTWAREPROCESSING=2,
USAGE_NPATCHES=4
};
DX8IndexBufferClass(unsigned short index_count,UsageType usage=USAGE_DEFAULT);
~DX8IndexBufferClass();
void Copy(unsigned int* indices,unsigned start_index,unsigned index_count);
void Copy(unsigned short* indices,unsigned start_index,unsigned index_count);
inline IDirect3DIndexBuffer8* Get_DX8_Index_Buffer() { return index_buffer; }
private:
IDirect3DIndexBuffer8* index_buffer; // actual dx8 index buffer
};
class SortingIndexBufferClass : public IndexBufferClass
{
W3DMPO_GLUE(SortingIndexBufferClass)
friend DX8Wrapper;
friend SortingRendererClass;
friend IndexBufferClass::WriteLockClass;
friend IndexBufferClass::AppendLockClass;
friend DynamicIBAccessClass::WriteLockClass;
public:
SortingIndexBufferClass(unsigned short index_count);
~SortingIndexBufferClass();
protected:
unsigned short* index_buffer;
};
extern int IndexBufferExceptionFunc(void);
#endif //DX8INDEXBUFFER_H

View File

@@ -0,0 +1,74 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8list.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 5 $*
* *
* 06/27/02 KM Texture class abstraction *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8LIST_H
#define DX8LIST_H
#include "always.h"
#include "multilist.h"
/*
** Here we're just typedefing some multi-lists so we don't have to write the
** long template names.
*/
class DX8TextureCategoryClass;
typedef MultiListClass<DX8TextureCategoryClass> TextureCategoryList;
typedef MultiListIterator<DX8TextureCategoryClass> TextureCategoryListIterator;
class DX8FVFCategoryContainer;
typedef MultiListClass<DX8FVFCategoryContainer> FVFCategoryList;
typedef MultiListIterator<DX8FVFCategoryContainer> FVFCategoryListIterator;
class DX8PolygonRendererClass;
typedef MultiListClass<DX8PolygonRendererClass> DX8PolygonRendererList;
typedef MultiListIterator<DX8PolygonRendererClass> DX8PolygonRendererListIterator;
class TextureTrackerClass;
typedef MultiListClass<TextureTrackerClass> TextureTrackerList;
typedef MultiListIterator<TextureTrackerClass> TextureTrackerListIterator;
#endif //DX8LIST_H

View File

@@ -0,0 +1,115 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8polygonrenderer.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 8/22/01 6:54p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "dx8polygonrenderer.h"
#include "dx8renderer.h"
// ----------------------------------------------------------------------------
DX8PolygonRendererClass::DX8PolygonRendererClass(
unsigned index_count_,
MeshModelClass* mmc_,
DX8TextureCategoryClass* tex_cat,
unsigned vertex_offset_,
unsigned index_offset_,
bool strip_,
unsigned pass_)
:
mmc(mmc_),
texture_category(tex_cat),
index_offset(index_offset_),
vertex_offset(vertex_offset_),
min_vertex_index(0),
vertex_index_range(0),
index_count(index_count_),
strip(strip_),
pass(pass_)
{
WWASSERT(index_count);
mmc->PolygonRendererList.Add_Tail(this);
}
DX8PolygonRendererClass::DX8PolygonRendererClass(const DX8PolygonRendererClass& src,MeshModelClass* mmc_)
:
mmc(mmc_),
texture_category(src.texture_category),
index_offset(src.index_offset),
vertex_offset(src.vertex_offset),
min_vertex_index(src.min_vertex_index),
vertex_index_range(src.vertex_index_range),
index_count(src.index_count),
strip(src.strip),
pass(src.pass)
{
mmc->PolygonRendererList.Add_Tail(this);
}
DX8PolygonRendererClass::~DX8PolygonRendererClass()
{
if (texture_category) texture_category->Remove_Polygon_Renderer(this);
}
// ----------------------------------------------------------------------------
void DX8PolygonRendererClass::Log()
{
StringClass work(true);
work.Format(" %8d %8d %6d %6d %6d %s\n",
index_count,
index_count/3,
index_offset,
min_vertex_index,
vertex_index_range,
mmc->Get_Name());
/* work.Format(
" Index count: %d (%d polys) i_offset: %d min_vi: %d vi_range: %d ident: %d (%s)\n",
index_count,
index_count/3,
index_offset,
min_vertex_index,
vertex_index_range,
mmc->ident,
mmc->Get_Name());
*/ WWDEBUG_SAY((work));
}

View File

@@ -0,0 +1,161 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8polygonrenderer.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 7/12/01 6:38p $*
* *
* $Revision:: 22 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8_POLYGON_RENDERER_H
#define DX8_POLYGON_RENDERER_H
#include "always.h"
#include "meshmdl.h"
#include "dx8list.h"
#include "sortingrenderer.h"
#include "mesh.h"
#include "dx8wrapper.h"
class DX8PolygonRendererClass;
class DX8TextureCategoryClass;
/**
** DX8PolygonRendererClass
** This is a record of a batch/range of polygons to be rendered. These hang off of the DX8TextureCategoryClass's
** and are rendered after the system installs a vertex buffer and textures in the DX8 wrapper.
*/
class DX8PolygonRendererClass : public MultiListObjectClass
{
MeshModelClass * mmc;
DX8TextureCategoryClass * texture_category;
unsigned index_offset; // absolute index of index 0 for our parent mesh
unsigned vertex_offset; // absolute index of vertex 0 for our parent mesh
unsigned index_count; // number of indices
unsigned min_vertex_index; // relative index of the first vertex our polys reference
unsigned vertex_index_range; // range to the last vertex our polys reference
bool strip; // is this a strip?
unsigned pass; // rendering pass
public:
DX8PolygonRendererClass(
unsigned index_count,
MeshModelClass* mmc_,
DX8TextureCategoryClass* tex_cat,
unsigned vertex_offset,
unsigned index_offset,
bool strip,
unsigned pass);
DX8PolygonRendererClass(const DX8PolygonRendererClass& src,MeshModelClass* mmc_);
~DX8PolygonRendererClass();
void Render(/*const Matrix3D & tm,*/int base_vertex_offset);
void Render_Sorted(/*const Matrix3D & tm,*/int base_vertex_offset,const SphereClass & bounding_sphere);
void Set_Vertex_Index_Range(unsigned min_vertex_index_,unsigned vertex_index_range_);
unsigned Get_Vertex_Offset(void) { return vertex_offset; }
unsigned Get_Index_Offset(void) { return index_offset; }
inline unsigned Get_Pass(void) { return pass; }
MeshModelClass* Get_Mesh_Model_Class() { return mmc; }
DX8TextureCategoryClass* Get_Texture_Category() { return texture_category; }
void Set_Texture_Category(DX8TextureCategoryClass* tc) { texture_category=tc; }
void Log();
};
// ----------------------------------------------------------------------------
inline void DX8PolygonRendererClass::Set_Vertex_Index_Range(unsigned min_vertex_index_,unsigned vertex_index_range_)
{
// WWDEBUG_SAY(("Set_Vertex_Index_Range - min: %d, range: %d\n",min_vertex_index_,vertex_index_range_));
// if (vertex_index_range_>30000) {
// int a=0;
// a++;
// }
min_vertex_index=min_vertex_index_;
vertex_index_range=vertex_index_range_;
}
// ----------------------------------------------------------------------------
inline void DX8PolygonRendererClass::Render(/*const Matrix3D & tm,*/int base_vertex_offset)
{
// DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
// SNAPSHOT_SAY(("Set_Transform\n"));
SNAPSHOT_SAY(("Set_Index_Buffer_Index_Offset(%d)\n",base_vertex_offset));
DX8Wrapper::Set_Index_Buffer_Index_Offset(base_vertex_offset);
if (strip) {
SNAPSHOT_SAY(("Draw_Strip(%d,%d,%d,%d)\n",index_offset,index_count-2,min_vertex_index,vertex_index_range));
DX8Wrapper::Draw_Strip(
index_offset,
index_count-2,
min_vertex_index,
vertex_index_range);
}
else {
SNAPSHOT_SAY(("Draw_Triangles(%d,%d,%d,%d)\n",index_offset,index_count-2,min_vertex_index,vertex_index_range));
DX8Wrapper::Draw_Triangles(
index_offset,
index_count/3,
min_vertex_index,
vertex_index_range);
}
}
inline void DX8PolygonRendererClass::Render_Sorted(/*const Matrix3D & tm,*/int base_vertex_offset,const SphereClass & bounding_sphere)
{
WWASSERT(!strip); // Strips can't be sorted for now
// DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
// SNAPSHOT_SAY(("Set_Transform\n"));
SNAPSHOT_SAY(("Set_Index_Buffer_Index_Offset(%d)\n",base_vertex_offset));
SNAPSHOT_SAY(("Insert_Sorting_Triangles(%d,%d,%d,%d)\n",index_offset,index_count-2,min_vertex_index,vertex_index_range));
DX8Wrapper::Set_Index_Buffer_Index_Offset(base_vertex_offset);
SortingRendererClass::Insert_Triangles(
bounding_sphere,
index_offset,
index_count/3,
min_vertex_index,
vertex_index_range);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8renderer.h $*
* *
* Original Author:: Jani Penttinen *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 29 $*
* *
* 06/27/02 KM Changes to max texture stage caps *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8_RENDERER_H
#define DX8_RENDERER_H
#include "always.h"
#include "wwstring.h"
#include "simplevec.h"
#include "refcount.h"
#include "vector.h"
#include "dx8list.h"
#include "shader.h"
#include "dx8wrapper.h"
#include "meshmatdesc.h"
class IndexBufferClass;
class VertexBufferClass;
class DX8RenderTypeArrayClass;
class MeshClass;
class MeshModelClass;
class DX8PolygonRendererClass;
class Vertex_Split_Table;
class DX8FVFCategoryContainer;
class DecalMeshClass;
class MaterialPassClass;
class MatPassTaskClass;
class PolyRenderTaskClass;
class TextureClass;
class VertexMaterialClass;
class CameraClass;
#define RECORD_RENDER(I,P) render_stats[I].count++; render_stats[I].polys+=P;
/**
** DX8TextureCategoryClass
** This class is used for each Material-Texture-Shader combination that is encountered during rendering.
** Each polygon_renderer that uses the same 'TextureCategory' will be linked to the 'TextureCategory' object.
** Then, all polygons will be rendered in 'TextureCategory' batches to reduce the number of stage changes
** (and most importantly, texture changes) that we cause in DX8.
*/
class DX8TextureCategoryClass : public MultiListObjectClass
{
int pass;
TextureClass * textures[MeshMatDescClass::MAX_TEX_STAGES];
ShaderClass shader;
VertexMaterialClass * material;
DX8PolygonRendererList PolygonRendererList;
DX8FVFCategoryContainer* container;
PolyRenderTaskClass * render_task_head; // polygon renderers queued for rendering
static bool m_gForceMultiply; // Forces opaque materials to use the multiply blend - pseudo transparent effect. jba.
public:
DX8TextureCategoryClass(DX8FVFCategoryContainer* container,TextureClass** textures, ShaderClass shd, VertexMaterialClass* mat,int pass);
~DX8TextureCategoryClass();
void Add_Render_Task(DX8PolygonRendererClass * p_renderer,MeshClass * p_mesh);
void Render(void);
bool Anything_To_Render() { return (render_task_head != NULL); }
void Clear_Render_List() { render_task_head = NULL; }
TextureClass * Peek_Texture(int stage) { return textures[stage]; }
const VertexMaterialClass * Peek_Material() { return material; }
ShaderClass Get_Shader() { return shader; }
DX8PolygonRendererList& Get_Polygon_Renderer_List() { return PolygonRendererList; }
unsigned Add_Mesh(
Vertex_Split_Table& split_buffer,
unsigned vertex_offset,
unsigned index_offset,
IndexBufferClass* index_buffer,
unsigned pass);
void Log(bool only_visible);
void Remove_Polygon_Renderer(DX8PolygonRendererClass* p_renderer);
void Add_Polygon_Renderer(DX8PolygonRendererClass* p_renderer,DX8PolygonRendererClass* add_after_this=NULL);
DX8FVFCategoryContainer * Get_Container(void) { return container; }
// Force multiply blend on all objects inserted from now on. (Doesn't affect the objects that are already in the lists)
static void SetForceMultiply(bool multiply) { m_gForceMultiply=multiply; }
};
// ----------------------------------------------------------------------------
/**
** DX8FVFCategoryContainer
*/
class DX8FVFCategoryContainer : public MultiListObjectClass
{
public:
enum {
MAX_PASSES=4
};
protected:
TextureCategoryList texture_category_list[MAX_PASSES];
TextureCategoryList visible_texture_category_list[MAX_PASSES];
MatPassTaskClass * visible_matpass_head;
MatPassTaskClass * visible_matpass_tail;
IndexBufferClass * index_buffer;
int used_indices;
unsigned FVF;
unsigned passes;
unsigned uv_coordinate_channels;
bool sorting;
bool AnythingToRender;
bool AnyDelayedPassesToRender;
void Generate_Texture_Categories(Vertex_Split_Table& split_table,unsigned vertex_offset);
void DX8FVFCategoryContainer::Insert_To_Texture_Category(
Vertex_Split_Table& split_table,
TextureClass** textures,
VertexMaterialClass* mat,
ShaderClass shader,
int pass,
unsigned vertex_offset);
inline bool Anything_To_Render() { return AnythingToRender; }
inline bool Any_Delayed_Passes_To_Render() { return AnyDelayedPassesToRender; }
void Render_Procedural_Material_Passes(void);
DX8TextureCategoryClass* Find_Matching_Texture_Category(
TextureClass* texture,
unsigned pass,
unsigned stage,
DX8TextureCategoryClass* ref_category);
DX8TextureCategoryClass* Find_Matching_Texture_Category(
VertexMaterialClass* vmat,
unsigned pass,
DX8TextureCategoryClass* ref_category);
public:
DX8FVFCategoryContainer(unsigned FVF,bool sorting);
virtual ~DX8FVFCategoryContainer();
static unsigned Define_FVF(MeshModelClass* mmc,bool enable_lighting);
bool Is_Sorting() const { return sorting; }
void Change_Polygon_Renderer_Texture(
DX8PolygonRendererList& polygon_renderer_list,
TextureClass* texture,
TextureClass* new_texture,
unsigned pass,
unsigned stage);
void Change_Polygon_Renderer_Material(
DX8PolygonRendererList& polygon_renderer_list,
VertexMaterialClass* vmat,
VertexMaterialClass* new_vmat,
unsigned pass);
void Remove_Texture_Category(DX8TextureCategoryClass* tex_category);
virtual void Render(void)=0;
virtual void Add_Mesh(MeshModelClass* mmc)=0;
virtual void Log(bool only_visible)=0;
virtual bool Check_If_Mesh_Fits(MeshModelClass* mmc)=0;
inline unsigned Get_FVF() const { return FVF; }
inline void Add_Visible_Texture_Category(DX8TextureCategoryClass * tex_category,int pass)
{
WWASSERT(pass<MAX_PASSES);
WWASSERT(tex_category != NULL);
WWASSERT(texture_category_list[pass].Contains(tex_category));
visible_texture_category_list[pass].Add(tex_category);
AnythingToRender=true;
}
/*
** Material pass rendering. The following two functions allow procedural material passes
** to be applied to meshes in this FVF category. In certain cases, the game will *only* render
** the procedural pass and not the base materials for the mesh. When this happens there can
** be rendering errors unless these procedural passes are rendered after all of the meshes in
** the scene. The virtual method Add_Delayed_Material_Pass is used in this case.
*/
void Add_Visible_Material_Pass(MaterialPassClass * pass,MeshClass * mesh);
virtual void Add_Delayed_Visible_Material_Pass(MaterialPassClass * pass, MeshClass * mesh) = 0;
virtual void Render_Delayed_Procedural_Material_Passes(void) = 0;
};
/**
** DX8RigidFVFCategoryContainer
** This is an FVFCategoryContainer for rigid (non-skin) meshes
*/
class DX8RigidFVFCategoryContainer : public DX8FVFCategoryContainer
{
public:
DX8RigidFVFCategoryContainer(unsigned FVF,bool sorting);
~DX8RigidFVFCategoryContainer();
void Add_Mesh(MeshModelClass* mmc);
void Log(bool only_visible);
bool Check_If_Mesh_Fits(MeshModelClass* mmc);
void Render(void); // Generic render function
/*
** This method adds a material pass which must be rendered after all of the other rendering is complete.
** This is needed whenever a mesh turns off its base passes and renders a translucent pass on its geometry.
*/
virtual void Add_Delayed_Visible_Material_Pass(MaterialPassClass * pass, MeshClass * mesh);
virtual void Render_Delayed_Procedural_Material_Passes(void);
protected:
VertexBufferClass * vertex_buffer;
int used_vertices;
MatPassTaskClass * delayed_matpass_head;
MatPassTaskClass * delayed_matpass_tail;
};
/**
** DX8SkinFVFCategoryContainer
** This is an FVFCategoryContainer for skin meshes
*/
class DX8SkinFVFCategoryContainer: public DX8FVFCategoryContainer
{
public:
DX8SkinFVFCategoryContainer(bool sorting);
~DX8SkinFVFCategoryContainer();
void Render(void);
void Add_Mesh(MeshModelClass* mmc);
void Log(bool only_visible);
bool Check_If_Mesh_Fits(MeshModelClass* mmc);
void Add_Visible_Skin(MeshClass * mesh);
/*
** Since skins are already rendered after the rigid meshes, the Add_Delayed_Material_Pass function simply
** routes into the Add_Visible_Material_Pass method and no extra overhead is added.
*/
virtual void Add_Delayed_Visible_Material_Pass(MaterialPassClass * pass, MeshClass * mesh) { Add_Visible_Material_Pass(pass,mesh); }
virtual void Render_Delayed_Procedural_Material_Passes(void) { }
private:
void Reset();
void clearVisibleSkinList();
unsigned int VisibleVertexCount;
MeshClass * VisibleSkinHead;
MeshClass * VisibleSkinTail;
};
/**
** DX8MeshRendererClass
** This object is controller for the entire DX8 mesh rendering system. It organizes mesh
** fragments into groups based on FVF, texture, and material. During rendering, a list of
** the visible mesh fragments is composed and rendered. There is a global instance of this
** class called TheDX8MeshRenderer that should be used for all mesh rendering.
*/
class DX8MeshRendererClass
{
public:
DX8MeshRendererClass();
~DX8MeshRendererClass();
void Init();
void Shutdown();
void Flush();
void Clear_Pending_Delete_Lists();
void Log_Statistics_String(bool only_visible);
static void Request_Log_Statistics();
void Register_Mesh_Type(MeshModelClass* mmc);
void Unregister_Mesh_Type(MeshModelClass* mmc);
void Set_Camera(CameraClass* cam) { camera=cam; }
CameraClass * Peek_Camera(void) { return camera; }
void Add_To_Render_List(DecalMeshClass * decalmesh);
// Enable or disable lighting on all objects inserted from now on. (Doesn't affect the objects that are already in the lists)
void Enable_Lighting(bool enable) { enable_lighting=enable; }
// This should be called at the beginning of a game or menu or after a major modifications to the scene...
void Invalidate(bool shutdown=false); // Added flag so it doesn't allocate more mem when shutting down. -MW
protected:
void Render_Decal_Meshes(void);
bool enable_lighting;
CameraClass * camera;
SimpleDynVecClass<FVFCategoryList *> texture_category_container_lists_rigid;
FVFCategoryList * texture_category_container_list_skin;
DecalMeshClass * visible_decal_meshes;
};
extern DX8MeshRendererClass TheDX8MeshRenderer;
#endif

View File

@@ -0,0 +1,132 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dx8rendererdebugger.h"
#include "hashtemplate.h"
#include "mesh.h"
#include "meshmdl.h"
static HashTemplateClass<unsigned, MeshClass*> MeshHash;
bool DX8RendererDebugger::Enabled;
void DX8RendererDebugger::Enable(bool enable)
{
Enabled=true;
}
void DX8RendererDebugger::Get_String(StringClass& s)
{
if (!Enabled) {
s="";
return;
}
s="\n\n\n\n";
int cnt=0;
HashTemplateIterator<unsigned,MeshClass*> ite(MeshHash);
for (ite.First();!ite.Is_Done();ite.Next()) {
StringClass tmp(0,true);
MeshClass* mesh=ite.Peek_Value();
MeshModelClass* mmc=mesh->Peek_Model();
int polys=0;
int verts=0;
if (mmc) {
polys=mmc->Get_Polygon_Count();
verts=mmc->Get_Vertex_Count();
}
tmp.Format("id: %5.5d mesh: %s %d polys, %d verts",
ite.Peek_Key(),
mesh->Get_Name(),
polys,
verts);
s+=tmp;
if (mesh->Is_Disabled_By_Debugger()) {
s+=" (disabled)\n";
}
else {
s+="\n";
}
cnt++;
if (cnt>20) break;
}
}
void DX8RendererDebugger::Update()
{
// Release references to all meshes and empty the hash
HashTemplateIterator<unsigned,MeshClass*> ite(MeshHash);
for (ite.First();!ite.Is_Done();ite.Next()) {
ite.Peek_Value()->Release_Ref();
}
MeshHash.Remove_All();
// if (!Enabled) return;
}
#ifdef WWDEBUG
void DX8RendererDebugger::Add_Mesh(MeshClass* mesh)
{
if (!Enabled) return;
// Don't insert the same mesh twice
if (MeshHash.Get(mesh->Get_Debug_Id())) return;
mesh->Add_Ref();
MeshHash.Insert(mesh->Get_Debug_Id(),mesh);
}
#endif
void DX8RendererDebugger::Disable_Mesh(unsigned id)
{
if (!Enabled) return;
MeshClass* mesh=MeshHash.Get(id);
if (!mesh) return;
mesh->Set_Debugger_Disable(true);
}
void DX8RendererDebugger::Enable_Mesh(unsigned id)
{
if (!Enabled) return;
MeshClass* mesh=MeshHash.Get(id);
if (!mesh) return;
mesh->Set_Debugger_Disable(false);
}
void DX8RendererDebugger::Disable_All()
{
if (!Enabled) return;
HashTemplateIterator<unsigned,MeshClass*> ite(MeshHash);
for (ite.First();!ite.Is_Done();ite.Next()) {
ite.Peek_Value()->Set_Debugger_Disable(true);
}
}
void DX8RendererDebugger::Enable_All()
{
if (!Enabled) return;
HashTemplateIterator<unsigned,MeshClass*> ite(MeshHash);
for (ite.First();!ite.Is_Done();ite.Next()) {
ite.Peek_Value()->Set_Debugger_Disable(false);
}
}

View File

@@ -0,0 +1,75 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8rendererdebugger.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/06/01 6:14p $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8_RENDERER_DEBUGGER_H
#define DX8_RENDERER_DEBUGGER_H
#include "always.h"
class StringClass;
class MeshClass;
// Note! For the debugger to be usable, the application must call DX8RendererDebugger::Update() once
// each frame.
class DX8RendererDebugger
{
static bool Enabled;
public:
static void Enable(bool enable);
WWINLINE static bool Is_Enabled() { return Enabled; }
static void Get_String(StringClass& s);
static void Update();
#ifdef WWDEBUG
static void Add_Mesh(MeshClass* mesh);
#else
static void Add_Mesh(MeshClass* mesh) {}
#endif
static void Disable_Mesh(unsigned id);
static void Enable_Mesh(unsigned id);
static void Disable_All();
static void Enable_All();
};
#endif

View File

@@ -0,0 +1,199 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : DX8 Texture Manager *
* *
* $Archive:: /Commando/Code/ww3d2/dx8texman.cpp $*
* *
* Original Author:: Hector Yee *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 4 $*
* *
* 06/27/02 KM Texture class abstraction *
*---------------------------------------------------------------------------------------------*
* Functions: *
* DX8TextureManagerClass::Shutdown -- Shuts down the texture manager *
* DX8TextureManagerClass::Add -- Adds a texture to be managed *
* DX8TextureManagerClass::Remove -- Removes a texture from being managed *
* DX8TextureManagerClass::Release_Textures -- Releases the internal d3d texture *
* DX8TextureManagerClass::Recreate_Textures -- Reallocates lost textures *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// This class manages textures that are in the default pool
// ensuring that they are released on device loss
// and created on device reset
// Note: It does NOT addref to textures because it is called in the texture
// destructor
#include "dx8texman.h"
TextureTrackerList DX8TextureManagerClass::Managed_Textures;
/***********************************************************************************************
* DX8TextureManagerClass::Shutdown -- Shuts down the texture manager *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 hy : Created. *
* 5/16/2002 km : Added depth stencil texture tracking and abstraction *
*=============================================================================================*/
void DX8TextureManagerClass::Shutdown()
{
while (!Managed_Textures.Is_Empty())
{
TextureTrackerClass *track=Managed_Textures.Remove_Head();
delete track;
track=NULL;
}
}
/***********************************************************************************************
* DX8TextureManagerClass::Add -- Adds a texture to be managed *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 hy : Created. *
* 5/16/2002 km : Added depth stencil texture tracking and abstraction *
*=============================================================================================*/
void DX8TextureManagerClass::Add(TextureTrackerClass *track)
{
// this function should only be called by the texture constructor
Managed_Textures.Add(track);
}
/***********************************************************************************************
* DX8TextureManagerClass::Remove -- Removes a texture from being managed *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 hy : Created. *
* 5/16/2002 km : Added depth stencil texture tracking and abstraction *
*=============================================================================================*/
void DX8TextureManagerClass::Remove(TextureBaseClass *tex)
{
// this function should only be called by the texture destructor
TextureTrackerListIterator it(&Managed_Textures);
while (!it.Is_Done())
{
TextureTrackerClass *track=it.Peek_Obj();
if (track->Get_Texture()==tex)
{
it.Remove_Current_Object();
delete track;
break;
}
it.Next();
}
}
/***********************************************************************************************
* DX8TextureManagerClass::Release_Textures -- Releases the internal d3d texture *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 hy : Created. *
* 5/16/2002 km : Added depth stencil texture tracking and abstraction *
*=============================================================================================*/
void DX8TextureManagerClass::Release_Textures()
{
TextureTrackerListIterator it(&Managed_Textures);
while (!it.Is_Done())
{
TextureTrackerClass *track=it.Peek_Obj();
track->Release();
it.Next();
}
}
/***********************************************************************************************
* DX8TextureManagerClass::Recreate_Textures -- Reallocates lost textures *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 hy : Created. *
* 5/16/2002 km : Added depth stencil texture tracking and abstraction *
*=============================================================================================*/
void DX8TextureManagerClass::Recreate_Textures()
{
TextureTrackerListIterator it(&Managed_Textures);
while (!it.Is_Done())
{
TextureTrackerClass *track=it.Peek_Obj();
track->Recreate();
track->Get_Texture()->Set_Dirty();
it.Next();
}
}

View File

@@ -0,0 +1,179 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : DX8 Texture Manager *
* *
* $Archive:: /Commando/Code/ww3d2/dx8texman.h $*
* *
* Original Author:: Hector Yee *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 4 $*
* *
* 06/27/02 KM Texture class abstraction *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8TEXTUREMANAGER_H
#define DX8TEXTUREMANAGER_H
#include "always.h"
#include "texture.h"
#include "dx8wrapper.h"
#include "ww3dformat.h"
#include "dx8list.h"
#include "ww3dformat.h"
#include "multilist.h"
class DX8TextureManagerClass;
class TextureTrackerClass : public MultiListObjectClass
{
public:
TextureTrackerClass
(
unsigned int w,
unsigned int h,
MipCountType count,
TextureBaseClass *tex
)
: Width(w),
Height(h),
Mip_level_count(count),
Texture(tex)
{
}
virtual void Recreate() const =0;
void Release()
{
Texture->Set_D3D_Base_Texture(NULL);
}
TextureBaseClass* Get_Texture() const { return Texture; }
protected:
unsigned int Width;
unsigned int Height;
MipCountType Mip_level_count;
TextureBaseClass *Texture;
};
class DX8TextureTrackerClass : public TextureTrackerClass
{
public:
DX8TextureTrackerClass
(
unsigned int w,
unsigned int h,
WW3DFormat format,
MipCountType count,
TextureBaseClass *tex,
bool rt
)
: TextureTrackerClass(w,h,count,tex), Format(format), RenderTarget(rt)
{
}
virtual void Recreate() const
{
WWASSERT(Texture->Peek_D3D_Base_Texture()==NULL);
Texture->Poke_Texture
(
DX8Wrapper::_Create_DX8_Texture
(
Width,
Height,
Format,
Mip_level_count,
D3DPOOL_DEFAULT,
RenderTarget
)
);
}
private:
WW3DFormat Format;
bool RenderTarget;
};
class DX8ZTextureTrackerClass : public TextureTrackerClass
{
public:
DX8ZTextureTrackerClass
(
unsigned int w,
unsigned int h,
WW3DZFormat zformat,
MipCountType count,
TextureBaseClass* tex
)
: TextureTrackerClass(w,h,count,tex), ZFormat(zformat)
{
}
virtual void Recreate() const
{
WWASSERT(Texture->Peek_D3D_Base_Texture()==NULL);
Texture->Poke_Texture
(
DX8Wrapper::_Create_DX8_ZTexture
(
Width,
Height,
ZFormat,
Mip_level_count,
D3DPOOL_DEFAULT
)
);
}
private:
WW3DZFormat ZFormat;
};
class DX8TextureManagerClass
{
public:
static void Shutdown();
static void Add(TextureTrackerClass *track);
static void Remove(TextureBaseClass *tex);
static void Release_Textures();
static void Recreate_Textures();
private:
static TextureTrackerList Managed_Textures;
};
#endif // ifdef TEXTUREMANAGER

View File

@@ -0,0 +1,910 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/dx8vertexbuffer.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 5:06p $*
* *
* $Revision:: 39 $*
* *
* 06/26/02 KM VB Vertex format size update for shaders *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//#define VERTEX_BUFFER_LOG
#include "dx8vertexbuffer.h"
#include "dx8wrapper.h"
#include "dx8fvf.h"
#include "dx8caps.h"
#include "thread.h"
#include "wwmemlog.h"
#include <D3dx8core.h>
#define DEFAULT_VB_SIZE 5000
static bool _DynamicSortingVertexArrayInUse=false;
//static VertexFormatXYZNDUV2* _DynamicSortingVertexArray=NULL;
static SortingVertexBufferClass* _DynamicSortingVertexArray=NULL;
static unsigned short _DynamicSortingVertexArraySize=0;
static unsigned short _DynamicSortingVertexArrayOffset=0;
static bool _DynamicDX8VertexBufferInUse=false;
static DX8VertexBufferClass* _DynamicDX8VertexBuffer=NULL;
static unsigned short _DynamicDX8VertexBufferSize=DEFAULT_VB_SIZE;
static unsigned short _DynamicDX8VertexBufferOffset=0;
static const FVFInfoClass _DynamicFVFInfo(dynamic_fvf_type);
static int _DX8VertexBufferCount=0;
static int _VertexBufferCount;
static int _VertexBufferTotalVertices;
static int _VertexBufferTotalSize;
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
VertexBufferClass::VertexBufferClass(unsigned type_, unsigned FVF, unsigned short vertex_count_, unsigned vertex_size)
:
VertexCount(vertex_count_),
type(type_),
engine_refs(0)
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(VertexCount);
WWASSERT(type==BUFFER_TYPE_DX8 || type==BUFFER_TYPE_SORTING);
WWASSERT((FVF!=0 && vertex_size==0) || (FVF==0 && vertex_size!=0));
fvf_info=W3DNEW FVFInfoClass(FVF,vertex_size);
_VertexBufferCount++;
_VertexBufferTotalVertices+=VertexCount;
_VertexBufferTotalSize+=VertexCount*fvf_info->Get_FVF_Size();
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("New VB, %d vertices, size %d bytes\n",VertexCount,VertexCount*fvf_info->Get_FVF_Size()));
WWDEBUG_SAY(("Total VB count: %d, total %d vertices, total size %d bytes\n",
_VertexBufferCount,
_VertexBufferTotalVertices,
_VertexBufferTotalSize));
#endif
}
// ----------------------------------------------------------------------------
VertexBufferClass::~VertexBufferClass()
{
_VertexBufferCount--;
_VertexBufferTotalVertices-=VertexCount;
_VertexBufferTotalSize-=VertexCount*fvf_info->Get_FVF_Size();
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("Delete VB, %d vertices, size %d bytes\n",VertexCount,VertexCount*fvf_info->Get_FVF_Size()));
WWDEBUG_SAY(("Total VB count: %d, total %d vertices, total size %d bytes\n",
_VertexBufferCount,
_VertexBufferTotalVertices,
_VertexBufferTotalSize));
#endif
delete fvf_info;
}
unsigned VertexBufferClass::Get_Total_Buffer_Count()
{
return _VertexBufferCount;
}
unsigned VertexBufferClass::Get_Total_Allocated_Vertices()
{
return _VertexBufferTotalVertices;
}
unsigned VertexBufferClass::Get_Total_Allocated_Memory()
{
return _VertexBufferTotalSize;
}
// ----------------------------------------------------------------------------
void VertexBufferClass::Add_Engine_Ref() const
{
engine_refs++;
}
// ----------------------------------------------------------------------------
void VertexBufferClass::Release_Engine_Ref() const
{
engine_refs--;
WWASSERT(engine_refs>=0);
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
VertexBufferClass::WriteLockClass::WriteLockClass(VertexBufferClass* VertexBuffer, int flags)
:
VertexBufferLockClass(VertexBuffer)
{
DX8_THREAD_ASSERT();
WWASSERT(VertexBuffer);
WWASSERT(!VertexBuffer->Engine_Refs());
VertexBuffer->Add_Ref();
switch (VertexBuffer->Type()) {
case BUFFER_TYPE_DX8:
#ifdef VERTEX_BUFFER_LOG
{
StringClass fvf_name;
VertexBuffer->FVF_Info().Get_FVF_Name(fvf_name);
WWDEBUG_SAY(("VertexBuffer->Lock(start_index: 0, index_range: 0(%d), fvf_size: %d, fvf: %s)\n",
VertexBuffer->Get_Vertex_Count(),
VertexBuffer->FVF_Info().Get_FVF_Size(),
fvf_name));
}
#endif
DX8_Assert();
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(
0,
0,
(unsigned char**)&Vertices,
flags)); //flags
break;
case BUFFER_TYPE_SORTING:
Vertices=static_cast<SortingVertexBufferClass*>(VertexBuffer)->VertexBuffer;
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
VertexBufferClass::WriteLockClass::~WriteLockClass()
{
DX8_THREAD_ASSERT();
switch (VertexBuffer->Type()) {
case BUFFER_TYPE_DX8:
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("VertexBuffer->Unlock()\n"));
#endif
DX8_Assert();
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock());
break;
case BUFFER_TYPE_SORTING:
break;
default:
WWASSERT(0);
break;
}
VertexBuffer->Release_Ref();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
VertexBufferClass::AppendLockClass::AppendLockClass(VertexBufferClass* VertexBuffer,unsigned start_index, unsigned index_range)
:
VertexBufferLockClass(VertexBuffer)
{
DX8_THREAD_ASSERT();
WWASSERT(VertexBuffer);
WWASSERT(!VertexBuffer->Engine_Refs());
WWASSERT(start_index+index_range<=VertexBuffer->Get_Vertex_Count());
VertexBuffer->Add_Ref();
switch (VertexBuffer->Type()) {
case BUFFER_TYPE_DX8:
#ifdef VERTEX_BUFFER_LOG
{
StringClass fvf_name;
VertexBuffer->FVF_Info().Get_FVF_Name(fvf_name);
WWDEBUG_SAY(("VertexBuffer->Lock(start_index: %d, index_range: %d, fvf_size: %d, fvf: %s)\n",
start_index,
index_range,
VertexBuffer->FVF_Info().Get_FVF_Size(),
fvf_name));
}
#endif
DX8_Assert();
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(
start_index*VertexBuffer->FVF_Info().Get_FVF_Size(),
index_range*VertexBuffer->FVF_Info().Get_FVF_Size(),
(unsigned char**)&Vertices,
0)); // Default (no) flags
break;
case BUFFER_TYPE_SORTING:
Vertices=static_cast<SortingVertexBufferClass*>(VertexBuffer)->VertexBuffer+start_index;
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
VertexBufferClass::AppendLockClass::~AppendLockClass()
{
DX8_THREAD_ASSERT();
switch (VertexBuffer->Type()) {
case BUFFER_TYPE_DX8:
DX8_Assert();
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("VertexBuffer->Unlock()\n"));
#endif
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock());
break;
case BUFFER_TYPE_SORTING:
break;
default:
WWASSERT(0);
break;
}
VertexBuffer->Release_Ref();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
SortingVertexBufferClass::SortingVertexBufferClass(unsigned short VertexCount)
:
VertexBufferClass(BUFFER_TYPE_SORTING, dynamic_fvf_type, VertexCount)
{
WWMEMLOG(MEM_RENDERER);
VertexBuffer=W3DNEWARRAY VertexFormatXYZNDUV2[VertexCount];
}
// ----------------------------------------------------------------------------
SortingVertexBufferClass::~SortingVertexBufferClass()
{
delete[] VertexBuffer;
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
// bool dynamic=false,bool softwarevp=false);
DX8VertexBufferClass::DX8VertexBufferClass(unsigned FVF, unsigned short vertex_count_, UsageType usage, unsigned vertex_size)
:
VertexBufferClass(BUFFER_TYPE_DX8, FVF, vertex_count_, vertex_size),
VertexBuffer(NULL)
{
Create_Vertex_Buffer(usage);
}
// ----------------------------------------------------------------------------
DX8VertexBufferClass::DX8VertexBufferClass(
const Vector3* vertices,
const Vector3* normals,
const Vector2* tex_coords,
unsigned short VertexCount,
UsageType usage)
:
VertexBufferClass(BUFFER_TYPE_DX8, D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_NORMAL, VertexCount),
VertexBuffer(NULL)
{
WWASSERT(vertices);
WWASSERT(normals);
WWASSERT(tex_coords);
Create_Vertex_Buffer(usage);
Copy(vertices,normals,tex_coords,0,VertexCount);
}
// ----------------------------------------------------------------------------
DX8VertexBufferClass::DX8VertexBufferClass(
const Vector3* vertices,
const Vector3* normals,
const Vector4* diffuse,
const Vector2* tex_coords,
unsigned short VertexCount,
UsageType usage)
:
VertexBufferClass(BUFFER_TYPE_DX8, D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_NORMAL|D3DFVF_DIFFUSE, VertexCount),
VertexBuffer(NULL)
{
WWASSERT(vertices);
WWASSERT(normals);
WWASSERT(tex_coords);
WWASSERT(diffuse);
Create_Vertex_Buffer(usage);
Copy(vertices,normals,tex_coords,diffuse,0,VertexCount);
}
// ----------------------------------------------------------------------------
DX8VertexBufferClass::DX8VertexBufferClass(
const Vector3* vertices,
const Vector4* diffuse,
const Vector2* tex_coords,
unsigned short VertexCount,
UsageType usage)
:
VertexBufferClass(BUFFER_TYPE_DX8, D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_DIFFUSE, VertexCount),
VertexBuffer(NULL)
{
WWASSERT(vertices);
WWASSERT(tex_coords);
WWASSERT(diffuse);
Create_Vertex_Buffer(usage);
Copy(vertices,tex_coords,diffuse,0,VertexCount);
}
// ----------------------------------------------------------------------------
DX8VertexBufferClass::DX8VertexBufferClass(
const Vector3* vertices,
const Vector2* tex_coords,
unsigned short VertexCount,
UsageType usage)
:
VertexBufferClass(BUFFER_TYPE_DX8, D3DFVF_XYZ|D3DFVF_TEX1, VertexCount),
VertexBuffer(NULL)
{
WWASSERT(vertices);
WWASSERT(tex_coords);
Create_Vertex_Buffer(usage);
Copy(vertices,tex_coords,0,VertexCount);
}
// ----------------------------------------------------------------------------
DX8VertexBufferClass::~DX8VertexBufferClass()
{
#ifdef VERTEX_BUFFER_LOG
WWDEBUG_SAY(("VertexBuffer->Release()\n"));
_DX8VertexBufferCount--;
WWDEBUG_SAY(("Current vertex buffer count: %d\n",_DX8VertexBufferCount));
#endif
VertexBuffer->Release();
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Create_Vertex_Buffer(UsageType usage)
{
DX8_THREAD_ASSERT();
WWASSERT(!VertexBuffer);
#ifdef VERTEX_BUFFER_LOG
StringClass fvf_name;
FVF_Info().Get_FVF_Name(fvf_name);
WWDEBUG_SAY(("CreateVertexBuffer(fvfsize=%d, vertex_count=%d, D3DUSAGE_WRITEONLY|%s|%s, fvf: %s, %s)\n",
FVF_Info().Get_FVF_Size(),
VertexCount,
(usage&USAGE_DYNAMIC) ? "D3DUSAGE_DYNAMIC" : "-",
(usage&USAGE_SOFTWAREPROCESSING) ? "D3DUSAGE_SOFTWAREPROCESSING" : "-",
fvf_name,
(usage&USAGE_DYNAMIC) ? "D3DPOOL_DEFAULT" : "D3DPOOL_MANAGED"));
_DX8VertexBufferCount++;
WWDEBUG_SAY(("Current vertex buffer count: %d\n",_DX8VertexBufferCount));
#endif
unsigned usage_flags=
D3DUSAGE_WRITEONLY|
((usage&USAGE_DYNAMIC) ? D3DUSAGE_DYNAMIC : 0)|
((usage&USAGE_NPATCHES) ? D3DUSAGE_NPATCHES : 0)|
((usage&USAGE_SOFTWAREPROCESSING) ? D3DUSAGE_SOFTWAREPROCESSING : 0);
if (!DX8Wrapper::Get_Current_Caps()->Support_TnL()) {
usage_flags|=D3DUSAGE_SOFTWAREPROCESSING;
}
// New Code
if (!DX8Wrapper::Get_Current_Caps()->Support_TnL()) {
usage_flags|=D3DUSAGE_SOFTWAREPROCESSING;
}
HRESULT ret=DX8Wrapper::_Get_D3D_Device8()->CreateVertexBuffer(
FVF_Info().Get_FVF_Size()*VertexCount,
usage_flags,
FVF_Info().Get_FVF(),
(usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
&VertexBuffer);
if (SUCCEEDED(ret)) {
return;
}
WWDEBUG_SAY(("Vertex buffer creation failed, trying to release assets...\n"));
// Vertex buffer creation failed, so try releasing least used textures and flushing the mesh cache.
// Free all textures that haven't been used in the last 5 seconds
TextureClass::Invalidate_Old_Unused_Textures(5000);
// Invalidate the mesh cache
WW3D::_Invalidate_Mesh_Cache();
//@todo: Find some way to invalidate the textures too
ret = DX8Wrapper::_Get_D3D_Device8()->ResourceManagerDiscardBytes(0);
// Try again...
ret=DX8Wrapper::_Get_D3D_Device8()->CreateVertexBuffer(
FVF_Info().Get_FVF_Size()*VertexCount,
usage_flags,
FVF_Info().Get_FVF(),
(usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
&VertexBuffer);
if (SUCCEEDED(ret)) {
WWDEBUG_SAY(("...Vertex buffer creation succesful\n"));
}
// If it still fails it is fatal
DX8_ErrorCode(ret);
/* Old Code
DX8CALL(CreateVertexBuffer(
FVF_Info().Get_FVF_Size()*VertexCount,
usage_flags,
FVF_Info().Get_FVF(),
(usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED,
&VertexBuffer));
*/
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, const Vector3* norm, const Vector2* uv, unsigned first_vertex,unsigned count)
{
WWASSERT(loc);
WWASSERT(norm);
WWASSERT(uv);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZNUV1);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZNUV1* verts=(VertexFormatXYZNUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZNUV1* verts=(VertexFormatXYZNUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
}
}
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, unsigned first_vertex, unsigned count)
{
WWASSERT(loc);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZ);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZ* verts=(VertexFormatXYZ*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZ* verts=(VertexFormatXYZ*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
}
}
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, const Vector2* uv, unsigned first_vertex, unsigned count)
{
WWASSERT(loc);
WWASSERT(uv);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZUV1);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZUV1* verts=(VertexFormatXYZUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZUV1* verts=(VertexFormatXYZUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
}
}
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, const Vector3* norm, unsigned first_vertex, unsigned count)
{
WWASSERT(loc);
WWASSERT(norm);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZN);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZN* verts=(VertexFormatXYZN*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZN* verts=(VertexFormatXYZN*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
}
}
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, const Vector3* norm, const Vector2* uv, const Vector4* diffuse, unsigned first_vertex, unsigned count)
{
WWASSERT(loc);
WWASSERT(norm);
WWASSERT(uv);
WWASSERT(diffuse);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZNDUV1);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZNDUV1* verts=(VertexFormatXYZNDUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
verts[v].diffuse=DX8Wrapper::Convert_Color(diffuse[v]);
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZNDUV1* verts=(VertexFormatXYZNDUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].nx=(*norm)[0];
verts[v].ny=(*norm)[1];
verts[v].nz=(*norm++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
verts[v].diffuse=DX8Wrapper::Convert_Color(diffuse[v]);
}
}
}
// ----------------------------------------------------------------------------
void DX8VertexBufferClass::Copy(const Vector3* loc, const Vector2* uv, const Vector4* diffuse, unsigned first_vertex, unsigned count)
{
WWASSERT(loc);
WWASSERT(uv);
WWASSERT(diffuse);
WWASSERT(count<=VertexCount);
WWASSERT(FVF_Info().Get_FVF()==DX8_FVF_XYZDUV1);
if (first_vertex) {
VertexBufferClass::AppendLockClass l(this,first_vertex,count);
VertexFormatXYZDUV1* verts=(VertexFormatXYZDUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
verts[v].diffuse=DX8Wrapper::Convert_Color(diffuse[v]);
}
}
else {
VertexBufferClass::WriteLockClass l(this);
VertexFormatXYZDUV1* verts=(VertexFormatXYZDUV1*)l.Get_Vertex_Array();
for (unsigned v=0;v<count;++v) {
verts[v].x=(*loc)[0];
verts[v].y=(*loc)[1];
verts[v].z=(*loc++)[2];
verts[v].u1=(*uv)[0];
verts[v].v1=(*uv++)[1];
verts[v].diffuse=DX8Wrapper::Convert_Color(diffuse[v]);
}
}
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
DynamicVBAccessClass::DynamicVBAccessClass(unsigned t,unsigned fvf,unsigned short vertex_count_)
:
Type(t),
FVFInfo(_DynamicFVFInfo),
VertexCount(vertex_count_),
VertexBuffer(0)
{
WWASSERT(fvf==dynamic_fvf_type);
WWASSERT(Type==BUFFER_TYPE_DYNAMIC_DX8 || Type==BUFFER_TYPE_DYNAMIC_SORTING);
if (Type==BUFFER_TYPE_DYNAMIC_DX8) {
Allocate_DX8_Dynamic_Buffer();
}
else {
Allocate_Sorting_Dynamic_Buffer();
}
}
DynamicVBAccessClass::~DynamicVBAccessClass()
{
if (Type==BUFFER_TYPE_DYNAMIC_DX8) {
_DynamicDX8VertexBufferInUse=false;
_DynamicDX8VertexBufferOffset+=(unsigned) VertexCount;
}
else {
_DynamicSortingVertexArrayInUse=false;
_DynamicSortingVertexArrayOffset+=VertexCount;
}
REF_PTR_RELEASE (VertexBuffer);
}
// ----------------------------------------------------------------------------
void DynamicVBAccessClass::_Deinit()
{
WWASSERT ((_DynamicDX8VertexBuffer == NULL) || (_DynamicDX8VertexBuffer->Num_Refs() == 1));
REF_PTR_RELEASE(_DynamicDX8VertexBuffer);
_DynamicDX8VertexBufferInUse=false;
_DynamicDX8VertexBufferSize=DEFAULT_VB_SIZE;
_DynamicDX8VertexBufferOffset=0;
WWASSERT ((_DynamicSortingVertexArray == NULL) || (_DynamicSortingVertexArray->Num_Refs() == 1));
REF_PTR_RELEASE(_DynamicSortingVertexArray);
WWASSERT(!_DynamicSortingVertexArrayInUse);
_DynamicSortingVertexArrayInUse=false;
_DynamicSortingVertexArraySize=0;
_DynamicSortingVertexArrayOffset=0;
}
void DynamicVBAccessClass::Allocate_DX8_Dynamic_Buffer()
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(!_DynamicDX8VertexBufferInUse);
_DynamicDX8VertexBufferInUse=true;
// If requesting more vertices than dynamic vertex buffer can fit, delete the vb
// and adjust the size to the new count.
if (VertexCount>_DynamicDX8VertexBufferSize) {
REF_PTR_RELEASE(_DynamicDX8VertexBuffer);
_DynamicDX8VertexBufferSize=VertexCount;
if (_DynamicDX8VertexBufferSize<DEFAULT_VB_SIZE) _DynamicDX8VertexBufferSize=DEFAULT_VB_SIZE;
}
// Create a new vb if one doesn't exist currently
if (!_DynamicDX8VertexBuffer) {
unsigned usage=DX8VertexBufferClass::USAGE_DYNAMIC;
if (DX8Wrapper::Get_Current_Caps()->Support_NPatches()) {
usage|=DX8VertexBufferClass::USAGE_NPATCHES;
}
_DynamicDX8VertexBuffer=NEW_REF(DX8VertexBufferClass,(
dynamic_fvf_type,
_DynamicDX8VertexBufferSize,
(DX8VertexBufferClass::UsageType)usage));
_DynamicDX8VertexBufferOffset=0;
}
// Any room at the end of the buffer?
if (((unsigned)VertexCount+_DynamicDX8VertexBufferOffset)>_DynamicDX8VertexBufferSize) {
_DynamicDX8VertexBufferOffset=0;
}
REF_PTR_SET(VertexBuffer,_DynamicDX8VertexBuffer);
VertexBufferOffset=_DynamicDX8VertexBufferOffset;
}
void DynamicVBAccessClass::Allocate_Sorting_Dynamic_Buffer()
{
WWMEMLOG(MEM_RENDERER);
WWASSERT(!_DynamicSortingVertexArrayInUse);
_DynamicSortingVertexArrayInUse=true;
unsigned new_vertex_count=_DynamicSortingVertexArrayOffset+VertexCount;
WWASSERT(new_vertex_count<65536);
if (new_vertex_count>_DynamicSortingVertexArraySize) {
REF_PTR_RELEASE(_DynamicSortingVertexArray);
_DynamicSortingVertexArraySize=new_vertex_count;
if (_DynamicSortingVertexArraySize<DEFAULT_VB_SIZE) _DynamicSortingVertexArraySize=DEFAULT_VB_SIZE;
}
if (!_DynamicSortingVertexArray) {
_DynamicSortingVertexArray=NEW_REF(SortingVertexBufferClass,(_DynamicSortingVertexArraySize));
_DynamicSortingVertexArrayOffset=0;
}
REF_PTR_SET(VertexBuffer,_DynamicSortingVertexArray);
VertexBufferOffset=_DynamicSortingVertexArrayOffset;
}
// ----------------------------------------------------------------------------
static int dx8_lock;
DynamicVBAccessClass::WriteLockClass::WriteLockClass(DynamicVBAccessClass* dynamic_vb_access_)
:
DynamicVBAccess(dynamic_vb_access_)
{
DX8_THREAD_ASSERT();
switch (DynamicVBAccess->Get_Type()) {
case BUFFER_TYPE_DYNAMIC_DX8:
#ifdef VERTEX_BUFFER_LOG
/* {
WWASSERT(!dx8_lock);
dx8_lock++;
StringClass fvf_name;
DynamicVBAccess->VertexBuffer->FVF_Info().Get_FVF_Name(fvf_name);
WWDEBUG_SAY(("DynamicVertexBuffer->Lock(start_index: %d, index_range: %d, fvf_size: %d, fvf: %s)\n",
DynamicVBAccess->VertexBufferOffset,
DynamicVBAccess->Get_Vertex_Count(),
DynamicVBAccess->VertexBuffer->FVF_Info().Get_FVF_Size(),
fvf_name));
}
*/
#endif
WWASSERT(_DynamicDX8VertexBuffer);
// WWASSERT(!_DynamicDX8VertexBuffer->Engine_Refs());
DX8_Assert();
// Lock with discard contents if the buffer offset is zero
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(DynamicVBAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Lock(
DynamicVBAccess->VertexBufferOffset*_DynamicDX8VertexBuffer->FVF_Info().Get_FVF_Size(),
DynamicVBAccess->Get_Vertex_Count()*DynamicVBAccess->VertexBuffer->FVF_Info().Get_FVF_Size(),
(unsigned char**)&Vertices,
D3DLOCK_NOSYSLOCK | (!DynamicVBAccess->VertexBufferOffset ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE)));
break;
case BUFFER_TYPE_DYNAMIC_SORTING:
Vertices=static_cast<SortingVertexBufferClass*>(DynamicVBAccess->VertexBuffer)->VertexBuffer;
Vertices+=DynamicVBAccess->VertexBufferOffset;
// vertices=_DynamicSortingVertexArray+_DynamicSortingVertexArrayOffset;
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
DynamicVBAccessClass::WriteLockClass::~WriteLockClass()
{
DX8_THREAD_ASSERT();
switch (DynamicVBAccess->Get_Type()) {
case BUFFER_TYPE_DYNAMIC_DX8:
#ifdef VERTEX_BUFFER_LOG
/* dx8_lock--;
WWASSERT(!dx8_lock);
WWDEBUG_SAY(("DynamicVertexBuffer->Unlock()\n"));
*/
#endif
DX8_Assert();
DX8_ErrorCode(static_cast<DX8VertexBufferClass*>(DynamicVBAccess->VertexBuffer)->Get_DX8_Vertex_Buffer()->Unlock());
break;
case BUFFER_TYPE_DYNAMIC_SORTING:
break;
default:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
void DynamicVBAccessClass::_Reset(bool frame_changed)
{
_DynamicSortingVertexArrayOffset=0;
if (frame_changed) _DynamicDX8VertexBufferOffset=0;
}
unsigned short DynamicVBAccessClass::Get_Default_Vertex_Count(void)
{
return _DynamicDX8VertexBufferSize;
}

View File

@@ -0,0 +1,265 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/dx8vertexbuffer.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 5:06p $*
* *
* $Revision:: 26 $*
* *
* 06/26/02 KM VB Vertex format size update for shaders *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DX8VERTEXBUFFER_H
#define DX8VERTEXBUFFER_H
#include "always.h"
#include "wwdebug.h"
#include "refcount.h"
#include "dx8fvf.h"
const unsigned dynamic_fvf_type=D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2|D3DFVF_DIFFUSE;
class DX8Wrapper;
class SortingRendererClass;
class Vector2;
class Vector3;
class Vector4;
class StringClass;
class DX8VertexBufferClass;
class FVFInfoClass;
struct IDirect3DVertexBuffer8;
class VertexBufferClass;
struct VertexFormatXYZNDUV2;
class VertexBufferLockClass
{
protected:
VertexBufferClass* VertexBuffer;
void* Vertices;
// This class can't be used directly, so constructor as to be protected
VertexBufferLockClass(VertexBufferClass* vertex_buffer_) : VertexBuffer(vertex_buffer_) {}
public:
void* Get_Vertex_Array() { return Vertices; }
};
/**
** DX8VertexBufferClass
** This class wraps a DX8 vertex buffer. Use the lock objects to modify or append to the vertex buffer.
*/
class VertexBufferClass : public W3DMPO, public RefCountClass
{
// nope, an ABC
//W3DMPO_GLUE(VertexBufferClass)
protected:
VertexBufferClass(unsigned type, unsigned FVF, unsigned short VertexCount, unsigned vertex_size=0);
virtual ~VertexBufferClass();
public:
inline const FVFInfoClass& FVF_Info() const { return *fvf_info; }
inline unsigned short Get_Vertex_Count() const { return VertexCount; }
inline unsigned Type() const { return type; }
void Add_Engine_Ref() const;
void Release_Engine_Ref() const;
inline unsigned Engine_Refs() const { return engine_refs; }
class WriteLockClass : public VertexBufferLockClass
{
public:
WriteLockClass(VertexBufferClass* vertex_buffer, int flags=0);
~WriteLockClass();
};
class AppendLockClass : public VertexBufferLockClass
{
public:
AppendLockClass(VertexBufferClass* vertex_buffer,unsigned start_index, unsigned index_range);
~AppendLockClass();
};
static unsigned Get_Total_Buffer_Count();
static unsigned Get_Total_Allocated_Vertices();
static unsigned Get_Total_Allocated_Memory();
protected:
unsigned type;
unsigned short VertexCount;
mutable int engine_refs;
FVFInfoClass* fvf_info;
};
/**
** Dynamic vertex buffer access is a wrapper to a single cycled dynamic vertex
** buffer.
** DynamicVBAccess gains an access to the dynamic vertex buffer and only
** only of these are allowed at any one time.
**
** The dynamic fvf buffers are always of the same type.
**
** NOTE: Dynamic vertex buffers accessors should only be used locally!
**
*/
class DynamicVBAccessClass
{
friend DX8Wrapper;
friend SortingRendererClass;
const FVFInfoClass& FVFInfo;
unsigned Type;
unsigned short VertexCount;
unsigned short VertexBufferOffset;
VertexBufferClass* VertexBuffer;
// static VertexFormatXYZNDUV2* _Get_Sorting_Vertex_Array();
void Allocate_Sorting_Dynamic_Buffer();
void Allocate_DX8_Dynamic_Buffer();
public:
// Type parameter can be either BUFFER_TYPE_DYNAMIC_DX8 or BUFFER_TYPE_DYNAMIC_SORTING.
// Note: Even though the constructor takes fvf as a parameter, currently the
// only acceptable parameter is "dynamic_fvf_type". Any other type will
// result to an assert.
DynamicVBAccessClass(unsigned type,unsigned fvf,unsigned short vertex_count);
~DynamicVBAccessClass();
// Access fvf
const FVFInfoClass& FVF_Info() const { return FVFInfo; }
unsigned Get_Type() const { return Type; }
unsigned short Get_Vertex_Count() const { return VertexCount; }
// Call at the end of the execution, or at whatever time you wish to release
// the recycled dynamic vertex buffer.
static void _Deinit();
static void _Reset(bool frame_changed);
static unsigned short Get_Default_Vertex_Count(void); ///<current size of dynamic vertex buffer
// To lock the vertex buffer, create instance of this write class locally.
// The buffer is automatically unlocked when you exit the scope.
class WriteLockClass// : public VertexBufferLockClass
{
DynamicVBAccessClass* DynamicVBAccess;
VertexFormatXYZNDUV2 * Vertices;
public:
WriteLockClass(DynamicVBAccessClass* vb_access);
~WriteLockClass();
// Use this function to get a pointer to the first vertex you can write into.
// If we ever change the format used by DynamicVBAccessClass, then the
// return type of this function will change and we'll easily find all code
// using it.
VertexFormatXYZNDUV2 * Get_Formatted_Vertex_Array();
};
friend WriteLockClass;
};
// ----------------------------------------------------------------------------
inline VertexFormatXYZNDUV2 * DynamicVBAccessClass::WriteLockClass::Get_Formatted_Vertex_Array()
{
// assert that the format of the dynamic vertex buffer is still what we think it is.
WWASSERT(DynamicVBAccess->VertexBuffer->FVF_Info().Get_FVF() == (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2|D3DFVF_DIFFUSE));
return Vertices;
}
// ----------------------------------------------------------------------------
/**
** DX8VertexBufferClass
** This class wraps a DX8 vertex buffer. Use the lock objects to modify or append to the vertex buffer.
*/
class DX8VertexBufferClass : public VertexBufferClass
{
W3DMPO_GLUE(DX8VertexBufferClass)
protected:
~DX8VertexBufferClass();
public:
enum UsageType {
USAGE_DEFAULT=0,
USAGE_DYNAMIC=1,
USAGE_SOFTWAREPROCESSING=2,
USAGE_NPATCHES=4
};
DX8VertexBufferClass(unsigned FVF, unsigned short VertexCount, UsageType usage=USAGE_DEFAULT, unsigned vertex_size=0); // Vertex size not used with FVF formats
DX8VertexBufferClass(const Vector3* vertices, const Vector3* normals, const Vector2* tex_coords, unsigned short VertexCount,UsageType usage=USAGE_DEFAULT);
DX8VertexBufferClass(const Vector3* vertices, const Vector3* normals, const Vector4* diffuse, const Vector2* tex_coords, unsigned short VertexCount,UsageType usage=USAGE_DEFAULT);
DX8VertexBufferClass(const Vector3* vertices, const Vector4* diffuse, const Vector2* tex_coords, unsigned short VertexCount,UsageType usage=USAGE_DEFAULT);
DX8VertexBufferClass(const Vector3* vertices, const Vector2* tex_coords, unsigned short VertexCount,UsageType usage=USAGE_DEFAULT);
IDirect3DVertexBuffer8* Get_DX8_Vertex_Buffer() { return VertexBuffer; }
void Copy(const Vector3* loc, unsigned first_vertex, unsigned count);
void Copy(const Vector3* loc, const Vector2* uv, unsigned first_vertex, unsigned count);
void Copy(const Vector3* loc, const Vector3* norm, unsigned first_vertex, unsigned count);
void Copy(const Vector3* loc, const Vector3* norm, const Vector2* uv, unsigned first_vertex, unsigned count);
void Copy(const Vector3* loc, const Vector3* norm, const Vector2* uv, const Vector4* diffuse, unsigned first_vertex, unsigned count);
void Copy(const Vector3* loc, const Vector2* uv, const Vector4* diffuse, unsigned first_vertex, unsigned count);
protected:
IDirect3DVertexBuffer8* VertexBuffer;
void Create_Vertex_Buffer(UsageType usage);
};
/**
** SortingVertexBufferClass
** This class acts as a vertex buffer for the vertices that need to be passed to alpha renderer.
*/
class SortingVertexBufferClass : public VertexBufferClass
{
W3DMPO_GLUE(SortingVertexBufferClass)
friend DX8Wrapper;
friend SortingRendererClass;
friend VertexBufferClass::WriteLockClass;
friend VertexBufferClass::AppendLockClass;
friend DynamicVBAccessClass::WriteLockClass;
VertexFormatXYZNDUV2* VertexBuffer;
protected:
~SortingVertexBufferClass();
public:
SortingVertexBufferClass(unsigned short VertexCount);
};
#endif //DX8VERTEXBUFFER_H

View File

@@ -0,0 +1,242 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//******************************************************************************************
//
// Earth And Beyond
// Copyright (c) 2002 Electronic Arts , Inc. - Westwood Studios
//
// File Name : dx8webbrowser.cpp
// Description : Implementation of D3D Embedded Browser wrapper.
// Author : Darren Schueller
// Date of Creation : 6/4/2002
//
//******************************************************************************************
// $Header: $
//******************************************************************************************
#include "dx8webbrowser.h"
#include "ww3d.h"
#include "dx8wrapper.h"
#if ENABLE_EMBEDDED_BROWSER
// Import the Browser Type Library
// BGC, the path for the dll file is pretty odd, no?
// I'll leave it like this till I can figure out a
// better way.
#import "..\..\..\..\..\run\BrowserEngine.DLL" no_namespace
static IFEBrowserEngine2Ptr pBrowser = 0;
HWND DX8WebBrowser::hWnd = 0;
bool DX8WebBrowser::Initialize( const char* badpageurl,
const char* loadingpageurl,
const char* mousefilename,
const char* mousebusyfilename)
{
if(pBrowser == 0)
{
// Initialize COM
CoInitialize(0);
// Create an instance of the browser control
HRESULT hr = pBrowser.CreateInstance(__uuidof(FEBrowserEngine2));
if(hr == REGDB_E_CLASSNOTREG)
{
HMODULE lib = ::LoadLibrary("BrowserEngine.DLL");
if(lib)
{
FARPROC proc = ::GetProcAddress(lib,"DllRegisterServer");
if(proc)
{
proc();
// Create an instance of the browser control
hr = pBrowser.CreateInstance(__uuidof(FEBrowserEngine2));
}
FreeLibrary(lib);
}
}
// Initialize the browser.
if(hr == S_OK)
{
hWnd = (HWND)WW3D::Get_Window();
pBrowser->Initialize(reinterpret_cast<long*>(DX8Wrapper::_Get_D3D_Device8()));
if(badpageurl)
pBrowser->put_BadPageURL(_bstr_t(badpageurl));
if(loadingpageurl)
pBrowser->put_LoadingPageURL(_bstr_t(loadingpageurl));
if(mousefilename)
pBrowser->put_MouseFileName(_bstr_t(mousefilename));
if(mousebusyfilename)
pBrowser->put_MouseBusyFileName(_bstr_t(mousebusyfilename));
}
else
{
pBrowser = 0;
return false;
}
}
return true;
}
void DX8WebBrowser::Shutdown()
{
if(pBrowser)
{
// Shutdown the browser
pBrowser->Shutdown();
// Release the smart pointer.
pBrowser = 0;
hWnd = 0;
// Shut down COM
CoUninitialize();
}
}
// ******************************************************************************************
// * Function Name: DX8WebBrowser::Update
// ******************************************************************************************
// * Description: Updates the browser image surfaces by copying the bits from the browser
// * DCs to the D3D Image surfaces.
// *
// * Return Type:
// *
// * Argument: void
// *
// ******************************************************************************************
void DX8WebBrowser::Update(void)
{
if(pBrowser) pBrowser->D3DUpdate();
};
// ******************************************************************************************
// * Function Name: DX8WebBrowser::Render
// ******************************************************************************************
// * Description: Draws all browsers to the back buffer.
// *
// * Return Type:
// *
// * Argument: int backbufferindex
// *
// ******************************************************************************************
void DX8WebBrowser::Render(int backbufferindex)
{
if(pBrowser) pBrowser->D3DRender(backbufferindex);
};
// ******************************************************************************************
// * Function Name: DX8WebBrowser::CreateBrowser
// ******************************************************************************************
// * Description: Creates a browser window.
// *
// * Return Type:
// *
// * Argument: const char* browsername - This is a "name" used to identify the
// * browser instance. Multiple browsers can
// * be created, and are referenced using this name.
// * Argument: const char* url - The url to display.
// * Argument: int x - The position and size of the browser (in pixels)
// * Argument: int y
// * Argument: int w
// * Argument: int h
// * Argument: int updateticks - When non-zero, this forces the browser image to get updated
// * at the specified rate (number of milliseconds) regardless
// * of paint messages. When this is zero (the default) the browser
// * image is only updated whenever a paint message is received.
// *
// ******************************************************************************************
void DX8WebBrowser::CreateBrowser(const char* browsername, const char* url, int x, int y, int w, int h, int updateticks, LONG options, LPDISPATCH gamedispatch)
{
WWDEBUG_SAY(("DX8WebBrowser::CreateBrowser - Creating browser with the name %s, url = %s, (x, y, w, h) = (%d, %d, %d, %d), update ticks = %d\n", browsername, url, x, y, h, w, updateticks));
if(pBrowser)
{
_bstr_t brsname(browsername);
pBrowser->CreateBrowser(brsname, _bstr_t(url), reinterpret_cast<long>(hWnd), x, y, w, h, options, gamedispatch);
pBrowser->SetUpdateRate(brsname, updateticks);
}
}
// ******************************************************************************************
// * Function Name: DX8WebBrowser::DestroyBrowser
// ******************************************************************************************
// * Description: Destroys the specified browser. This closes the window and releases
// * the browser instance.
// *
// * Return Type:
// *
// * Argument: const char* browsername - The name of the browser to destroy.
// *
// ******************************************************************************************
void DX8WebBrowser::DestroyBrowser(const char* browsername)
{
WWDEBUG_SAY(("DX8WebBrowser::DestroyBrowser - destroying browser %s\n", browsername));
if(pBrowser)
pBrowser->DestroyBrowser(_bstr_t(browsername));
}
// ******************************************************************************************
// * Function Name: DX8WebBrowser::Is_Browser_Open
// ******************************************************************************************
// * Description: This function checks to see if a browser of the specified name exists and
// * is currently open.
// *
// * Return Type:
// *
// * Argument: const char* browsername - The name of the browser to test.
// *
// ******************************************************************************************
bool DX8WebBrowser::Is_Browser_Open(const char* browsername)
{
if(pBrowser == 0) return false;
return (pBrowser->IsOpen(_bstr_t(browsername)) != 0);
}
// ******************************************************************************************
// * Function Name: DX8WebBrowser::Navigate
// ******************************************************************************************
// * Description: This function causes the browser to navigate to the specified page.
// *
// * Return Type:
// *
// * Argument: const char* browsername - The name of the browser to test.
// * const char* url - The url to navigate to.
// *
// ******************************************************************************************
void DX8WebBrowser::Navigate(const char* browsername, const char* url)
{
if(pBrowser == 0) return;
pBrowser->Navigate(_bstr_t(browsername),_bstr_t(url));
}
#endif

View File

@@ -0,0 +1,93 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//******************************************************************************************
//
// Earth And Beyond
// Copyright (c) 2002 Electronic Arts , Inc. - Westwood Studios
//
// File Name : dx8webbrowser.h
// Description : Implementation of D3D Embedded Browser Wrapper
// Author : Darren Schueller
// Date of Creation : 6/4/2002
//
//******************************************************************************************
// $Header: $
//******************************************************************************************
#ifndef DX8_WEBBROWSER_H
#define DX8_WEBBROWSER_H
#include <windows.h>
#include "d3d8.h"
// ***********************************
// Set this to 0 to remove all embedded browser code.
//
#define ENABLE_EMBEDDED_BROWSER 1
//
// ***********************************
#if ENABLE_EMBEDDED_BROWSER
// These options must match the browser option bits defined in the BrowserEngine code.
// Look in febrowserengine.h
#define BROWSEROPTION_SCROLLBARS 0x0001
#define BROWSEROPTION_3DBORDER 0x0002
struct IDirect3DDevice8;
/**
** DX8WebBrowser
**
** DX8 interface wrapper class. This encapsulates the BrowserEngine interface.
*/
class DX8WebBrowser
{
public:
static bool Initialize( const char* badpageurl = 0,
const char* loadingpageurl = 0,
const char* mousefilename = 0,
const char* mousebusyfilename = 0); //Initialize the Embedded Browser
static void Shutdown(void); // Shutdown the embedded browser. Will close any open browsers.
static void Update(void); // Copies all browser contexts to D3D Image surfaces.
static void Render(int backbufferindex); //Draws all browsers to the backbuffer.
// Creates a browser with the specified name
static void CreateBrowser(const char* browsername, const char* url, int x, int y, int w, int h, int updateticks = 0, LONG options = BROWSEROPTION_SCROLLBARS | BROWSEROPTION_3DBORDER, LPDISPATCH gamedispatch = 0);
// Destroys the browser with the specified name
static void DestroyBrowser(const char* browsername);
// Returns true if a browser with the specified name is open.
static bool Is_Browser_Open(const char* browsername);
// Navigates the specified browser to the specified page.
static void Navigate(const char* browsername, const char* url);
private:
// The window handle of the application. This is initialized by Initialize().
static HWND hWnd;
};
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,833 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/dynamesh.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 12/03/01 4:50p $*
* *
* $Revision:: 25 $*
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "dynamesh.h"
#include "dx8vertexbuffer.h"
#include "dx8indexbuffer.h"
#include "dx8wrapper.h"
#include "sortingrenderer.h"
#include "rinfo.h"
#include "camera.h"
#include "dx8fvf.h"
/*
** DynamicMeshModel implementation
*/
DynamicMeshModel::DynamicMeshModel(unsigned int max_polys, unsigned int max_verts) :
MeshGeometryClass(),
DynamicMeshPNum(0),
DynamicMeshVNum(0),
MatDesc(NULL),
MatInfo(NULL)
{
MatInfo = NEW_REF(MaterialInfoClass, ());
MatDesc = W3DNEW MeshMatDescClass;
MatDesc->Set_Polygon_Count(max_polys);
MatDesc->Set_Vertex_Count(max_verts);
Reset_Geometry(max_polys, max_verts);
}
DynamicMeshModel::DynamicMeshModel(unsigned int max_polys, unsigned int max_verts, MaterialInfoClass *mat_info) :
MeshGeometryClass(),
DynamicMeshPNum(0),
DynamicMeshVNum(0),
MatDesc(NULL),
MatInfo(NULL)
{
MatInfo = mat_info;
MatInfo->Add_Ref();
MatDesc = W3DNEW MeshMatDescClass;
MatDesc->Set_Polygon_Count(max_polys);
MatDesc->Set_Vertex_Count(max_verts);
Reset_Geometry(max_polys, max_verts);
}
DynamicMeshModel::DynamicMeshModel(const DynamicMeshModel &src) :
MeshGeometryClass(src),
DynamicMeshPNum(src.DynamicMeshPNum),
DynamicMeshVNum(src.DynamicMeshVNum),
MatDesc(NULL),
MatInfo(NULL)
{
// Copy the material info structure.
MatInfo = NEW_REF(MaterialInfoClass, (*(src.MatInfo)));
// [SKB: Feb 21 2002 @ 11:47pm] :
// Moved before the remapping cause I don't like referencing null.
MatDesc = W3DNEW MeshMatDescClass;
// remap!
MaterialRemapperClass remapper(src.MatInfo, MatInfo);
remapper.Remap_Mesh(src.MatDesc, MatDesc);
}
DynamicMeshModel::~DynamicMeshModel(void)
{
if (MatDesc) {
delete MatDesc;
MatDesc = NULL;
}
REF_PTR_RELEASE(MatInfo);
}
void DynamicMeshModel::Compute_Plane_Equations(void)
{
// Make sure the arrays are allocated before we do this
get_vert_normals();
Vector4 * planes = get_planes(true);
// Set the poly and vertex counts to the dynamic counts, call the base class function, then
// set them back.
int old_poly_count = PolyCount;
int old_vert_count = VertexCount;
PolyCount = DynamicMeshPNum;
VertexCount = DynamicMeshVNum;
MeshGeometryClass::Compute_Plane_Equations(planes);
PolyCount = old_poly_count;
VertexCount = old_vert_count;
}
void DynamicMeshModel::Compute_Vertex_Normals(void)
{
// Make sure the arrays are allocated before we do this
Vector3 * vnorms = get_vert_normals();
get_planes(true);
// Set the poly and vertex counts to the dynamic counts, call the base class function, then
// set them back.
int old_poly_count = PolyCount;
int old_vert_count = VertexCount;
PolyCount = DynamicMeshPNum;
VertexCount = DynamicMeshVNum;
MeshGeometryClass::Compute_Vertex_Normals(vnorms);
PolyCount = old_poly_count;
VertexCount = old_vert_count;
}
void DynamicMeshModel::Compute_Bounds(Vector3 * verts)
{
// Set the poly and vertex counts to the dynamic counts, call the base class function, then
// set them back.
int old_poly_count = PolyCount;
int old_vert_count = VertexCount;
PolyCount = DynamicMeshPNum;
VertexCount = DynamicMeshVNum;
MeshGeometryClass::Compute_Bounds(verts);
PolyCount = old_poly_count;
VertexCount = old_vert_count;
}
void DynamicMeshModel::Reset(void)
{
Set_Counts(0, 0);
int polycount = Get_Polygon_Count();
int vertcount = Get_Vertex_Count();
Reset_Geometry(polycount, vertcount);
MatDesc->Reset(polycount, vertcount, 1);
REF_PTR_RELEASE(MatInfo);
MatInfo = NEW_REF(MaterialInfoClass, ());
}
void DynamicMeshModel::Render(RenderInfoClass & rinfo)
{
// Process texture reductions:
// MatInfo->Process_Texture_Reduction();
unsigned buffer_type=(Get_Flag(MeshGeometryClass::SORT)&& WW3D::Is_Sorting_Enabled()) ? BUFFER_TYPE_DYNAMIC_SORTING : BUFFER_TYPE_DYNAMIC_DX8;
/*
** Write the vertex data to the vertex buffer. We assume the FVF contains positions, normals,
** one texture channel, and the diffuse color channel (color0). If it does not contain all
** these components, the code will fail.
*/
DynamicVBAccessClass dynamic_vb(buffer_type,dynamic_fvf_type,DynamicMeshVNum);
const FVFInfoClass &fvf_info = dynamic_vb.FVF_Info();
{ // scope for lock
DynamicVBAccessClass::WriteLockClass lock(&dynamic_vb);
unsigned char *vertices = (unsigned char*)lock.Get_Formatted_Vertex_Array();
const Vector3 *locs = Get_Vertex_Array();
const Vector3 *normals = Get_Vertex_Normal_Array();
const Vector2 *uvs = MatDesc->Get_UV_Array_By_Index(0, false);
const Vector2 *uv1s = MatDesc->Get_UV_Array_By_Index(1, false);
const unsigned *colors = MatDesc->Get_Color_Array(0, false);
const static Vector3 default_normal(0.0f, 0.0f, 0.0f);
const static Vector2 default_uv(0.0f, 0.0f);
const unsigned int default_color = 0xFFFFFFFF;
for (int i=0; i < DynamicMeshVNum; i++)
{
*(Vector3 *)(vertices + fvf_info.Get_Location_Offset()) = locs[i];
*(Vector3 *)(vertices + fvf_info.Get_Normal_Offset()) = normals[i];
if (uvs) {
*(Vector2 *)(vertices + fvf_info.Get_Tex_Offset(0)) = uvs[i];
} else {
*(Vector2 *)(vertices + fvf_info.Get_Tex_Offset(0)) = default_uv;
}
if (uv1s) {
*(Vector2 *)(vertices + fvf_info.Get_Tex_Offset(1)) = uv1s[i];
} else {
*(Vector2 *)(vertices + fvf_info.Get_Tex_Offset(1)) = default_uv;
}
if (colors) {
*(unsigned int *)(vertices + fvf_info.Get_Diffuse_Offset()) = colors[i];
} else {
*(unsigned int *)(vertices + fvf_info.Get_Diffuse_Offset()) = default_color;
}
vertices += fvf_info.Get_FVF_Size();
}
} // end scope for lock
/*
** Write index data to index buffers
*/
DynamicIBAccessClass dynamic_ib(buffer_type,DynamicMeshPNum * 3);
const TriIndex *tris = Get_Polygon_Array();
{ // scope for lock
DynamicIBAccessClass::WriteLockClass lock(&dynamic_ib);
unsigned short * indices = lock.Get_Index_Array();
for (int i=0; i < DynamicMeshPNum; i++)
{
indices[i*3 + 0] = (unsigned short)tris[i][0];
indices[i*3 + 1] = (unsigned short)tris[i][1];
indices[i*3 + 2] = (unsigned short)tris[i][2];
}
} // end scope for lock
/*
** Set vertex and index buffers
*/
DX8Wrapper::Set_Vertex_Buffer(dynamic_vb);
DX8Wrapper::Set_Index_Buffer(dynamic_ib,0);
/*
** Draw dynamesh, one pass at a time
*/
unsigned int pass_count = Get_Pass_Count();
for (unsigned int pass = 0; pass < pass_count; pass++) {
/*
** Set current render states (texture, vertex material, shader). Scan triangles until one
** of these changes, and then draw.
*/
// The vertex index range used
unsigned short min_vert_idx = DynamicMeshVNum - 1;
unsigned short max_vert_idx = 0;
unsigned short start_tri_idx = 0;
unsigned short cur_tri_idx = 0;
bool done = false;
bool texture_changed = false;
bool texture1_changed = false;
bool material_changed = false;
bool shader_changed = false;
TextureClass **texture_array0 = NULL;
TexBufferClass * tex_buf = MatDesc->Get_Texture_Array(pass, 0, false);
if (tex_buf) {
texture_array0 = tex_buf->Get_Array();
} else {
texture_array0 = NULL;
}
TextureClass **texture_array1 = NULL;
TexBufferClass * tex_buf1 = MatDesc->Get_Texture_Array(pass, 1, false);
if (tex_buf1) {
texture_array1 = tex_buf1->Get_Array();
} else {
texture_array1 = NULL;
}
VertexMaterialClass **material_array = NULL;
MatBufferClass * mat_buf = MatDesc->Get_Material_Array(pass, false);
if (mat_buf) {
material_array = mat_buf->Get_Array();
} else {
material_array = NULL;
}
ShaderClass *shader_array = MatDesc->Get_Shader_Array(pass, false);
// Set the DX8 state to the first triangle's state
if (texture_array0) {
DX8Wrapper::Set_Texture(0,texture_array0[0]);
} else {
DX8Wrapper::Set_Texture(0,MatDesc->Peek_Single_Texture(pass, 0));
}
if (texture_array1) {
DX8Wrapper::Set_Texture(1,texture_array1[0]);
} else {
DX8Wrapper::Set_Texture(1,MatDesc->Peek_Single_Texture(pass, 1));
}
if (material_array) {
DX8Wrapper::Set_Material(material_array[tris[0].I]);
} else {
DX8Wrapper::Set_Material(MatDesc->Peek_Single_Material(pass));
}
if (shader_array) {
DX8Wrapper::Set_Shader(shader_array[0]);
} else {
DX8Wrapper::Set_Shader(MatDesc->Get_Single_Shader(pass));
}
SphereClass sphere(Vector3(0.0f,0.0f,0.0f),0.0f);
Get_Bounding_Sphere(&sphere);
// If no texture, shader or material arrays for this pass just draw and go to next pass
if (!texture_array0 && !texture_array1 && !material_array && !shader_array) {
if (buffer_type==BUFFER_TYPE_DYNAMIC_SORTING) {
SortingRendererClass::Insert_Triangles(sphere,0, DynamicMeshPNum, 0, DynamicMeshVNum);
}
else {
DX8Wrapper::Draw_Triangles(0, DynamicMeshPNum, 0, DynamicMeshVNum);
}
continue;
}
while (!done) {
// Add vertex indices of tri[cur_tri_idx] to min_vert_idx, max_vert_idx
const TriIndex &tri = tris[cur_tri_idx];
unsigned short min_idx = (unsigned short)MIN(MIN(tri.I, tri.J), tri.K);
unsigned short max_idx = (unsigned short)MAX(MAX(tri.I, tri.J), tri.K);
min_vert_idx = MIN(min_vert_idx, min_idx);
max_vert_idx = MAX(max_vert_idx, max_idx);
// Check the next triangle to see if the current run has ended.
unsigned short next_tri_idx = cur_tri_idx + 1;
done = next_tri_idx >= DynamicMeshPNum;
if (done) {
texture_changed = false;
texture1_changed = false;
material_changed = false;
shader_changed = false;
} else {
texture_changed = texture_array0 && texture_array0[cur_tri_idx] != texture_array0[next_tri_idx];
texture1_changed = texture_array1 && texture_array1[cur_tri_idx] != texture_array1[next_tri_idx];
material_changed = material_array && material_array[tris[cur_tri_idx].I] != material_array[tris[next_tri_idx].I];
shader_changed = shader_array && shader_array[cur_tri_idx] != shader_array[next_tri_idx];
}
// If run ends (mesh ends or state changes) draw, reset indices, set state for next run.
if (done || texture_changed || material_changed || shader_changed) {
if (buffer_type==BUFFER_TYPE_DYNAMIC_SORTING) {
SortingRendererClass::Insert_Triangles(
sphere,
(start_tri_idx * 3),
(1 + cur_tri_idx - start_tri_idx),
min_vert_idx,
1 + max_vert_idx - min_vert_idx);
}
else {
DX8Wrapper::Draw_Triangles(
(start_tri_idx * 3),
(1 + cur_tri_idx - start_tri_idx),
min_vert_idx,
1 + max_vert_idx - min_vert_idx);
}
start_tri_idx = next_tri_idx;
min_vert_idx = DynamicMeshVNum - 1;
max_vert_idx = 0;
if (texture_changed) DX8Wrapper::Set_Texture(0,texture_array0[next_tri_idx]);
if (texture1_changed) DX8Wrapper::Set_Texture(1,texture_array1[next_tri_idx]);
if (material_changed) DX8Wrapper::Set_Material(material_array[tris[next_tri_idx].I]);
if (shader_changed) DX8Wrapper::Set_Shader(shader_array[next_tri_idx]);
}
cur_tri_idx = next_tri_idx;
} // while (!done)
} // for (pass)
}
void DynamicMeshModel::Initialize_Texture_Array(int pass, int stage, TextureClass *texture)
{
TexBufferClass * texlist = MatDesc->Get_Texture_Array(pass, 0, true);
for (int lp = 0; lp < PolyCount; lp++) {
texlist->Set_Element(lp, texture);
}
}
void DynamicMeshModel::Initialize_Material_Array(int pass, VertexMaterialClass *vmat)
{
MatBufferClass * vertmatlist = MatDesc->Get_Material_Array(pass, true);
for (int lp = 0; lp < VertexCount; lp++) {
vertmatlist->Set_Element(lp, vmat);
}
}
void DynamicMeshClass::Render(RenderInfoClass & rinfo)
{
if (Is_Not_Hidden_At_All() == false) return;
// test for an empty mesh..
if (PolyCount == 0 ) return;
// If static sort lists are enabled and this mesh has a sort level, put it on the list instead
// of rendering it.
if (WW3D::Are_Static_Sort_Lists_Enabled() && SortLevel != SORT_LEVEL_NONE) {
WW3D::Add_To_Static_Sort_List(this, SortLevel);
} else {
const FrustumClass & frustum = rinfo.Camera.Get_Frustum();
if (CollisionMath::Overlap_Test(frustum, Get_Bounding_Box()) != CollisionMath::OUTSIDE) {
DX8Wrapper::Set_Transform(D3DTS_WORLD, Transform);
Model->Render(rinfo);
}
}
}
bool DynamicMeshClass::End_Vertex()
{
// check that we have room for a new vertex
WWASSERT(VertCount < Model->Get_Vertex_Count());
// if we are a multi-material object record the material
int pass = Get_Pass_Count();
while (pass--) {
if (MultiVertexMaterial[pass]) {
VertexMaterialClass *mat = Peek_Material_Info()->Get_Vertex_Material(VertexMaterialIdx[pass]);
Model->Set_Material(VertCount, mat, pass);
REF_PTR_RELEASE(mat);
}
}
// if we are multi colored, record the color
for (int color_array_index = 0; color_array_index < MAX_COLOR_ARRAYS; color_array_index++) {
if (MultiVertexColor[color_array_index]) {
// Vector4 * color = &((Model->Get_Color_Array(color_array_index))[VertCount]);
// color->X = CurVertexColor[color_array_index].X;
// color->Y = CurVertexColor[color_array_index].Y;
// color->Z = CurVertexColor[color_array_index].Z;
// color->W = CurVertexColor[color_array_index].W;
unsigned * color = &((Model->Get_Color_Array(color_array_index))[VertCount]);
*color=DX8Wrapper::Convert_Color_Clamp(CurVertexColor[color_array_index]);
}
}
// mark this vertex as being complete
VertCount++;
TriVertexCount++;
// if we have 3 or more vertices, add a new poly
if (TriVertexCount >= 3) {
// check that we have room for a new poly
WWASSERT(PolyCount < Model->Get_Polygon_Count());
// set vertex indices
TriIndex *poly = &(Model->Get_Non_Const_Polygon_Array())[PolyCount];
if (TriMode == TRI_MODE_STRIPS) {
(*poly)[0] = VertCount-3;
(*poly)[1] = VertCount-2;
(*poly)[2] = VertCount-1;
// for every other tri, reverse vertex order
if (Flip_Face()) {
(*poly)[1] = VertCount-1;
(*poly)[2] = VertCount-2;
}
} else {
(*poly)[0] = FanVertex;
(*poly)[1] = VertCount-2;
(*poly)[2] = VertCount-1;
}
// check each pass
int pass = Get_Pass_Count();
while (pass--) {
// If we are multi texture
if (MultiTexture[pass]) {
TextureClass *tex = Peek_Material_Info()->Get_Texture(TextureIdx[pass]);
Model->Set_Texture(PolyCount, tex, pass);
REF_PTR_RELEASE(tex);
}
}
// increase the count and record that we have a new material
PolyCount++;
Model->Set_Counts(PolyCount, VertCount);
}
return true;
}
/******************************************************************
**
** DynamicMeshClass
**
*******************************************************************/
DynamicMeshClass::DynamicMeshClass(int max_poly, int max_vert) :
Model(NULL),
PolyCount(0),
VertCount(0),
TriVertexCount(0),
FanVertex(0),
TriMode(TRI_MODE_STRIPS),
SortLevel(SORT_LEVEL_NONE)
{
int pass = MAX_PASSES;
while (pass--) {
MultiTexture[pass] = false;
TextureIdx[pass] = -1;
MultiVertexMaterial[pass] = false;
VertexMaterialIdx[pass] = -1;
}
for (int color_array_index = 0; color_array_index < MAX_COLOR_ARRAYS; color_array_index++) {
MultiVertexColor[color_array_index] = false;
CurVertexColor[color_array_index].Set(1.0f, 1.0f, 1.0f, 1.0f);
}
Model = NEW_REF(DynamicMeshModel, (max_poly, max_vert));
}
DynamicMeshClass::DynamicMeshClass(int max_poly, int max_vert, MaterialInfoClass *mat_info) :
Model(NULL),
PolyCount(0),
VertCount(0),
TriVertexCount(0),
FanVertex(0),
TriMode(TRI_MODE_STRIPS),
SortLevel(SORT_LEVEL_NONE)
{
int pass = MAX_PASSES;
while (pass--) {
MultiTexture[pass] = false;
TextureIdx[pass] = -1;
MultiVertexMaterial[pass] = false;
VertexMaterialIdx[pass] = -1;
}
for (int color_array_index = 0; color_array_index < MAX_COLOR_ARRAYS; color_array_index++) {
MultiVertexColor[color_array_index] = false;
CurVertexColor[color_array_index].Set(1.0f, 1.0f, 1.0f, 1.0f);
}
Model = NEW_REF(DynamicMeshModel, (max_poly, max_vert, mat_info));
}
DynamicMeshClass::DynamicMeshClass(const DynamicMeshClass & src) :
RenderObjClass(src),
Model(NULL),
PolyCount(src.PolyCount),
VertCount(src.VertCount),
TriVertexCount(src.TriVertexCount),
FanVertex(src.FanVertex),
TriMode(src.TriMode),
SortLevel(src.SortLevel)
{
int pass = MAX_PASSES;
while (pass--) {
MultiTexture[pass] = src.MultiTexture[pass];
TextureIdx[pass] = src.TextureIdx[pass];
MultiVertexMaterial[pass] = src.MultiVertexMaterial[pass];
VertexMaterialIdx[pass] = src.VertexMaterialIdx[pass];
MultiVertexColor[pass] = src.MultiVertexColor[pass];
CurVertexColor[pass] = src.CurVertexColor[pass];
}
for (int color_array_index = 0; color_array_index < MAX_COLOR_ARRAYS; color_array_index++) {
MultiVertexColor[color_array_index] = src.MultiVertexColor[color_array_index];
CurVertexColor[color_array_index] = src.CurVertexColor[color_array_index];
}
Model = NEW_REF(DynamicMeshModel, (*(src.Model)));
}
void DynamicMeshClass::Resize(int max_polys, int max_verts)
{
Reset();
REF_PTR_RELEASE(Model);
Model = NEW_REF(DynamicMeshModel, (max_polys, max_verts));
// reset all the texture & vertex material indices
int pass = MAX_PASSES;
while (pass--) {
TextureIdx[pass] = -1;
VertexMaterialIdx[pass] = -1;
MultiVertexMaterial[pass] = false;
}
}
DynamicMeshClass::~DynamicMeshClass()
{
REF_PTR_RELEASE(Model);
}
RenderObjClass * DynamicMeshClass::Clone(void) const
{
return NEW_REF(DynamicMeshClass, (*this));
}
void DynamicMeshClass::Location(float x, float y, float z)
{
Vector3 * loc = Model->Get_Vertex_Array();
assert(loc);
loc[VertCount].X = x;
loc[VertCount].Y = y;
loc[VertCount].Z = z;
}
/*
** For moving a vertex after the DynaMesh has already been created.
*/
void DynamicMeshClass::Move_Vertex(int index, float x, float y, float z)
{
Vector3 * loc = Model->Get_Vertex_Array();
assert(loc);
loc[index][0] = x;
loc[index][1] = y;
loc[index][2] = z;
}
/*
** Get a vertex value.
*/
void DynamicMeshClass::Get_Vertex(int index, float &x, float &y, float &z)
{
Vector3 * loc = Model->Get_Vertex_Array();
assert(loc);
x = loc[index][0];
y = loc[index][1];
z = loc[index][2];
}
/*
** Offset the entire mesh
*/
void DynamicMeshClass::Translate_Vertices(const Vector3 & offset)
{
Vector3 * loc = Model->Get_Vertex_Array();
assert(loc);
for (int i=0; i < Get_Num_Vertices(); i++) {
loc[i].X += offset.X;
loc[i].Y += offset.Y;
loc[i].Z += offset.Z;
}
Set_Dirty_Bounds();
Set_Dirty_Planes();
}
int DynamicMeshClass::Set_Vertex_Material(int idx, int pass)
{
assert(idx < Peek_Material_Info()->Vertex_Material_Count());
VertexMaterialIdx[pass] = idx;
if (!MultiVertexMaterial[pass]) {
// WWASSERT( VertexMaterialIdx[pass] == 0);
VertexMaterialClass *mat = Peek_Material_Info()->Get_Vertex_Material(VertexMaterialIdx[pass]);
Model->Set_Single_Material(mat, pass);
mat->Release_Ref();
}
return VertexMaterialIdx[pass];
}
int DynamicMeshClass::Set_Vertex_Material(VertexMaterialClass *material, bool dont_search, int pass)
{
// Check if same as the last vertex material
if (Peek_Material_Info()->Vertex_Material_Count() && (VertexMaterialIdx[pass] != -1) && Peek_Material_Info()->Peek_Vertex_Material(VertexMaterialIdx[pass]) == material) {
return VertexMaterialIdx[pass];
}
// if there are vertex materials in the list then we may have just jumped
// to becoming a multi-vertex-material object. Take care of that here.
if ((!MultiVertexMaterial[pass]) && Peek_Material_Info()->Vertex_Material_Count() && (VertexMaterialIdx[pass] != -1) && Peek_Material_Info()->Peek_Vertex_Material(VertexMaterialIdx[pass]) != material) {
// allocate the array of per-vertex vertex material overrides
VertexMaterialClass *mat = Peek_Material_Info()->Get_Vertex_Material(VertexMaterialIdx[pass]);
Model->Initialize_Material_Array(pass, mat);
mat->Release_Ref();
// flag that we need to write the per -vertex vertex material override array
MultiVertexMaterial[pass] = true;
}
// add the material to the material info class if we cant find it in the
// list. if we are not supposed to search the list for it then just add
// it.
if (!dont_search) {
for (int lp = 0, found = 0; lp < Peek_Material_Info()->Vertex_Material_Count(); lp ++) {
VertexMaterialClass *mat = Peek_Material_Info()->Get_Vertex_Material(lp);
if (material == mat) {
VertexMaterialIdx[pass] = lp;
found = true;
mat->Release_Ref();
break;
}
mat->Release_Ref();
}
if (!found) {
Peek_Material_Info()->Add_Vertex_Material(material);
VertexMaterialIdx[pass] = Peek_Material_Info()->Vertex_Material_Count() - 1;
}
} else {
Peek_Material_Info()->Add_Vertex_Material(material);
VertexMaterialIdx[pass] = Peek_Material_Info()->Vertex_Material_Count() - 1;
}
if (!MultiVertexMaterial[pass]) {
Model->Set_Single_Material(Peek_Material_Info()->Peek_Vertex_Material(VertexMaterialIdx[pass]), pass);
}
return(VertexMaterialIdx[pass]);
}
int DynamicMeshClass::Set_Texture(int idx, int pass)
{
WWASSERT(idx < Peek_Material_Info()->Texture_Count());
TextureIdx[pass] = idx;
if (!MultiTexture[pass]) {
TextureClass *tex = Peek_Material_Info()->Get_Texture(TextureIdx[pass]);
Model->Set_Single_Texture(tex, pass);
tex->Release_Ref();
}
return TextureIdx[pass];
}
int DynamicMeshClass::Set_Texture(TextureClass *texture, bool dont_search, int pass)
{
// Check if same as the last texture
if (Peek_Material_Info()->Texture_Count() && (TextureIdx[pass] != -1) && Peek_Material_Info()->Peek_Texture(TextureIdx[pass]) == texture) {
return TextureIdx[pass];
}
// if there are textures in the list then we may have just jumped
// to becoming a multi-texture object. Take care of that here.
if ((!MultiTexture[pass]) && Peek_Material_Info()->Texture_Count() && (TextureIdx[pass] != -1) && Peek_Material_Info()->Peek_Texture(TextureIdx[pass]) != texture) {
// allocate the array of per polygon material over-rides
TextureClass *tex = Peek_Material_Info()->Get_Texture(TextureIdx[pass]);
Model->Initialize_Texture_Array(pass, 0, tex);
tex->Release_Ref();
// flag that we need to write the per polygon material overide array
MultiTexture[pass] = true;
}
// add the material to the material info class if we cant find it in the
// list. if we are not supposed to search the list for it then just add
// it.
if (!dont_search) {
for (int lp = 0, found = 0; lp < Peek_Material_Info()->Texture_Count(); lp ++) {
TextureClass *tex = Peek_Material_Info()->Get_Texture(lp);
if (texture == tex) {
TextureIdx[pass] = lp;
found = true;
tex->Release_Ref();
break;
}
tex->Release_Ref();
}
if (!found) {
Peek_Material_Info()->Add_Texture(texture);
TextureIdx[pass] = Peek_Material_Info()->Texture_Count() - 1;
}
} else {
Peek_Material_Info()->Add_Texture(texture);
TextureIdx[pass] = Peek_Material_Info()->Texture_Count() - 1;
}
if (!MultiTexture[pass]) {
TextureClass *tex = Peek_Material_Info()->Get_Texture(TextureIdx[pass]);
Model->Set_Single_Texture(tex, pass);
tex->Release_Ref();
}
return(TextureIdx[pass]);
}
/*
**
*/
// Remap locations to match a screen
void DynamicScreenMeshClass::Location( float x, float y, float z)
{
DynamicMeshClass::Location( (x * 2) - 1, Aspect - (y * 2 * Aspect), 0);
}
// For moving a vertex after the DynaMesh has already been created.
void DynamicScreenMeshClass::Move_Vertex(int index, float x, float y, float z)
{
DynamicMeshClass::Move_Vertex( index, (x * 2) - 1, Aspect - (y * 2 * Aspect), 0);
}
// Set position
void DynamicScreenMeshClass::Set_Position(const Vector3 &v)
{
DynamicMeshClass::Set_Position(Vector3(v.X * 2, -(v.Y * 2 * Aspect), 0));
}
void DynamicScreenMeshClass::Reset( void )
{
Reset_Flags();
Reset_Mesh_Counters();
}

View File

@@ -0,0 +1,582 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/dynamesh.h $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 12/03/01 4:20p $*
* *
* $Revision:: 15 $*
* *
*-------------------------------------------------------------------------*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef DYNAMESH_H
#define DYNAMESH_H
#include "meshgeometry.h"
#include "meshmatdesc.h"
#include "matinfo.h"
#include "rendobj.h"
#include "polyinfo.h"
#include "dx8wrapper.h"
class ShaderClass;
class IntersectionClass;
class IntersectionResultClass;
/*
** DynamicMeshModel: used for low-level rendering of DynamicMeshClass.
** It is composed of the same two classes (one base, one embedded as
** MeshModelClass, so many of its features are similar (see meshmdl.h)
*/
class DynamicMeshModel : public MeshGeometryClass
{
W3DMPO_GLUE(DynamicMeshModel)
public:
DynamicMeshModel(unsigned int max_polys, unsigned int max_verts);
DynamicMeshModel(unsigned int max_polys, unsigned int max_verts, MaterialInfoClass *mat_info);
DynamicMeshModel(const DynamicMeshModel &src);
~DynamicMeshModel(void);
// Inherited from MeshGeometryClass
virtual void Compute_Plane_Equations(void);
virtual void Compute_Vertex_Normals(void);
virtual void Compute_Bounds(Vector3 * verts);
// Reset mesh (with existing max polygon and max vertex counts)
void Reset(void);
// Render mesh
void Render(RenderInfoClass & rinfo);
// Set current polygon and vertex counts
void Set_Counts(int pnum, int vnum) { DynamicMeshPNum = pnum; DynamicMeshVNum = vnum; }
// Access to material stuff:
unsigned * Get_Color_Array(int color_array_index) { return MatDesc->Get_Color_Array(color_array_index); }
Vector2 * Get_UV_Array(int uv_array_index) { return MatDesc->Get_UV_Array_By_Index(uv_array_index); }
void Set_Single_Material(VertexMaterialClass * vmat, int pass=0) { MatDesc->Set_Single_Material(vmat, pass); }
void Set_Single_Texture(TextureClass * tex, int pass=0, int stage=0) { MatDesc->Set_Single_Texture(tex, pass, stage); }
void Set_Single_Shader(ShaderClass shader, int pass=0) { MatDesc->Set_Single_Shader(shader, pass); }
void Set_Material(int vidx, VertexMaterialClass * vmat, int pass=0) { MatDesc->Set_Material(vidx, vmat, pass); }
void Set_Shader(int pidx, ShaderClass shader, int pass=0) { MatDesc->Set_Shader(pidx, shader, pass); }
void Set_Texture(int pidx, TextureClass * tex, int pass=0, int stage=0) { MatDesc->Set_Texture(pidx, tex, pass, stage); }
void Set_Pass_Count(int passes) { MatDesc->Set_Pass_Count(passes); }
int Get_Pass_Count(void) const { return MatDesc->Get_Pass_Count(); }
// Create the array (if it doesn't exist), fill it with the supplied value.
void Initialize_Texture_Array(int pass, int stage, TextureClass *texture = NULL);
void Initialize_Material_Array(int pass, VertexMaterialClass *vmat = NULL);
// Accessors to material info:
MaterialInfoClass *Peek_Material_Info(void) { return MatInfo; }
MaterialInfoClass *Get_Material_Info(void) { if (MatInfo) MatInfo->Add_Ref(); return MatInfo;}
void Set_Material_Info(MaterialInfoClass *mat_info)
{
if (MatInfo)
MatInfo->Release_Ref();
WWASSERT(MatInfo != 0);
MatInfo = mat_info;
MatInfo->Add_Ref();
}
// New geometry accessors (non-const)
TriIndex * Get_Non_Const_Polygon_Array(void);
Vector3 * Get_Non_Const_Vertex_Normal_Array(void);
private:
// These are current counts, as opposed to those in the mesh geometry and
// material description which are actually maximum counts.
int DynamicMeshPNum;
int DynamicMeshVNum;
// All non-geometry properties (uvs, colors, textures, shaders, etc)
MeshMatDescClass * MatDesc;
// Lists of textures and vertex materials for ease of processing
MaterialInfoClass * MatInfo;
};
/*
** Dynamic Meshes
*/
class DynamicMeshClass : public RenderObjClass {
public:
// constructor and destructor
DynamicMeshClass( int max_poly, int max_vert);
DynamicMeshClass( int max_poly, int max_vert, MaterialInfoClass *mat_info);
DynamicMeshClass( const DynamicMeshClass & src);
virtual ~DynamicMeshClass( void);
// Inherited from RenderObjClass:
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_DYNAMESH; }
virtual void Render(RenderInfoClass & rinfo);
virtual MaterialInfoClass *Peek_Material_Info(void) { return Model->Peek_Material_Info(); }
virtual MaterialInfoClass *Get_Material_Info(void) { return Model->Get_Material_Info(); }
virtual void Set_Material_Info(MaterialInfoClass *mat_info) { Model->Set_Material_Info(mat_info); }
// all render objects should be able to tell you how many polygons were
// used in the making of the render object.
virtual int Get_Num_Polys(void) const { return PolyCount; }
// return the number of vertices used by this renderobject
virtual int Get_Num_Vertices(void) const { return VertCount; }
// Get and set static sort level
virtual int Get_Sort_Level(void) const { return SortLevel; }
virtual void Set_Sort_Level(int level) { SortLevel = level; if(level != SORT_LEVEL_NONE) Disable_Sort();}
// object space bounding volumes
virtual inline void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual inline void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
// Set the vertex material for the current triangle
int Set_Vertex_Material( int idx, int pass = 0);
int Set_Vertex_Material( VertexMaterialClass *material, bool dont_search = false, int pass = 0);
// Set the number of passes for the mesh
void Set_Pass_Count(int passes) { assert(PolyCount == 0); Model->Set_Pass_Count(passes); }
int Get_Pass_Count() { return Model->Get_Pass_Count(); }
// Set the texture for the current triangle
int Set_Texture( int idx, int pass = 0);
int Set_Texture( TextureClass *texture, bool dont_search = false, int pass = 0);
// Set the shader for the current triangle
int Set_Shader( const ShaderClass & shader, int pass = 0) { Model->Set_Single_Shader(shader, pass); return 0; }
// set the shader, texture, and vertex material as found in the polygon info object
void Set_Polygon_Info(const PolygonInfoClass &polyInfo, bool dont_search_texture = false, bool dont_search_vertex_material = false, int pass = 0)
{
// there must be a shader..
assert(polyInfo.Peek_Shader() != 0);
Set_Shader(* (polyInfo.Peek_Shader()), pass);
assert(polyInfo.Peek_Vertex_Material() != 0);
Set_Vertex_Material(polyInfo.Peek_Vertex_Material(), dont_search_vertex_material, pass);
if (polyInfo.Peek_Texture())
Set_Texture(polyInfo.Peek_Texture(), dont_search_texture, pass);
}
// Set vertex Color
inline int Set_Vertex_Color(const Vector4 & color, int color_array_index = 0);
inline int Set_Vertex_Color(const Vector3 & color, int color_array_index = 0);
// reset the mesh flags
void Reset_Flags() { Set_Dirty(); }
// Flush the mesh
void Reset_Native_Mesh() { Model->Reset(); }
// reset our poly and vertex counts
void Reset_Mesh_Counters()
{
Model->Set_Counts(0, 0);
Disable_Sort();
PolyCount = 0;
VertCount = 0;
}
// Reset all polys and verts. Call the other reset functions directly if you do not want all
// characteristics to be reset.
virtual void Reset( void )
{
// Note that the active poly count has changed since the last render call by setting the dirty flag
Reset_Flags();
Reset_Native_Mesh();
Reset_Mesh_Counters();
// reset all the texture & vertex material indices
int pass = MAX_PASSES;
while (pass--) {
TextureIdx[pass] = -1;
VertexMaterialIdx[pass] = -1;
MultiVertexMaterial[pass] = false;
}
}
// Deletes mesh and recreates it with new max_polys and verts.
void Resize(int max_polys, int max_verts);
// Triangle creation routines
void Begin_Tri_Strip( void ) { TriVertexCount = 0; TriMode = TRI_MODE_STRIPS; }
void Begin_Tri_Fan( void ) { TriVertexCount = 0; TriMode = TRI_MODE_FANS; FanVertex = VertCount; }
// vertex creation routines
void Begin_Vertex( void) {}
virtual void Location( float x, float y, float z);
// version for speedier use in certain cases
void Location_Inline( float x, float y, float z )
{
Vector3 * loc = Model->Get_Vertex_Array();
assert(loc);
loc[VertCount].X = x;
loc[VertCount].Y = y;
loc[VertCount].Z = z;
}
void Location_Inline(Vector3 &v) { Location_Inline(v.X,v.Y,v.Z); }
// retrieve a reference to the vertex in the object
// WARNING: does not validate index
Vector3 & Get_Location(int index) {
return Model->Get_Vertex_Array()[index];
}
void Normal( float x, float y, float z)
{
Vector3 * norms = Model->Get_Non_Const_Vertex_Normal_Array();
assert(norms);
norms[VertCount].X = x;
norms[VertCount].Y = y;
norms[VertCount].Z = z;
}
void Normal(Vector3 &v) { Normal(v.X,v.Y,v.Z); }
// retrieve a reference to the normal vector3 in the object
// WARNING: does not validate index
Vector3 & Get_Normal(int index) { return Model->Get_Non_Const_Vertex_Normal_Array()[index]; }
void Color(float r, float g, float b, float a, int color_array_index = 0)
{
// Vector4 * color = Model->Get_Color_Array(color_array_index);
unsigned * color = Model->Get_Color_Array(color_array_index);
assert(color);
color[VertCount]=DX8Wrapper::Convert_Color_Clamp(Vector4(r,g,b,a));
// color[VertCount].X = r;
// color[VertCount].Y = g;
// color[VertCount].Z = b;
// color[VertCount].W = a;
}
void Color(const Vector4 &v, int color_array_index = 0) { Color(v.X, v.Y, v.Z, v.W, color_array_index); }
void Color(unsigned v, int color_array_index=0)
{
unsigned * color = Model->Get_Color_Array(color_array_index);
assert(color);
color[VertCount]=v;
}
// retrieve a reference to a color entry in the object
// WARNING: does not validate index
// Vector4 & Get_Color(int index, int color_array_index = 0) { return Model->Get_Color_Array(color_array_index)[index]; }
unsigned Get_Color(int index, int color_array_index = 0) { return Model->Get_Color_Array(color_array_index)[index]; }
void UV(float u, float v, int uv_array_index = 0)
{
Vector2 * uv = Model->Get_UV_Array(uv_array_index);
assert(uv);
uv[VertCount].U = u;
uv[VertCount].V = v;
}
void UV( Vector2 &v, int uv_array_index = 0) { UV(v.U, v.V, uv_array_index); }
// retrieve a reference to a UV entry in the object
// WARNING: does not validate index
Vector2 & Get_UV(int index, int uv_array_index = 0 )
{
return Model->Get_UV_Array(uv_array_index)[index];
}
bool End_Vertex( void);
// vertex creation shortcut, performs a begin, projected, rotated, and end
bool Vertex(float x, float y, float z, float u, float v)
{
Begin_Vertex();
Location(x, y, z);
UV(u, v);
return End_Vertex();
}
bool Vertex(Vector2 v)
{
Begin_Vertex();
Location(v.X, v.Y, 0);
return End_Vertex();
}
void End_Tri_Strip( void )
{
TriVertexCount = 0;
}
void End_Tri_Fan( void )
{
TriVertexCount = 0;
}
// For moving a vertex after the DynaMesh has already been created.
virtual void Move_Vertex(int index, float x, float y, float z);
virtual void Get_Vertex(int index, float &x, float &y, float &z);
// For moving all vertices in the mesh by a fixed amount
void Translate_Vertices(const Vector3 & offset);
// For changing the color of a vertex after DynaMesh has been created.
virtual void Change_Vertex_Color(int index, const Vector4 &color, int color_array_index)
{
// check if switching to multivertexcolor
if (!MultiVertexColor[color_array_index]) {
Switch_To_Multi_Vertex_Color(color_array_index);
}
CurVertexColor[color_array_index].X = color.X;
CurVertexColor[color_array_index].Y = color.Y;
CurVertexColor[color_array_index].Z = color.Z;
CurVertexColor[color_array_index].W = color.W;
// Vector4 * color_list = Model->Get_Color_Array(color_array_index);
unsigned * color_list = Model->Get_Color_Array(color_array_index);
color_list[index] = DX8Wrapper::Convert_Color_Clamp(color);
}
/*
** The following are a bunch of inlined functions for setting & clearing the mesh model's various flags
*/
// dirty flags
void Set_Dirty_Bounds(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_BOUNDS, true); }
void Clear_Dirty_Bounds(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_BOUNDS, false); }
void Set_Dirty_Planes(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_PLANES, true); }
void Clear_Dirty_Planes(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_PLANES, false); }
void Set_Dirty_Vertex_Normals(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_VNORMALS, true); }
void Clear_Dirty_Vertex_Normals(void) { Model->Set_Flag(MeshGeometryClass::DIRTY_VNORMALS, false); }
// control flags
void Disable_Sort(void) { Model->Set_Flag(MeshGeometryClass::SORT, false); }
void Enable_Sort(void) { Model->Set_Flag(MeshGeometryClass::SORT, true); }
bool Sort_Enabled(void) { return (Model->Get_Flag(MeshGeometryClass::SORT) != 0); }
void Disable_Bounding_Box(void) { Model->Set_Flag(MeshGeometryClass::DISABLE_BOUNDING_BOX, true); }
void Enable_Bounding_Box(void) { Model->Set_Flag(MeshGeometryClass::DISABLE_BOUNDING_BOX, false); }
bool Test_Bounding_Box(void) { return (Model->Get_Flag(MeshGeometryClass::DISABLE_BOUNDING_BOX) == 0); }
void Disable_Bounding_Sphere(void) { Model->Set_Flag(MeshGeometryClass::DISABLE_BOUNDING_SPHERE, true); }
void Enable_Bounding_Sphere(void) { Model->Set_Flag(MeshGeometryClass::DISABLE_BOUNDING_SPHERE, false); }
bool Test_Bounding_Sphere(void) { return (Model->Get_Flag(MeshGeometryClass::DISABLE_BOUNDING_SPHERE) == 0); }
// this is called by the Reset function
void Set_Dirty( void) { Set_Dirty_Bounds(); Set_Dirty_Planes(); Set_Dirty_Vertex_Normals(); }
enum {
MAX_COLOR_ARRAYS = MeshMatDescClass::MAX_COLOR_ARRAYS,
MAX_PASSES = MeshMatDescClass::MAX_PASSES
};
// USER BE WARNED: This hack is only here because DynamicMeshClass does not expose all of the
// features that DynamicMeshModel provides. It may be dangerous to modify the model behind the
// DynamicMeshClass's back so use at your own risk!
DynamicMeshModel * Peek_Model(void) { return Model; }
protected:
inline void Switch_To_Multi_Vertex_Color(int color_array_index = 0);
// tells when the triangle needs to be back flipped
virtual bool Flip_Face( void) { return (!(TriVertexCount & 1)); }
// Low-level mesh object
DynamicMeshModel * Model;
int VertexMaterialIdx[MAX_PASSES];
bool MultiVertexMaterial[MAX_PASSES];
int TextureIdx[MAX_PASSES];
bool MultiTexture[MAX_PASSES];
Vector4 CurVertexColor[MAX_COLOR_ARRAYS];
bool MultiVertexColor[MAX_COLOR_ARRAYS];
// number of polygons in the mesh
int PolyCount;
// number of vertices in the mesh
int VertCount;
// triangle vertex number
int TriVertexCount;
// base vertex when submitting fans
int FanVertex;
// is user currently submitting strips or fans
enum { TRI_MODE_STRIPS = 0, TRI_MODE_FANS = 1 };
int TriMode;
// The static sort level
char SortLevel;
};
inline Vector3 * DynamicMeshModel::Get_Non_Const_Vertex_Normal_Array(void)
{
if (Get_Flag(DIRTY_VNORMALS)) {
Compute_Vertex_Normals();
}
return get_vert_normals();
}
inline TriIndex * DynamicMeshModel::Get_Non_Const_Polygon_Array(void)
{
return get_polys();
}
inline void DynamicMeshClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
if (!Bounding_Volumes_Valid()) {
Model->Compute_Bounds(NULL);
}
Model->Get_Bounding_Sphere(&sphere);
}
inline void DynamicMeshClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
if (!Bounding_Volumes_Valid()) {
Model->Compute_Bounds(NULL);
}
Model->Get_Bounding_Box(&box);
}
/*
**
*/
void DynamicMeshClass::Switch_To_Multi_Vertex_Color(int color_array_index)
{
/* Vector4 * color_list = Model->Get_Color_Array(color_array_index);
// set the proper color for all the existing vertices
for (int lp = 0; lp < VertCount; lp++) {
color_list[lp].X = CurVertexColor[color_array_index].X;
color_list[lp].Y = CurVertexColor[color_array_index].Y;
color_list[lp].Z = CurVertexColor[color_array_index].Z;
color_list[lp].W = CurVertexColor[color_array_index].W;
}
*/
unsigned * color_list = Model->Get_Color_Array(color_array_index);
// set the proper color for all the existing vertices
unsigned vertex_color=DX8Wrapper::Convert_Color_Clamp(CurVertexColor[color_array_index]);
for (int lp = 0; lp < VertCount; lp++) {
color_list[lp]=vertex_color;
}
MultiVertexColor[color_array_index] = true;
}
/*
** Set the color of all vertices in the mesh for one color_array_index
*/
int DynamicMeshClass::Set_Vertex_Color(const Vector4 & color, int color_array_index)
{
// check if switching to multivertexcolor
if (!MultiVertexColor[color_array_index]) {
Switch_To_Multi_Vertex_Color(color_array_index);
}
CurVertexColor[color_array_index] = color;
return 0;
}
int DynamicMeshClass::Set_Vertex_Color(const Vector3 & color, int color_array_index)
{
Set_Vertex_Color(Vector4(color.X,color.Y,color.Z,1),color_array_index);
return 0;
}
/*
** Dynamic Screen Meshes
**
** Same as DynamicMesh, but mapped in Screen Coordinates
**
** Screen -> 0,0 1,0
** +---+
** | |
** +---+
** 0,1 1,1
**
**
** View -> -1,1 1,1
** +---+
** | |
** +---+
** -1,-1 1,-1
**
** Note: since y is inverted, it switches from right handed to left handed
** (from counter-clockwise to clockwise), so Flip Face accounts for this
*/
class DynamicScreenMeshClass : public DynamicMeshClass {
public:
// constructor and destructor
DynamicScreenMeshClass( int max_poly, int max_vert, float aspect = 1.0f ) : DynamicMeshClass( max_poly, max_vert), Aspect( aspect ) {}
DynamicScreenMeshClass( const DynamicScreenMeshClass & src) : DynamicMeshClass(src), Aspect(src.Aspect) {}
virtual ~DynamicScreenMeshClass( void) {}
// function to clone a dynamic screen mesh class
virtual RenderObjClass * Clone(void) const { return NEW_REF( DynamicScreenMeshClass, (*this)); }
// class id of this render object
virtual int Class_ID(void) const { return CLASSID_DYNASCREENMESH; }
// Remap locations to match a screen
virtual void Location( float x, float y, float z = 0.0f);
// For moving a vertex after the DynaMesh has already been created.
virtual void Move_Vertex(int index, float x, float y, float z = 0.0f);
// Set position
virtual void Set_Position(const Vector3 &v);
virtual void Reset( void);
virtual void Set_Aspect(float aspect) { Aspect=aspect; };
protected:
// Aspect Ratio of the virtual screen.
// 1.0 gives a -1,-1 to 1,1 display
// 3/4 givs a -1,-3/4 to 1,3/4 display
float Aspect;
// tells when the triangle needs to be back flipped
virtual bool Flip_Face( void) { return !DynamicMeshClass::Flip_Face(); }
};
#endif // DYNAMESH

View File

@@ -0,0 +1,429 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/font3d.cpp $*
* *
* $Org Author:: Jani_p $*
* *
* $Author:: Kenny_m $*
* *
* $Modtime:: 08/05/02 10:44a $*
* *
* $Revision:: 17 $*
* *
* 08/05/02 KM Texture class redesign
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "font3d.h"
#include "assetmgr.h"
#include "texture.h"
#include <assert.h>
#include <wwdebug.h>
#include "surfaceclass.h"
#include "texture.h"
#include "vector2i.h"
static SurfaceClass *_surface;
/***********************************************************************************************
* *
* Font3DDataClass::Font3DDataClass -- constructor *
* *
* Constructs and load a Targa font image to create a texture matetial *
* *
***********************************************************************************************/
Font3DDataClass::Font3DDataClass( const char *filename )
{
Texture = NULL;
Load_Font_Image( filename);
Name = strdup( filename);
Name = strupr( Name);
}
/***********************************************************************************************
* *
* Font3DDataClass::~Font3DDataClass -- destructor *
* *
***********************************************************************************************/
Font3DDataClass::~Font3DDataClass(void)
{
if (Name != NULL) {
free(Name);
Name = NULL;
}
REF_PTR_RELEASE(Texture);
}
/***********************************************************************************************
* *
* FontClass::Minimize_Font_Image *
* *
* Rebuilds the give image to better pack characters and to insure a square power of two size *
* Must be called AFTER Make_Proportional() so each chars minimal bounding box is known *
* Will only create a new texture of size 128x128 or 256x256, dependent on original width *
* *
***********************************************************************************************/
SurfaceClass *Font3DDataClass::Minimize_Font_Image( SurfaceClass *surface )
{
SurfaceClass::SurfaceDescription sd;
surface->Get_Description(sd);
float current_width = sd.Width;
float current_height = sd.Height;
// determine new width make the size of the new image either 128x128 or 256x256,
// dependent on the width of the original image
int new_width;
if (current_width < 256) {
new_width = 128;
} else {
new_width = 256;
}
int new_height = new_width;
// create a new 4 bit alpha image to build into
// We dont support non-homogeneous copies just yet
SurfaceClass *new_surface = NEW_REF(SurfaceClass,(new_width, new_height,WW3D_FORMAT_A4R4G4B4));
//SurfaceClass *new_surface0 = NEW_REF(SurfaceClass,(new_width, new_height,sd.Format));
// fill with transparent black
new_surface->Clear();
// indices for the location of each added char
int new_x = 0;
int new_y = 0;
// for each character, copy minimum bounding area to (new_x, new_y) in the new image
for (int char_index = 0; char_index < 256; char_index++) {
// find the lop left coordinate and the height and width of the char's bounding box
// (must convert the normalized uv tables to pixels and round off)
int src_x = (int)(UOffsetTable[ char_index ] * current_width + 0.5);
int src_y = (int)(VOffsetTable[ char_index ] * current_height + 0.5);
int width = (int)(UWidthTable[ char_index ] * current_width + 0.5);
int height = (int)(VHeight * current_height + 0.5);
// if the character has any visible pixels at all...
if (width != 0) {
// if this charactger will not fit on the current line, goto the next line
if (new_x + width > new_width) {
new_x = 0;
new_y += height;
// if we have run out of lines, we have a problem
// we assert because we have already modified tables for some of the chars
if (new_y + height > new_height) {
new_y -= height;
WWDEBUG_SAY(( "Font doesn't fit texture 2 on char %c\n", char_index ));
}
}
// blit from original image to new image
new_surface->Copy(new_x, new_y,src_x,src_y,width,height,surface);
}
// update the U and V tables to show new character location
UOffsetTable[ char_index ] = (float)(new_x) / (float)new_width;
VOffsetTable[ char_index ] = (float)(new_y) / (float)new_width;
// update width in terms of new normal image width
UWidthTable[ char_index ] *= (float)current_width / (float)new_width;
new_x += width;
}
// update height in terms of new normal image height
VHeight *= (float)current_height / (float)new_height;
// be sure the new image is SMALLER than the old image
// assert ( (new_width * new_height) <= (current_width * current_height));
// release the old surface and return the new one
REF_PTR_RELEASE(surface);
_surface = new_surface;
return _surface;
}
/***********************************************************************************************
* *
* FontClass::Make_Proportional *
* *
* Modifys U and Width tables to convert a monospace font into a proportional font. Hieght *
* remains the same. Performed by getting the current mono-space bounding box and bringing *
* in the left and right edges to the first non-transparent ( != 0 ) pixel. Then the U and *
* width tables are updated with the new values. The image itself is not modified unless... *
* *
* we complete by calling Minimize_Font_Image to shink the image & insure a power of 2 square *
* *
***********************************************************************************************/
SurfaceClass *Font3DDataClass::Make_Proportional( SurfaceClass *surface )
{
SurfaceClass::SurfaceDescription sd;
surface->Get_Description(sd);
float width = sd.Width;
float height = sd.Height;
// for each character in the font...
for (int char_index = 0; char_index < 256; char_index++) {
// find the current bounding box
// (must convert the normalized uv tables to pixels and round off)
int x0 = (int)(UOffsetTable[ char_index ] * width + 0.5);
int y0 = (int)(VOffsetTable[ char_index ] * height + 0.5);
int x1 = x0 + (int)(UWidthTable[ char_index ] * width + 0.5);
int y1 = y0 + (int)(VHeight * height + 0.5);
// find minimum bounding box by finding the minimum and maximum non-0 x pixel location
Vector2i minb(x0,y0);
Vector2i maxb(x1,y1);
surface->FindBB(&minb,&maxb);
// set the new edges
x0 = minb.I;
x1 = maxb.I+1;
// if we didn't find ANY non-transparent pixels, the char has no width.
if (x1 < x0) {
x1 = x0;
}
// turn off all character after del
if (char_index > 0x80) {
x1 = x0;
}
// update the U and width tables
UOffsetTable[ char_index ] = (float)x0 / width;
UWidthTable[ char_index ] = (float)( x1 - x0 ) / width;
CharWidthTable[ char_index ] = x1 - x0;
}
// now shink the image given the minimum char sizes
// surface = Minimize_Font_Image( surface );
Minimize_Font_Image( _surface );
return NULL;
}
/***********************************************************************************************
* *
* Font3DDataClass::Load_Font_Image( SR_SCENE *scene, char *filename ) *
* *
* Loads a targa font image file, arranged as 16x16 characters, and builds u v tables to *
* find each character. Converts the mono-space font into a proportional font, then uploads *
* the image to the scene as a textur material. *
* *
***********************************************************************************************/
bool Font3DDataClass::Load_Font_Image( const char *filename )
{
// get the font surface
SurfaceClass *surface = NEW_REF(SurfaceClass,(filename));
WWASSERT(surface);
SurfaceClass::SurfaceDescription sd;
surface->Get_Description(sd);
// If input is a font strike (strip) process it as such
if ( sd.Width > 8 * sd.Height ) {
// the height of the strike is the height of the characters
VHeight = 1;
CharHeight = sd.Height;
int column = 0;
int width = sd.Width;
// for each char, find the uv start location and set the
// mono-spaced width and height in normalized screen units
for (int char_index = 0; char_index < 256; char_index++) {
if ( char_index >= 0x7F ) {
UOffsetTable[ char_index ] = 0;
VOffsetTable[ char_index ] = 0;
UWidthTable[ char_index ] = 0;
CharWidthTable[ char_index ] = 0;
} else {
// find the first non-transparent column...
while (( column < width ) && ( surface->Is_Transparent_Column(column) )) column++;
int start = column;
// find the first transparent column...
while (( column < width ) && ( !surface->Is_Transparent_Column(column) )) column++;
int end = column;
if ( end <= start ) {
WWDEBUG_SAY(( "Error Char %d start %d end %d width %d\n", char_index, start, end, width ));
}
// WWASSERT( end > start );
UOffsetTable[ char_index ] = (float)start / width;
VOffsetTable[ char_index ] = 0;
UWidthTable[ char_index ] = (float)(end - start) / width;
CharWidthTable[ char_index ] = end - start;
}
}
// convert the just created mon-spaced font to proportional (optional)
// surface = Make_Proportional( surface );
_surface = surface;
surface = NULL;
Minimize_Font_Image( _surface );
} else {
// Determine the width and height of each mono spaced character in pixels
// (assumes 16x16 array of chars)
float font_width = sd.Width;
float font_height = sd.Height;
float mono_pixel_width = (font_width / 16);
float mono_pixel_height = (font_height / 16);
// for each char, find the uv start location and set the
// mono-spaced width and height in normalized screen units
for (int char_index = 0; char_index < 256; char_index++) {
UOffsetTable[ char_index ] = (float)((char_index % 16) * mono_pixel_width) / font_width;
VOffsetTable[ char_index ] = (float)((char_index / 16) * mono_pixel_height) / font_height;
UWidthTable[ char_index ] = mono_pixel_width / font_width;
CharWidthTable[ char_index ] = mono_pixel_width;
}
VHeight = mono_pixel_height / font_height;
CharHeight = mono_pixel_height;
// convert the just created mon-spaced font to proportional (optional)
_surface = surface;
surface = NULL;
Make_Proportional( _surface );
}
// create the texture
if ( _surface ) {
Texture = NEW_REF(TextureClass,(_surface,MIP_LEVELS_1));
REF_PTR_RELEASE(_surface);
}
// return SUCCESS!
return true;
}
/***********************************************************************************************
* *
* Font3DInstanceClass::Font3DInstanceClass -- constructor *
* *
* Constructs and load a Targa font image to create a texture matetial *
* *
***********************************************************************************************/
Font3DInstanceClass::Font3DInstanceClass( const char *filename )
{
FontData = WW3DAssetManager::Get_Instance()->Get_Font3DData( filename);
MonoSpacing = 0.0f;
Scale = 1.0f;
SpaceSpacing = (int)(FontData->Char_Width('H') / 2.0f);
InterCharSpacing = 1;
Build_Cached_Tables();
}
/***********************************************************************************************
* *
* Font3DInstanceClass::~Font3DInstanceClass -- destructor *
* *
***********************************************************************************************/
Font3DInstanceClass::~Font3DInstanceClass(void)
{
REF_PTR_RELEASE(FontData);
}
/*
**
*/
void Font3DInstanceClass::Set_Mono_Spaced( void )
{
MonoSpacing = FontData->Char_Width('W') + 1;
Build_Cached_Tables();
}
void Font3DInstanceClass::Build_Cached_Tables()
{
// Rebuild the cached tables
for (int a=0;a<256;++a) {
float width = (float)FontData->Char_Width(a);
if ( a == ' ' ) {
width = SpaceSpacing;
}
ScaledWidthTable[a] = Scale * width;
if (MonoSpacing != 0.0f) {
ScaledSpacingTable[a] = Scale * MonoSpacing;
} else {
ScaledSpacingTable[a] = Scale * (width + InterCharSpacing);
}
}
ScaledHeight = floorf(Scale * (float)FontData->Char_Height('A'));
}
/***********************************************************************************************
* *
* Font3DInstanceClass::String_Screen_Width( char *test_str ) *
* *
* Finds the normalized screenspace width of a character string - useful for checking before *
* printing to avoid overflowing the screen. * *
***********************************************************************************************/
float Font3DInstanceClass::String_Width( const WCHAR *test_str )
{
float width = 0.0;
for (; *test_str; test_str++) {
width += Char_Spacing(*test_str);
}
return width;
}
float Font3DInstanceClass::String_Width( const char *test_str )
{
float width = 0.0;
for (; *test_str; test_str++) {
width += Char_Spacing(*test_str);
}
return width;
}

View File

@@ -0,0 +1,223 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/font3d.h $*
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 4/05/01 2:19p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef FONT3D_H
#define FONT3D_H
#include "always.h"
#include "refcount.h"
#include "vector4.h"
#include "widestring.h"
#include "rect.h"
class TextureClass;
class SurfaceClass;
/******************************************************************
**
** Font3DDataClass
**
** This class provides an interface to a font texture. Once
** created and loaded with a font, the object can return texture
** u v coordinate for any character in the font, as well as the
** character width for proportional fonts. Fonts are loaded as
** 16-bit Targa files, then converted to proportional fonts by
** finding the minimum bounding box for each chacter. The font
** texture is then minimized to a 256x256 or 128x128 texture
** material by re-stacking chars by thier minimum bounding box.
**
** During use, this class is really no more than a data table accessor
** Only during creation is any real code run.
**
** Since the space char nevers needs to be drawn, do not use
** the conventional method of acessing and drawing chars (which will
** still work). Instead, call Get_Space_Width to determine the user-
** settable width, and skip the drawing.
**
*******************************************************************/
class Font3DDataClass : public RefCountClass {
public:
/*
** Constructor, Constructor which loads a targa file,
** and Destructor
*/
Font3DDataClass( const char *filename );
~Font3DDataClass();
// the name of the font data (used for name matching and the like.)
char *Name;
/*
** access character width and height in pixels (clamp char to 0.255)
*/
unsigned char Char_Width( WCHAR ch = (WCHAR)'H' ) { return CharWidthTable[ch&0xFF]; }// & 0xFF]; } // No need to "& 0xff" with chars!!!
unsigned char Char_Height( WCHAR /*ch = 'H'*/ ) { return CharHeight; }
// u and v are in normalized texture space
inline float Char_U_Offset( WCHAR ch = (WCHAR)'H') { return UOffsetTable[ch&0xFF]; }// & 0xFF]; }
inline float Char_V_Offset( WCHAR ch = (WCHAR)'H') { return VOffsetTable[ch&0xFF]; }// & 0xFF]; }
inline float Char_U_Width( WCHAR ch = (WCHAR)'H' ) { return UWidthTable[ch&0xFF]; }// & 0xFF]; }
inline float Char_V_Height( WCHAR /*ch = 'H'*/) { return VHeight; }
// get all four UV values as one vector4
Vector4 Char_UV_Corners( WCHAR ch = (WCHAR)'H' )
{
// ch &= 0xFF;
return Vector4( UOffsetTable[ch], VOffsetTable[ch],
UOffsetTable[ch] + UWidthTable[ch],
VOffsetTable[ch] + VHeight );
}
/*
** access texture material
*/
TextureClass * Peek_Texture( void ) { return Texture; }
private:
/*
** The material (texture) which holds the font
*/
TextureClass * Texture;
/*
** Normalized texture page offsets for each character
*/
float UOffsetTable[ 256 ];
float VOffsetTable[ 256 ];
float UWidthTable[ 256 ];
float VHeight;
unsigned char CharWidthTable[ 256 ];
unsigned char CharHeight;
/*
** load a targa font image (.TGA)
*/
bool Load_Font_Image( const char *filename );
/*
** routines to convert a mono-spaced font to a proportional
** font and minimize texture image size as a result
*/
SurfaceClass *Make_Proportional( SurfaceClass *font_image );
SurfaceClass *Minimize_Font_Image( SurfaceClass *font_image );
};
/******************************************************************
**
** Font3DInstanceClass
**
*******************************************************************/
class Font3DInstanceClass : public RefCountClass {
public:
/*
** Constructor which creates/gets a Font3DDataClass object,
** and Destructor
*/
Font3DInstanceClass( const char *filename );
~Font3DInstanceClass();
/*
** access texture material
*/
TextureClass *Peek_Texture( void ) { return FontData->Peek_Texture(); }
/*
** The non-scaled monospace char width in pixels ( set to 0 for proportional spaced font )
*/
void Set_Mono_Spaced( void );
void Set_Proportional( void ) { MonoSpacing = 0; Build_Cached_Tables(); }
/*
** Set the font scale (default to 1)
** This amount will be automatically applied to all Char_Screen_Width calls
*/
void Set_Scale( float scale ) { Scale = scale; Build_Cached_Tables(); }
// float Get_Scale() const { return Scale; }
/*
** The scaled character pixel width, height, and spacing data (clamp char to 0.255)
*/
float Char_Width( WCHAR ch ) const { return ScaledWidthTable[ch&0xFF]; }
float Char_Spacing( WCHAR ch ) const { return ScaledSpacingTable[ch&0xFF]; }
float Char_Height( void ) const { return ScaledHeight; }
/*
** The scaled pixel width of a string; useful before printing to avoid screen overflows.
*/
float String_Width( const WCHAR *test_str );
float String_Width( const char *test_str );
/*
** Char UVs
*/
// u and v are in normalized texture space
// inline float Char_U_Offset( WCHAR ch = (WCHAR)'H') { return FontData->Char_U_Offset( ch & 0xFF ); }
// inline float Char_V_Offset( WCHAR ch = (WCHAR)'H') { return FontData->Char_V_Offset( ch & 0xFF ); }
// inline float Char_U_Width( WCHAR ch = (WCHAR)'H' ) { return FontData->Char_U_Width( ch & 0xFF ); }
// inline float Char_V_Height( WCHAR ch = (WCHAR)'H') { return FontData->Char_V_Height( ch & 0xFF ); }
// Vector4 Char_UV_Corners( WCHAR ch = (WCHAR)'H' ) { return FontData->Char_UV_Corners( ch & 0xFF ); }
RectClass Char_UV( WCHAR ch ) { return RectClass( FontData->Char_U_Offset(ch),
FontData->Char_V_Offset(ch),
FontData->Char_U_Offset(ch) + FontData->Char_U_Width(ch),
FontData->Char_V_Offset(ch) + FontData->Char_V_Height(ch) ); }
private:
Font3DDataClass * FontData; // The font data
float Scale; // The current scale factor
float SpaceSpacing; // non-scaled width of space in pixels ( defaults to 1/2 'H' width )
float InterCharSpacing; // non-scaled width between chars in pixels
float MonoSpacing; // non-scaled monospace char width in pixels (0 for proportional)
float ScaledWidthTable[256]; // scaled cache of the chars pixel widths
float ScaledSpacingTable[256]; // scaled cache of the chars pixel spacing
float ScaledHeight; // scaled cache of the chars pixel height
void Build_Cached_Tables();
};
#endif

View File

@@ -0,0 +1,251 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/formconv.cpp $*
* *
* Original Author:: Nathaniel Hoffman *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 3 $*
* *
* 06/27/02 KM Z Format support *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "formconv.h"
D3DFORMAT WW3DFormatToD3DFormatConversionArray[WW3D_FORMAT_COUNT] = {
D3DFMT_UNKNOWN,
D3DFMT_R8G8B8,
D3DFMT_A8R8G8B8,
D3DFMT_X8R8G8B8,
D3DFMT_R5G6B5,
D3DFMT_X1R5G5B5,
D3DFMT_A1R5G5B5,
D3DFMT_A4R4G4B4,
D3DFMT_R3G3B2,
D3DFMT_A8,
D3DFMT_A8R3G3B2,
D3DFMT_X4R4G4B4,
D3DFMT_A8P8,
D3DFMT_P8,
D3DFMT_L8,
D3DFMT_A8L8,
D3DFMT_A4L4,
D3DFMT_V8U8, // Bumpmap
D3DFMT_L6V5U5, // Bumpmap
D3DFMT_X8L8V8U8, // Bumpmap
D3DFMT_DXT1,
D3DFMT_DXT2,
D3DFMT_DXT3,
D3DFMT_DXT4,
D3DFMT_DXT5
};
// adding depth stencil format conversion
D3DFORMAT WW3DZFormatToD3DFormatConversionArray[WW3D_ZFORMAT_COUNT] =
{
#ifndef _XBOX
D3DFMT_UNKNOWN,
D3DFMT_D16_LOCKABLE, // 16-bit z-buffer bit depth. This is an application-lockable surface format.
D3DFMT_D32, // 32-bit z-buffer bit depth.
D3DFMT_D15S1, // 16-bit z-buffer bit depth where 15 bits are reserved for the depth channel and 1 bit is reserved for the stencil channel.
D3DFMT_D24S8, // 32-bit z-buffer bit depth using 24 bits for the depth channel and 8 bits for the stencil channel.
D3DFMT_D16, // 16-bit z-buffer bit depth.
D3DFMT_D24X8, // 32-bit z-buffer bit depth using 24 bits for the depth channel.
D3DFMT_D24X4S4, // 32-bit z-buffer bit depth using 24 bits for the depth channel and 4 bits for the stencil channel.
#else
D3DFMT_UNKNOWN,
D3DFMT_D16_LOCKABLE, // 16-bit z-buffer bit depth. This is an application-lockable surface format.
D3DFMT_D32, // 32-bit z-buffer bit depth.
D3DFMT_D15S1, // 16-bit z-buffer bit depth where 15 bits are reserved for the depth channel and 1 bit is reserved for the stencil channel.
D3DFMT_D24S8, // 32-bit z-buffer bit depth using 24 bits for the depth channel and 8 bits for the stencil channel.
D3DFMT_D16, // 16-bit z-buffer bit depth.
D3DFMT_D24X8, // 32-bit z-buffer bit depth using 24 bits for the depth channel.
D3DFMT_D24X4S4, // 32-bit z-buffer bit depth using 24 bits for the depth channel and 4 bits for the stencil channel.
D3DFMT_LIN_D24S8,
D3DFMT_LIN_F24S8,
D3DFMT_LIN_D16,
D3DFMT_LIN_F16
#endif
};
/*
#define HIGHEST_SUPPORTED_D3DFORMAT D3DFMT_X8L8V8U8 //A4L4
WW3DFormat D3DFormatToWW3DFormatConversionArray[HIGHEST_SUPPORTED_D3DFORMAT + 1] = {
WW3D_FORMAT_UNKNOWN, // 0
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_R8G8B8, // 20
WW3D_FORMAT_A8R8G8B8,
WW3D_FORMAT_X8R8G8B8,
WW3D_FORMAT_R5G6B5,
WW3D_FORMAT_X1R5G5B5,
WW3D_FORMAT_A1R5G5B5,
WW3D_FORMAT_A4R4G4B4,
WW3D_FORMAT_R3G3B2,
WW3D_FORMAT_A8,
WW3D_FORMAT_A8R3G3B2,
WW3D_FORMAT_X4R4G4B4, // 30
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_A8P8, // 40
WW3D_FORMAT_P8,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN, WW3D_FORMAT_UNKNOWN,
WW3D_FORMAT_L8, // 50
WW3D_FORMAT_A8L8,
WW3D_FORMAT_A4L4
};
*/
#ifndef _XBOX
#define HIGHEST_SUPPORTED_D3DFORMAT D3DFMT_X8L8V8U8
#define HIGHEST_SUPPORTED_D3DZFORMAT D3DFMT_D24X4S4
#else
#define HIGHEST_SUPPORTED_D3DFORMAT D3DFMT_LIN_R8G8B8A8
#define HIGHEST_SUPPORTED_D3DZFORMAT D3DFMT_LIN_F16
#endif
WW3DFormat D3DFormatToWW3DFormatConversionArray[HIGHEST_SUPPORTED_D3DFORMAT + 1];
WW3DZFormat D3DFormatToWW3DZFormatConversionArray[HIGHEST_SUPPORTED_D3DZFORMAT + 1];
D3DFORMAT WW3DFormat_To_D3DFormat(WW3DFormat ww3d_format) {
if (ww3d_format >= WW3D_FORMAT_COUNT) {
return D3DFMT_UNKNOWN;
} else {
return WW3DFormatToD3DFormatConversionArray[(unsigned int)ww3d_format];
}
}
WW3DFormat D3DFormat_To_WW3DFormat(D3DFORMAT d3d_format)
{
switch (d3d_format) {
// The DXT-codes are created with FOURCC macro and thus can't be placed in the conversion table
case D3DFMT_DXT1: return WW3D_FORMAT_DXT1;
case D3DFMT_DXT2: return WW3D_FORMAT_DXT2;
case D3DFMT_DXT3: return WW3D_FORMAT_DXT3;
case D3DFMT_DXT4: return WW3D_FORMAT_DXT4;
case D3DFMT_DXT5: return WW3D_FORMAT_DXT5;
default:
if (d3d_format > HIGHEST_SUPPORTED_D3DFORMAT) {
return WW3D_FORMAT_UNKNOWN;
} else {
return D3DFormatToWW3DFormatConversionArray[(unsigned int)d3d_format];
}
break;
}
}
//**********************************************************************************************
//! Depth Stencil W3D to D3D format conversion
/*! KJM
*/
D3DFORMAT WW3DZFormat_To_D3DFormat(WW3DZFormat ww3d_zformat)
{
if (ww3d_zformat >= WW3D_ZFORMAT_COUNT)
{
return D3DFMT_UNKNOWN;
}
else
{
return WW3DZFormatToD3DFormatConversionArray[(unsigned int)ww3d_zformat];
}
}
//**********************************************************************************************
//! D3D to Depth Stencil W3D format conversion
/*! KJM
*/
WW3DZFormat D3DFormat_To_WW3DZFormat(D3DFORMAT d3d_format)
{
if (d3d_format>HIGHEST_SUPPORTED_D3DZFORMAT)
{
return WW3D_ZFORMAT_UNKNOWN;
}
else
{
return D3DFormatToWW3DZFormatConversionArray[(unsigned int)d3d_format];
}
}
//**********************************************************************************************
//! Init format conversion tables
/*!
* 06/27/02 KM Z Format support *
*/
void Init_D3D_To_WW3_Conversion()
{
for (int i=0;i<HIGHEST_SUPPORTED_D3DFORMAT;++i) {
D3DFormatToWW3DFormatConversionArray[i]=WW3D_FORMAT_UNKNOWN;
}
D3DFormatToWW3DFormatConversionArray[D3DFMT_R8G8B8]=WW3D_FORMAT_R8G8B8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A8R8G8B8]=WW3D_FORMAT_A8R8G8B8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_X8R8G8B8]=WW3D_FORMAT_X8R8G8B8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_R5G6B5]=WW3D_FORMAT_R5G6B5;
D3DFormatToWW3DFormatConversionArray[D3DFMT_X1R5G5B5]=WW3D_FORMAT_X1R5G5B5;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A1R5G5B5]=WW3D_FORMAT_A1R5G5B5;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A4R4G4B4]=WW3D_FORMAT_A4R4G4B4;
D3DFormatToWW3DFormatConversionArray[D3DFMT_R3G3B2]=WW3D_FORMAT_R3G3B2;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A8]=WW3D_FORMAT_A8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A8R3G3B2]=WW3D_FORMAT_A8R3G3B2;
D3DFormatToWW3DFormatConversionArray[D3DFMT_X4R4G4B4]=WW3D_FORMAT_X4R4G4B4;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A8P8]=WW3D_FORMAT_A8P8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_P8]=WW3D_FORMAT_P8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_L8]=WW3D_FORMAT_L8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A8L8]=WW3D_FORMAT_A8L8;
D3DFormatToWW3DFormatConversionArray[D3DFMT_A4L4]=WW3D_FORMAT_A4L4;
D3DFormatToWW3DFormatConversionArray[D3DFMT_V8U8]=WW3D_FORMAT_U8V8; // Bumpmap
D3DFormatToWW3DFormatConversionArray[D3DFMT_L6V5U5]=WW3D_FORMAT_L6V5U5; // Bumpmap
D3DFormatToWW3DFormatConversionArray[D3DFMT_X8L8V8U8]=WW3D_FORMAT_X8L8V8U8; // Bumpmap
// init depth stencil conversion
for (i=0; i<HIGHEST_SUPPORTED_D3DZFORMAT; i++)
{
D3DFormatToWW3DZFormatConversionArray[i]=WW3D_ZFORMAT_UNKNOWN;
}
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D16_LOCKABLE]=WW3D_ZFORMAT_D16_LOCKABLE;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D32]=WW3D_ZFORMAT_D32;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D15S1]=WW3D_ZFORMAT_D15S1;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D24S8]=WW3D_ZFORMAT_D24S8;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D16]=WW3D_ZFORMAT_D16;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D24X8]=WW3D_ZFORMAT_D24X8;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_D24X4S4]=WW3D_ZFORMAT_D24X4S4;
#ifdef _XBOX
D3DFormatToWW3DZFormatConversionArray[D3DFMT_LIN_D24S8]=WW3D_ZFORMAT_LIN_D24S8;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_LIN_F24S8]=WW3D_ZFORMAT_LIN_F24S8;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_LIN_D16]=WW3D_ZFORMAT_LIN_D16;
D3DFormatToWW3DZFormatConversionArray[D3DFMT_LIN_F16]=WW3D_ZFORMAT_LIN_F16;
#endif
};

View File

@@ -0,0 +1,62 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/formconv.h $*
* *
* Original Author:: Nathaniel Hoffman *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/27/02 1:27p $*
* *
* $Revision:: 3 $*
* *
* 06/27/02 KM Z Format support *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef FORMCONV_H
#define FORMCONV_H
#include "ww3dformat.h"
#include <d3d8.h>
/*
** This file is used for conversions between D3DFORMAT and WW3DFormat.
*/
D3DFORMAT WW3DFormat_To_D3DFormat(WW3DFormat ww3d_format);
WW3DFormat D3DFormat_To_WW3DFormat(D3DFORMAT d3d_format);
D3DFORMAT WW3DZFormat_To_D3DFormat(WW3DZFormat ww3d_zformat);
WW3DZFormat D3DFormat_To_WW3DZFormat(D3DFORMAT d3d_format);
void Init_D3D_To_WW3_Conversion();
#endif

View File

@@ -0,0 +1,117 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/framgrab.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef FRAMEGRAB_H
#define FRAMEGRAB_H
#ifndef ALWAYS_H
#include "always.h"
#endif
#if defined (_MSC_VER)
#pragma warning (push, 3) // (gth) system headers complain at warning level 4...
#endif
#ifndef _WINDOWS_
#include "windows.h"
#endif
#ifndef _INC_WINDOWSX
#include "windowsx.h"
#endif
#ifndef _INC_VFW
#include "vfw.h"
#endif
#if defined (_MSC_VER)
#pragma warning (pop)
#endif
// FramGrab.h: interface for the FrameGrabClass class.
//
//////////////////////////////////////////////////////////////////////
class FrameGrabClass
{
public:
enum MODE {
RAW,
AVI
};
// depending on which mode you select, it will produce either frames or an AVI.
FrameGrabClass(const char *filename, MODE mode, int width, int height, int bitdepth, float framerate );
virtual ~FrameGrabClass();
void ConvertGrab(void *BitmapPointer);
void Grab(void *BitmapPointer);
long * GetBuffer() { return Bitmap; }
float GetFrameRate() { return FrameRate; }
protected:
const char *Filename;
float FrameRate;
MODE Mode;
long Counter; // used for incrementing filename cunter, etc.
void GrabAVI(void *BitmapPointer);
void GrabRawFrame(void *BitmapPointer);
// avi settings
PAVIFILE AVIFile;
long *Bitmap;
PAVISTREAM Stream;
AVISTREAMINFO AVIStreamInfo;
BITMAPINFOHEADER BitmapInfoHeader;
// general purpose cleanup routine
void CleanupAVI();
// convert the SR image into AVI byte ordering
void ConvertFrame(void *BitmapPointer);
};
#endif

View File

@@ -0,0 +1,468 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hanim.cpp 3 12/13/01 7:01p Patrick $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hanim.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/13/01 6:54p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hanim.h"
#include "assetmgr.h"
#include "htree.h"
#include "motchan.h"
#include "chunkio.h"
#include "w3d_file.h"
#include "wwdebug.h"
#include <string.h>
#include <nstrdup.h>
/*
**
** HAnimComboClass
**
**
*/
NamedPivotMapClass::~NamedPivotMapClass(void)
{
}
NamedPivotMapClass::WeightInfoStruct & NamedPivotMapClass::WeightInfoStruct::operator = (WeightInfoStruct const &that)
{
if(Name) delete [] Name;
assert(that.Name != 0);
Name = nstrdup(that.Name);
Weight = that.Weight;
return *this;
}
// add a name & weight to the arrays
void NamedPivotMapClass::Add(const char *Name, float Weight)
{
WeightInfoStruct info;
info.Name = (char *) Name;
info.Weight = Weight;
WeightInfo.Add(info);
info.Name = 0;
}
// configure the base pivot map using the specified tree
void NamedPivotMapClass::Update_Pivot_Map(const HTreeClass *Tree)
{
// first, resize the base pivot map to fit the tree
int numPivots = Tree->Num_Pivots();
Resize(numPivots);
ActiveCount = numPivots;
// first, reset all weights to 1 (the default)
while(numPivots--) {
(*this)[numPivots] = 1.0f;
}
// for each named pivot, find the correct index and set the weight if the indicated bone is present
// note: there is no check for redundant names
int count = WeightInfo.Count();
while(count--) {
int actualPivot = Tree->Get_Bone_Index(WeightInfo[count].Name);
if(actualPivot != -1) {
(*this)[actualPivot] = WeightInfo[count].Weight;
}
}
}
/*
**
*/
DEFINE_AUTO_POOL(HAnimComboDataClass,256);
HAnimComboDataClass::HAnimComboDataClass(bool shared)
: Shared(shared), HAnim(0), PivotMap(0), Frame(0), PrevFrame(0), Weight(1)
{}
HAnimComboDataClass::HAnimComboDataClass(const HAnimComboDataClass &src)
: PivotMap(src.Get_Pivot_Map()),
HAnim(src.Get_HAnim())
{
Shared = src.Is_Shared();
Frame = src.Get_Frame();
PrevFrame = src.Get_Prev_Frame();
Weight = src.Get_Weight();
}
void HAnimComboDataClass::Copy(const HAnimComboDataClass *src)
{
if(src) {
HAnim = src->Get_HAnim();
PivotMap = src->Get_Pivot_Map();
Frame = src->Get_Frame();
PrevFrame = src->Get_Prev_Frame();
Weight = src->Get_Weight();
} else {
HAnim = 0;
PivotMap = 0;
Frame = 0;
PrevFrame = 0;
Weight = 1;
}
}
HAnimComboDataClass::~HAnimComboDataClass(void)
{
if(HAnim)
HAnim->Release_Ref();
if(PivotMap)
PivotMap->Release_Ref();
}
void HAnimComboDataClass::Clear(void)
{
if ( HAnim != NULL ) {
HAnim->Release_Ref();
HAnim = NULL;
}
// not sure if the pivot map should be deleted or just have everything set to one.
// removing it effectively sets it to one, so that's what I'm doing for now.
if(PivotMap) {
PivotMap->Release_Ref();
PivotMap = NULL;
}
Frame = 0.0f;
PrevFrame = 0.0f;
Weight = 1.0;
PivotMap = NULL;
}
void HAnimComboDataClass::Set_HAnim(HAnimClass *motion)
{
if ( motion != NULL ) {
motion->Add_Ref();
}
if ( HAnim != NULL ) {
HAnim->Release_Ref();
}
HAnim = motion;
}
void HAnimComboDataClass::Set_Pivot_Map(PivotMapClass *map)
{
if ( map != NULL ) {
map->Add_Ref();
}
if ( PivotMap != NULL ) {
PivotMap->Release_Ref();
}
PivotMap = map;
}
/*
** This function will replace the current pivot map (if any) with another pivot map that is
** set to 1 for only those pivot indices that actually have data.
*/
void HAnimComboDataClass::Build_Active_Pivot_Map(void)
{
if ( PivotMap != NULL ) {
PivotMap->Release_Ref();
}
if(HAnim == NULL) {
PivotMap = 0;
return;
}
int numpivots = HAnim->Get_Num_Pivots();
PivotMap = NEW_REF( PivotMapClass, ());
PivotMap->Resize(numpivots);
int count = 0;
while(count < numpivots) {
if(HAnim->Is_Node_Motion_Present(count)) {
PivotMap->Add(1);
} else {
PivotMap->Add(0);
}
count++;
}
}
/*
** HAnimComboClass
**
**
*/
HAnimComboClass::HAnimComboClass(void)
{}
HAnimComboClass::HAnimComboClass( int num_animations )
{
HAnimComboData.Resize(num_animations);
while(num_animations--) {
HAnimComboData.Add(new HAnimComboDataClass());
}
}
HAnimComboClass::~HAnimComboClass(void)
{
Reset();
}
void HAnimComboClass::Clear( void )
{
int numAnimations = HAnimComboData.Count();
while ( numAnimations-- ) {
HAnimComboDataClass *data = HAnimComboData[numAnimations];
if(data && (! data->Is_Shared()))
data->Clear();
}
}
void HAnimComboClass::Reset( void )
{
int numAnimations = HAnimComboData.Count();
while ( numAnimations-- ) {
HAnimComboDataClass *data = HAnimComboData[numAnimations];
if(data && (! data->Is_Shared())) {
delete data;
}
}
HAnimComboData.Reset_Active();
}
bool HAnimComboClass::Normalize_Weights(void)
{
// NOTE: This can only work if either no anims have pivot weight maps (in which case we will
// adjust the anim weights to ensure normalization), or else if all do (in which case we will
// adjust the pivot maps). Otherwise we do nothing and return false.
int anim_count = Get_Num_Anims();
if (!anim_count) return true; // Trivially succeeded
// Loop over all anims. Check if all or none have pivot maps, and also calculate the minimum
// number of pivots.
int anim_idx = 0;
bool all_pivot_maps = true;
bool none_pivot_maps = true;
int num_anim_pivots = 100000;
for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
num_anim_pivots = MIN(num_anim_pivots, Peek_Motion(anim_idx)->Get_Num_Pivots());
bool has_pivot_map = Peek_Pivot_Weight_Map(anim_idx) != NULL;
all_pivot_maps &= has_pivot_map;
none_pivot_maps &= !has_pivot_map;
}
if ( num_anim_pivots == 100000 ) {
num_anim_pivots = 0;
}
if (none_pivot_maps) {
// Calculate total weight of all active anims, ensure it is very close to 1.
float weight_total = 0.0f;
for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
if (Peek_Motion(anim_idx) != NULL ) {
float weight = Get_Weight(anim_idx);
weight_total += weight;
}
}
// weight_total should be very close to 1. If not, normalize this pivot's weights
if (weight_total != 0.0 && WWMath::Fabs( weight_total - 1.0 ) > WWMATH_EPSILON) {
float oo_total = 1.0f / weight_total;
for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
if (Peek_Motion(anim_idx) != NULL ) {
Set_Weight(anim_idx, Get_Weight(anim_idx) * oo_total);
}
}
}
} else {
if (all_pivot_maps) {
// For each pivot, calculate total weight of all active anims, ensure close to 1.
for (int piv_idx = 1; piv_idx < num_anim_pivots; piv_idx++) {
float weight_total = 0.0f;
for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
if (Peek_Motion(anim_idx) != NULL ) {
float weight = Get_Weight(anim_idx) * (*Peek_Pivot_Weight_Map(anim_idx))[piv_idx];
weight_total += weight;
}
}
// weight_total should be very close to 1. If not, normalize this pivot's weights
if (weight_total != 0.0 && WWMath::Fabs( weight_total - 1.0 ) > WWMATH_EPSILON) {
float oo_total = 1.0f / weight_total;
for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
if (Peek_Motion(anim_idx) != NULL ) {
PivotMapClass *pivot_map = Get_Pivot_Weight_Map(anim_idx);
float new_weight = (*pivot_map)[piv_idx] * oo_total;
(*pivot_map)[piv_idx] = new_weight;
pivot_map->Release_Ref();
}
}
}
}
} else {
return false;
}
}
return true;
}
void HAnimComboClass::Set_Motion( int index, HAnimClass *motion )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
data->Set_HAnim(motion);
}
HAnimClass *HAnimComboClass::Get_Motion( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
HAnimClass *anim = data->Peek_HAnim();
if ( anim != NULL ) {
anim->Add_Ref();
}
return anim;
}
HAnimClass *HAnimComboClass::Peek_Motion( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
HAnimClass *anim = data->Peek_HAnim();
return anim;
}
void HAnimComboClass::Set_Frame( int index, float frame )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
data->Set_Frame(frame);
}
float HAnimComboClass::Get_Frame( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
return data->Get_Frame();
}
void HAnimComboClass::Set_Prev_Frame( int index, float frame )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
data->Set_Prev_Frame(frame);
}
float HAnimComboClass::Get_Prev_Frame( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
return data->Get_Prev_Frame();
}
void HAnimComboClass::Set_Weight( int index, float weight )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
data->Set_Weight(weight);
}
float HAnimComboClass::Get_Weight( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
return data->Get_Weight();
}
void HAnimComboClass::Set_Pivot_Weight_Map( int index, PivotMapClass *map )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
data->Set_Pivot_Map(map);
}
PivotMapClass *HAnimComboClass::Get_Pivot_Weight_Map( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
return data->Get_Pivot_Map();
}
PivotMapClass *HAnimComboClass::Peek_Pivot_Weight_Map( int index )
{
HAnimComboDataClass *data = HAnimComboData[index];
WWASSERT(data);
return data->Peek_Pivot_Map();
}
void HAnimComboClass::Append_Anim_Combo_Data(HAnimComboDataClass * Data)
{
HAnimComboData.Add(Data);
}
void HAnimComboClass::Remove_Anim_Combo_Data(HAnimComboDataClass * Data)
{
HAnimComboData.Delete(Data);
}

View File

@@ -0,0 +1,273 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hanim.h 3 12/13/01 7:01p Patrick $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hanim.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/13/01 6:54p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HANIM_H
#define HANIM_H
#include "always.h"
#include "quat.h"
#include "refcount.h"
#include "w3d_file.h"
#include "hash.h"
#include "mempool.h"
#include <refcount.h>
#include <slist.h>
#include <vector.h>
struct NodeMotionStruct;
class MotionChannelClass;
class BitChannelClass;
class HTreeClass;
class ChunkLoadClass;
class ChunkSaveClass;
class HTreeClass;
#define EMBEDDED_SOUND_BONE_INDEX_NOT_SET -1
/**********************************************************************************
HAnimClass
This is the base class for all animation formats used in W3D. It
contains the virtual interface that all animations must support.
**********************************************************************************/
class HAnimClass : public RefCountClass, public HashableClass
{
public:
enum
{
CLASSID_UNKNOWNANIM = 0xFFFFFFFF,
CLASSID_HRAWANIM = 0,
CLASSID_LASTANIM = 0x0000FFFF
};
HAnimClass(void) :
EmbeddedSoundBoneIndex (EMBEDDED_SOUND_BONE_INDEX_NOT_SET) { }
virtual ~HAnimClass(void) { }
virtual const char * Get_Name(void) const = 0;
virtual const char * Get_HName(void) const = 0;
virtual const char * Get_Key( void ) { return Get_Name(); }
virtual int Get_Num_Frames(void) = 0;
virtual float Get_Frame_Rate() = 0;
virtual float Get_Total_Time() = 0;
// virtual Vector3 Get_Translation(int pividx,float frame) = 0;
// virtual Quaternion Get_Orientation(int pividx,float frame) = 0;
// Jani: Changed to pass in reference of destination to avoid copying
virtual void Get_Translation(int pividx,float frame) {} // todo: remove
virtual void Get_Orientation(int pividx,float frame) {} // todo: remove
virtual void Get_Translation(Vector3& translation, int pividx,float frame) const = 0;
virtual void Get_Orientation(Quaternion& orientation, int pividx,float frame) const = 0;
virtual void Get_Transform(Matrix3D&, int pividx, float frame) const = 0;
virtual bool Get_Visibility(int pividx,float frame) = 0;
virtual int Get_Num_Pivots(void) const = 0;
virtual bool Is_Node_Motion_Present(int pividx) = 0;
// Methods that test the presence of a certain motion channel.
virtual bool Has_X_Translation (int pividx) { return true; }
virtual bool Has_Y_Translation (int pividx) { return true; }
virtual bool Has_Z_Translation (int pividx) { return true; }
virtual bool Has_Rotation (int pividx) { return true; }
virtual bool Has_Visibility (int pividx) { return true; }
virtual int Class_ID(void) const { return CLASSID_UNKNOWNANIM; }
// Animated sound-triggering support
virtual bool Has_Embedded_Sounds (void) const { if (EmbeddedSoundBoneIndex < 0) return false; return true;}
virtual void Set_Embedded_Sound_Bone_Index (int bone) { EmbeddedSoundBoneIndex = bone; }
virtual int Get_Embedded_Sound_Bone_Index() {return EmbeddedSoundBoneIndex;}
protected:
int EmbeddedSoundBoneIndex;
};
/*
** The PivotMapClass is used by the HAnimComboDataClass (sometimes) to keep track of animation
** weights per-pivot point.
*/
class NamedPivotMapClass;
class PivotMapClass : public DynamicVectorClass<float>, public RefCountClass
{
public:
virtual NamedPivotMapClass * As_Named_Pivot_Map() { return 0; }
};
/*
** The NamedPivotMapClass allows us to create HAnimComboDataClass objects with pivot maps without
** having to have the HAnim available. Later, when an HAnim is retrieved from the asset manager,
** the pivot map can be updated to produce the correct map.
*/
class NamedPivotMapClass : public PivotMapClass
{
public:
~NamedPivotMapClass(void);
virtual NamedPivotMapClass * As_Named_Pivot_Map() { return this; }
// add a name & weight to the arrays
void Add(const char *Name, float Weight);
// configure the base pivot map using the specified tree
void Update_Pivot_Map(const HTreeClass *Tree);
private:
// This info is packaged into a struct to minimize DynamicVectorClass overhead
struct WeightInfoStruct {
WeightInfoStruct() : Name(0) {}
~WeightInfoStruct() { if(Name) delete [] Name; }
char *Name;
float Weight;
// operators required for the DynamicVectorClass
WeightInfoStruct & operator = (WeightInfoStruct const &that);
bool operator == (WeightInfoStruct const &that) const { return &that == this; }
bool operator != (WeightInfoStruct const &that) const { return &that != this; }
};
DynamicVectorClass<WeightInfoStruct> WeightInfo;
};
/*
** The HAnimComboDataClass is used by the new HAnimComboClass to allow for a mix of shared/unshared data
** which will allow us to have the anim combo refer to data whereever we wish to put it.
*/
class HAnimComboDataClass : public AutoPoolClass<HAnimComboDataClass,256> {
public:
HAnimComboDataClass(bool shared = false);
HAnimComboDataClass(const HAnimComboDataClass &);
~HAnimComboDataClass(void);
void Copy(const HAnimComboDataClass *);
void Clear(void);
void Set_HAnim(HAnimClass *motion);
void Give_HAnim(HAnimClass *motion) { if(HAnim) HAnim->Release_Ref(); HAnim = motion; } // used for giving this object the reference
void Set_Frame(float frame) { PrevFrame = Frame; Frame = frame; }
void Set_Prev_Frame(float frame) { PrevFrame = frame; }
void Set_Weight(float weight) { Weight = weight; }
void Set_Pivot_Map(PivotMapClass *map);
HAnimClass * Peek_HAnim(void) const { return HAnim; } // note: does not add reference
HAnimClass * Get_HAnim(void) const { if(HAnim) HAnim->Add_Ref(); return HAnim; } // note: does not add reference
float Get_Frame(void) const { return Frame; }
float Get_Prev_Frame(void) const { return PrevFrame; }
float Get_Weight(void) const { return Weight; }
PivotMapClass * Peek_Pivot_Map(void) const { return PivotMap; }
PivotMapClass * Get_Pivot_Map(void) const { if(PivotMap) PivotMap->Add_Ref(); return PivotMap; }
bool Is_Shared(void) const { return Shared; }
void Build_Active_Pivot_Map(void);
private:
HAnimClass *HAnim;
float Frame;
float PrevFrame;
float Weight;
PivotMapClass * PivotMap;
bool Shared; // this is set to false when the HAnimCombo allocates it
};
/*
** defines a combination of animations for blending/mixing
*/
class HAnimComboClass {
public:
HAnimComboClass(void);
HAnimComboClass( int num_animations ); // allocates specified number of channels and sets then all to not-shared
~HAnimComboClass(void);
void Clear( void ); // zeros all data
void Reset( void ); // empties the dynamic vector
bool Normalize_Weights(void); // Normalizes all weights (returns true if succeeded)
int Get_Num_Anims( void ) { return HAnimComboData.Count(); }
void Set_Motion( int indx, HAnimClass *motion );
HAnimClass *Get_Motion( int indx );
HAnimClass *Peek_Motion( int indx );
void Set_Frame( int indx, float frame );
void Set_Prev_Frame( int indx, float frame );
float Get_Frame( int indx );
float Get_Prev_Frame( int indx );
void Set_Weight( int indx, float weight );
float Get_Weight( int indx );
void Set_Pivot_Weight_Map( int indx, PivotMapClass * map );
PivotMapClass * Get_Pivot_Weight_Map( int indx );
PivotMapClass * Peek_Pivot_Weight_Map( int indx );
// add an entry to the dynamic vector
void Append_Anim_Combo_Data(HAnimComboDataClass * AnimComboData);
// remove an entry from the vector
void Remove_Anim_Combo_Data(HAnimComboDataClass * AnimComboData);
// retrieve a specific combo data
HAnimComboDataClass * Peek_Anim_Combo_Data(int index) { return HAnimComboData[index]; }
protected:
DynamicVectorClass<HAnimComboDataClass *> HAnimComboData;
};
#endif

View File

@@ -0,0 +1,458 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hanimmgr.cpp 3 1/16/02 9:51a Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hanimmgr.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 1/16/02 9:49a $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* HAnimManagerClass::HAnimManagerClass -- constructor *
* HAnimManagerClass::~HAnimManagerClass -- destructor *
* HAnimManagerClass::Load_Anim -- loads a set of motion data from a file *
* HAnimManagerClass::Get_Anim_ID -- looks up the ID of a named Hierarchy Animation *
* HAnimManagerClass::Get_Anim -- returns a pointer to the specified animation data *
* HAnimManagerClass::Get_Anim -- returns a pointer to the specified Hierarchy Animation *
* HAnimManagerClass::Free -- de-allocate all memory in use *
* HAnimManagerClass::Free_All_Anims -- de-allocate all currently loaded animations *
* HAnimManagerClass::Load_Raw_Anim -- Load a raw anim *
* HAnimManagerClass::Load_Compressed_Anim -- load a compressed animation *
* HAnimManagerClass::Add_Anim -- Adds an externally created animation to the manager *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hanimmgr.h"
#include <string.h>
#include "hanim.h"
#include "hrawanim.h"
#include "hcanim.h"
#include "hmorphanim.h"
#include "chunkio.h"
#include "wwmemlog.h"
#include "w3dexclusionlist.h"
#include "animatedsoundmgr.h"
/***********************************************************************************************
* HAnimManagerClass::HAnimManagerClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HAnimManagerClass::HAnimManagerClass(void)
{
// Create the hash tables
AnimPtrTable = W3DNEW HashTableClass( 2048 );
MissingAnimTable = W3DNEW HashTableClass( 2048 );
}
/***********************************************************************************************
* HAnimManagerClass::~HAnimManagerClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HAnimManagerClass::~HAnimManagerClass(void)
{
Free_All_Anims();
Reset_Missing(); // Jani: Deleting missing animations as well
delete AnimPtrTable;
AnimPtrTable = NULL;
Reset_Missing();
delete MissingAnimTable;
MissingAnimTable = NULL;
}
/***********************************************************************************************
* HAnimManagerClass::Load_Anim -- loads a set of motion data from a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
int HAnimManagerClass::Load_Anim(ChunkLoadClass & cload)
{
WWMEMLOG(MEM_ANIMATION);
switch (cload.Cur_Chunk_ID())
{
case W3D_CHUNK_ANIMATION:
return Load_Raw_Anim(cload);
break;
case W3D_CHUNK_COMPRESSED_ANIMATION:
return Load_Compressed_Anim(cload);
break;
case W3D_CHUNK_MORPH_ANIMATION:
return Load_Morph_Anim(cload);
break;
}
return 0;
}
/***********************************************************************************************
* HAnimManagerClass::Load_Morph_Anim -- Load a HMorphAnimClass *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/23/2000 pds : Created. *
*=============================================================================================*/
int HAnimManagerClass::Load_Morph_Anim(ChunkLoadClass & cload)
{
HMorphAnimClass * newanim = W3DNEW HMorphAnimClass;
if (newanim == NULL) {
goto Error;
}
SET_REF_OWNER( newanim );
if (newanim->Load_W3D(cload) != HMorphAnimClass::OK) {
// load failed!
newanim->Release_Ref();
goto Error;
} else if (Peek_Anim(newanim->Get_Name()) != NULL) {
// duplicate exists!
newanim->Release_Ref(); // Release the one we just loaded
goto Error;
} else {
Add_Anim( newanim );
newanim->Release_Ref();
}
return 0;
Error:
return 1;
}
/***********************************************************************************************
* HAnimManagerClass::Load_Raw_Anim -- Load a raw anim *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/23/2000 gth : Created. *
*=============================================================================================*/
int HAnimManagerClass::Load_Raw_Anim(ChunkLoadClass & cload)
{
HRawAnimClass * newanim = W3DNEW HRawAnimClass;
if (newanim == NULL) {
goto Error;
}
SET_REF_OWNER( newanim );
if (newanim->Load_W3D(cload) != HRawAnimClass::OK) {
// load failed!
newanim->Release_Ref();
goto Error;
} else if (Peek_Anim(newanim->Get_Name()) != NULL) {
// duplicate exists!
newanim->Release_Ref(); // Release the one we just loaded
goto Error;
} else {
Add_Anim( newanim );
newanim->Release_Ref();
}
return 0;
Error:
return 1;
}
/***********************************************************************************************
* HAnimManagerClass::Load_Compressed_Anim -- load a compressed animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/23/2000 gth : Created. *
*=============================================================================================*/
int HAnimManagerClass::Load_Compressed_Anim(ChunkLoadClass & cload)
{
HCompressedAnimClass * newanim = W3DNEW HCompressedAnimClass;
if (newanim == NULL) {
goto Error;
}
SET_REF_OWNER( newanim );
if (newanim->Load_W3D(cload) != HCompressedAnimClass::OK) {
// load failed!
newanim->Release_Ref();
goto Error;
} else if (Peek_Anim(newanim->Get_Name()) != NULL) {
// duplicate exists!
newanim->Release_Ref(); // Release the one we just loaded
goto Error;
} else {
Add_Anim( newanim );
newanim->Release_Ref();
}
return 0;
Error:
return 1;
}
/***********************************************************************************************
* HAnimManagerClass::Peek_Anim -- returns a pointer to the specified animation data *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HAnimClass * HAnimManagerClass::Peek_Anim(const char * name)
{
return (HAnimClass*)AnimPtrTable->Find( name );
}
/***********************************************************************************************
* HAnimManagerClass::Get_Anim -- returns a pointer to the specified animation data *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HAnimClass * HAnimManagerClass::Get_Anim(const char * name)
{
HAnimClass * anim = Peek_Anim( name );
if ( anim != NULL ) {
anim->Add_Ref();
}
return anim;
}
/***********************************************************************************************
* HAnimManagerClass::Free_All_Anims -- de-allocate all currently loaded animations *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HAnimManagerClass::Free_All_Anims(void)
{
// Make an iterator, and release all ptrs
HAnimManagerIterator it( *this );
for( it.First(); !it.Is_Done(); it.Next() ) {
HAnimClass *anim = it.Get_Current_Anim();
anim->Release_Ref();
}
// Then clear the table
AnimPtrTable->Reset();
}
/***********************************************************************************************
* HAnimManagerClass::Free_All_Anims_With_Exclusion_List -- release animations not in the list *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/12/2002 GH : Created. *
*=============================================================================================*/
void HAnimManagerClass::Free_All_Anims_With_Exclusion_List(const W3DExclusionListClass & exclusion_list)
{
// Remove and Release_Ref any animation not in the exclusion list.
HAnimManagerIterator it( *this );
for( it.First(); !it.Is_Done(); it.Next() ) {
HAnimClass *anim = it.Get_Current_Anim();
if ((anim->Num_Refs() == 1) && (exclusion_list.Is_Excluded(anim) == false)) {
//WWDEBUG_SAY(("deleting HAnim %s\n",anim->Get_Name()));
AnimPtrTable->Remove(anim);
anim->Release_Ref();
}
//else
//{
// WWDEBUG_SAY(("keeping HAnim %s (ref %d)\n",anim->Get_Name(),anim->Num_Refs()));
//}
}
}
/***********************************************************************************************
* HAnimManagerClass::Create_Asset_List -- Create a list of the W3D files that are loaded *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/12/2002 GH : Created. *
*=============================================================================================*/
void HAnimManagerClass::Create_Asset_List(DynamicVectorClass<StringClass> & exclusion_list)
{
HAnimManagerIterator it( *this );
for( it.First(); !it.Is_Done(); it.Next() ) {
HAnimClass *anim = it.Get_Current_Anim();
// File that this anim came from should be the name after the '.'
// Anims are named in the format: <skeleton>.<animname>
const char * anim_name = anim->Get_Name();
char * filename = strchr(anim_name,'.');
if (filename != NULL) {
exclusion_list.Add(StringClass(filename+1));
}
}
}
/***********************************************************************************************
* HAnimManagerClass::Add_Anim -- Adds an externally created animation to the manager *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/31/2000 PDS : Created. *
*=============================================================================================*/
bool HAnimManagerClass::Add_Anim(HAnimClass *new_anim)
{
WWASSERT (new_anim != NULL);
// Increment the refcount on the W3DNEW animation and add it to our table.
new_anim->Add_Ref ();
AnimPtrTable->Add( new_anim );
return true;
}
/*
** Missing Anims
**
** The idea here, allow the system to register which anims are determined to be missing
** so that if they are asked for again, we can quickly return NULL, without searching the
** disk again.
*/
void HAnimManagerClass::Register_Missing( const char * name )
{
MissingAnimTable->Add( W3DNEW MissingAnimClass( name ) );
}
bool HAnimManagerClass::Is_Missing( const char * name )
{
return ( MissingAnimTable->Find( name ) != NULL );
}
void HAnimManagerClass::Reset_Missing( void )
{
// Make an iterator, and release all ptrs
HashTableIteratorClass it( *MissingAnimTable );
for( it.First(); !it.Is_Done(); it.Next() ) {
MissingAnimClass *missing = (MissingAnimClass *)it.Get_Current();
delete missing;
}
// Then clear the table
MissingAnimTable->Reset();
}
/*
** Iterator converter from HashableClass to HAnimClass
*/
HAnimClass * HAnimManagerIterator::Get_Current_Anim( void )
{
return (HAnimClass *)Get_Current();
}

View File

@@ -0,0 +1,117 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hanimmgr.h 1 1/22/01 3:36p Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hanimmgr.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HANIMMGR_H
#define HANIMMGR_H
#include "always.h"
#include "hash.h"
#include "wwstring.h"
#include "vector.h"
class HAnimClass;
class ChunkLoadClass;
class W3DExclusionListClass;
/*
** An entry for a table of anims not found, so we can quickly determine their loss
*/
class MissingAnimClass : public HashableClass {
public:
MissingAnimClass( const char * name ) : Name( name ) {}
virtual ~MissingAnimClass( void ) {}
virtual const char * Get_Key( void ) { return Name; }
private:
StringClass Name;
};
/*
HAnimManagerClass
This class is used to keep track of all of the motion data.
*/
class HAnimManagerClass
{
public:
HAnimManagerClass(void);
~HAnimManagerClass(void);
int Load_Anim(ChunkLoadClass & cload);
HAnimClass * Get_Anim(const char * name);
HAnimClass * Peek_Anim(const char * name);
bool Add_Anim(HAnimClass *new_anim);
void Free_All_Anims(void);
void Free_All_Anims_With_Exclusion_List(const W3DExclusionListClass & exclusion_list);
void Create_Asset_List(DynamicVectorClass<StringClass> & exclusion_list);
void Register_Missing( const char * name );
bool Is_Missing( const char * name );
void Reset_Missing( void );
private:
int Load_Compressed_Anim(ChunkLoadClass & cload);
int Load_Raw_Anim(ChunkLoadClass & cload);
int Load_Morph_Anim(ChunkLoadClass & cload);
HashTableClass * AnimPtrTable;
HashTableClass * MissingAnimTable;
friend class HAnimManagerIterator;
};
/*
** An Iterator to get to all loaded HAnims in a HAnimManager
*/
class HAnimManagerIterator : public HashTableIteratorClass {
public:
HAnimManagerIterator( HAnimManagerClass & manager ) : HashTableIteratorClass( *manager.AnimPtrTable ) {}
HAnimClass * Get_Current_Anim( void );
};
#endif

View File

@@ -0,0 +1,710 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hcanim.cpp 3 6/29/01 6:41p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hcanim.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 6/27/01 7:50p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* NodeMotionStruct::NodeMotionStruct -- constructor *
* NodeMotionStruct::~NodeMotionStruct -- destructor *
* HCompressedAnimClass::HCompressedAnimClass -- constructor *
* HCompressedAnimClass::~HCompressedAnimClass -- Destructor *
* HCompressedAnimClass::Free -- De-allocates all memory in use *
* HCompressedAnimClass::Load -- Loads hierarchy animation from a file *
* HCompressedAnimClass::read_channel -- Reads in a single channel of motion *
* HCompressedAnimClass::add_channel -- Adds a motion channel to the animation *
* HCompressedAnimClass::Get_Translation -- returns the translation vector for the given fram*
* HCompressedAnimClass::Get_Orientation -- returns a quaternion for the orientation of the p*
* HCompressedAnimClass::read_bit_channel -- read a bit channel from the file *
* HCompressedAnimClass::add_bit_channel -- install a bit channel into the animation *
* HCompressedAnimClass::Get_Visibility -- return visibility state for given pivot/frame *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hcanim.h"
#include "assetmgr.h"
#include "htree.h"
#include "motchan.h"
#include "chunkio.h"
#include "w3d_file.h"
#include "wwdebug.h"
#include <string.h>
#include <nstrdup.h>
struct NodeCompressedMotionStruct
{
NodeCompressedMotionStruct();
~NodeCompressedMotionStruct();
void SetFlavor(int flavor) {Flavor = flavor;}
int Flavor;
union {
struct {
TimeCodedMotionChannelClass * X;
TimeCodedMotionChannelClass * Y;
TimeCodedMotionChannelClass * Z;
TimeCodedMotionChannelClass * Q;
} tc;
struct {
AdaptiveDeltaMotionChannelClass * X;
AdaptiveDeltaMotionChannelClass * Y;
AdaptiveDeltaMotionChannelClass * Z;
AdaptiveDeltaMotionChannelClass * Q;
} ad;
struct {
void * X;
void * Y;
void * Z;
void * Q;
} vd;
};
TimeCodedBitChannelClass * Vis;
};
/***********************************************************************************************
* NodeCompressedMotionStruct::NodeCompressedMotionStruct -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
NodeCompressedMotionStruct::NodeCompressedMotionStruct() :
Vis(NULL)
{
vd.X = NULL;
vd.Y = NULL;
vd.Z = NULL;
vd.Q = NULL;
}
/***********************************************************************************************
* NodeCompressedMotionStruct::~NodeCompressedMotionStruct -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/23/98 GTH : Created. *
* 02/02/00 JGA : Compressed *
*=============================================================================================*/
NodeCompressedMotionStruct::~NodeCompressedMotionStruct()
{
// Needs to be changed to call the correct destructors
switch (Flavor) {
case ANIM_FLAVOR_TIMECODED:
if (tc.X) delete tc.X;
if (tc.Y) delete tc.Y;
if (tc.Z) delete tc.Z;
if (tc.Q) delete tc.Q;
break;
case ANIM_FLAVOR_ADAPTIVE_DELTA:
if (ad.X) delete ad.X;
if (ad.Y) delete ad.Y;
if (ad.Z) delete ad.Z;
if (ad.Q) delete ad.Q;
break;
default:
WWASSERT(0); // unknown flavor
break;
}
if (Vis) delete Vis;
} // ~NodeCompressedMotionStruct
/***********************************************************************************************
* HCompressedAnimClass::HCompressedAnimClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HCompressedAnimClass::HCompressedAnimClass(void) :
NumFrames(0),
NumNodes(0),
Flavor(0),
FrameRate(0),
NodeMotion(NULL)
{
memset(Name,0,W3D_NAME_LEN);
memset(HierarchyName,0,W3D_NAME_LEN);
}
/***********************************************************************************************
* HCompressedAnimClass::~HCompressedAnimClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HCompressedAnimClass::~HCompressedAnimClass(void)
{
Free();
}
/***********************************************************************************************
* HCompressedAnimClass::Free -- De-allocates all memory in use *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::Free(void)
{
if (NodeMotion != NULL) {
delete[] NodeMotion;
}
}
/***********************************************************************************************
* HCompressedAnimClass::Load -- Loads hierarchy animation from a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
int HCompressedAnimClass::Load_W3D(ChunkLoadClass & cload)
{
int i = 0;
/*
** First make sure we release any memory in use
*/
Free();
/*
** Open the first chunk, it should be the animation header
*/
if (!cload.Open_Chunk()) return LOAD_ERROR;
if (cload.Cur_Chunk_ID() != W3D_CHUNK_COMPRESSED_ANIMATION_HEADER) {
// ERROR: Expected Animation Header!
return LOAD_ERROR;
}
W3dCompressedAnimHeaderStruct aheader;
if (cload.Read(&aheader,sizeof(W3dAnimHeaderStruct)) != sizeof(W3dAnimHeaderStruct)) {
return LOAD_ERROR;
}
cload.Close_Chunk();
strcpy(Name,aheader.HierarchyName);
strcat(Name,".");
strcat(Name,aheader.Name);
// TSS chasing crash bug 05/26/99
WWASSERT(HierarchyName != NULL);
WWASSERT(aheader.HierarchyName != NULL);
WWASSERT(sizeof(HierarchyName) >= W3D_NAME_LEN);
strncpy(HierarchyName,aheader.HierarchyName,W3D_NAME_LEN);
HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName);
if (base_pose == NULL) {
goto Error;
}
NumNodes = base_pose->Num_Pivots();
NumFrames = aheader.NumFrames;
FrameRate = aheader.FrameRate;
Flavor = aheader.Flavor;
// Just for now
WWASSERT((Flavor == ANIM_FLAVOR_TIMECODED)||(Flavor == ANIM_FLAVOR_ADAPTIVE_DELTA));
NodeMotion = W3DNEWARRAY NodeCompressedMotionStruct[ NumNodes ];
if (NodeMotion == NULL) {
goto Error;
}
// Initialize Flavor
for (i=0; i<NumNodes; i++) {
NodeMotion[i].SetFlavor(Flavor);
}
/*
** Now, read in all of the other chunks (motion channels).
*/
TimeCodedMotionChannelClass * tc_chan;
AdaptiveDeltaMotionChannelClass * ad_chan;
TimeCodedBitChannelClass * newbitchan;
while (cload.Open_Chunk()) {
switch (cload.Cur_Chunk_ID()) {
case W3D_CHUNK_COMPRESSED_ANIMATION_CHANNEL:
switch ( Flavor ) {
case ANIM_FLAVOR_TIMECODED:
if (!read_channel(cload,&tc_chan)) {
goto Error;
}
if (tc_chan->Get_Pivot() < NumNodes) {
add_channel(tc_chan);
} else {
// PWG 12-14-98: we have only allocated space for NumNode pivots.
// If we have an index thats equal or higher than NumNode we are
// gonna trash memory. Boy will we trash memory.
// GTH 09-25-2000: print a warning and survive this error
delete tc_chan;
WWDEBUG_SAY(("ERROR! animation %s indexes a bone not present in the model. Please re-export!\r\n",Name));
}
break;
case ANIM_FLAVOR_ADAPTIVE_DELTA:
if (!read_channel(cload,&ad_chan)) {
goto Error;
}
if (ad_chan->Get_Pivot() < NumNodes) {
add_channel(ad_chan);
} else {
// PWG 12-14-98: we have only allocated space for NumNode pivots.
// If we have an index thats equal or higher than NumNode we are
// gonna trash memory. Boy will we trash memory.
// GTH 09-25-2000: print a warning and survive this error
delete ad_chan;
WWDEBUG_SAY(("ERROR! animation %s indexes a bone not present in the model. Please re-export!\r\n",Name));
}
break;
}
break;
case W3D_CHUNK_COMPRESSED_BIT_CHANNEL:
if (!read_bit_channel(cload,&newbitchan)) {
goto Error;
}
if (newbitchan->Get_Pivot() < NumNodes) {
add_bit_channel(newbitchan);
} else {
// PWG 12-14-98: we have only allocated space for NumNode pivots.
// If we have an index thats equal or higher than NumNode we are
// gonna trash memory. Boy will we trash memory.
// GTH 09-25-2000: print a warning and survive this error
delete newbitchan;
WWDEBUG_SAY(("ERROR! animation %s indexes a bone not present in the model. Please re-export!\r\n",Name));
}
break;
default:
break;
}
cload.Close_Chunk();
}
return OK;
Error:
Free();
return LOAD_ERROR;
} // Load_W3D
/***********************************************************************************************
* HCompressedAnimClass::read_channel -- Reads in a single channel of motion *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
bool HCompressedAnimClass::read_channel(ChunkLoadClass & cload,TimeCodedMotionChannelClass * * newchan)
{
*newchan = W3DNEW TimeCodedMotionChannelClass;
bool result = (*newchan)->Load_W3D(cload);
return result;
} // read_channel
bool HCompressedAnimClass::read_channel(ChunkLoadClass & cload,AdaptiveDeltaMotionChannelClass * * newchan)
{
*newchan = W3DNEW AdaptiveDeltaMotionChannelClass;
bool result = (*newchan)->Load_W3D(cload);
return result;
} // read_channel
/***********************************************************************************************
* HCompressedAnimClass::add_channel -- Adds a motion channel to the animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::add_channel(TimeCodedMotionChannelClass * newchan)
{
int idx = newchan->Get_Pivot();
switch (newchan->Get_Type())
{
case ANIM_CHANNEL_X:
NodeMotion[idx].tc.X = newchan;
break;
case ANIM_CHANNEL_Y:
NodeMotion[idx].tc.Y = newchan;
break;
case ANIM_CHANNEL_Z:
NodeMotion[idx].tc.Z = newchan;
break;
case ANIM_CHANNEL_Q:
NodeMotion[idx].tc.Q = newchan;
break;
}
} // add_channel
void HCompressedAnimClass::add_channel(AdaptiveDeltaMotionChannelClass * newchan)
{
int idx = newchan->Get_Pivot();
switch (newchan->Get_Type())
{
case ANIM_CHANNEL_X:
NodeMotion[idx].ad.X = newchan;
break;
case ANIM_CHANNEL_Y:
NodeMotion[idx].ad.Y = newchan;
break;
case ANIM_CHANNEL_Z:
NodeMotion[idx].ad.Z = newchan;
break;
case ANIM_CHANNEL_Q:
NodeMotion[idx].ad.Q = newchan;
break;
}
} // add_channel
/***********************************************************************************************
* HCompressedAnimClass::read_bit_channel -- read a bit channel from the file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/19/98 GTH : Created. *
*=============================================================================================*/
bool HCompressedAnimClass::read_bit_channel(ChunkLoadClass & cload,TimeCodedBitChannelClass * * newchan)
{
*newchan = W3DNEW TimeCodedBitChannelClass;
bool result = (*newchan)->Load_W3D(cload);
return result;
} // read_bit_channel
/***********************************************************************************************
* HCompressedAnimClass::add_bit_channel -- install a bit channel into the animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/19/98 GTH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::add_bit_channel(TimeCodedBitChannelClass * newchan)
{
int idx = newchan->Get_Pivot();
switch (newchan->Get_Type())
{
case BIT_CHANNEL_VIS:
NodeMotion[idx].Vis = newchan;
break;
}
}
/***********************************************************************************************
* HCompressedAnimClass::Get_Translation -- returns the translation vector for the given frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::Get_Translation( Vector3& trans, int pividx, float frame ) const
{
struct NodeCompressedMotionStruct * motion = &NodeMotion[pividx];
trans=Vector3(0,0,0);
switch(Flavor) {
case ANIM_FLAVOR_TIMECODED:
if (motion->tc.X) motion->tc.X->Get_Vector(frame, &(trans[0]));
if (motion->tc.Y) motion->tc.Y->Get_Vector(frame, &(trans[1]));
if (motion->tc.Z) motion->tc.Z->Get_Vector(frame, &(trans[2]));
break;
case ANIM_FLAVOR_ADAPTIVE_DELTA:
if (motion->ad.X) motion->ad.X->Get_Vector(frame, &(trans[0]));
if (motion->ad.Y) motion->ad.Y->Get_Vector(frame, &(trans[1]));
if (motion->ad.Z) motion->ad.Z->Get_Vector(frame, &(trans[2]));
break;
default:
WWASSERT(0); // unknown flavor
break;
}
}
/***********************************************************************************************
* HCompressedAnimClass::Get_Orientation -- returns a quaternion for the orientation of the *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::Get_Orientation(Quaternion& q, int pividx,float frame) const
{
switch(Flavor) {
case ANIM_FLAVOR_TIMECODED:
if (NodeMotion[pividx].tc.Q) q = NodeMotion[pividx].tc.Q->Get_QuatVector(frame);
else q.Make_Identity();
break;
case ANIM_FLAVOR_ADAPTIVE_DELTA:
if (NodeMotion[pividx].ad.Q) q = NodeMotion[pividx].ad.Q->Get_QuatVector(frame);
else q.Make_Identity();
break;
default:
WWASSERT(0); // unknown flavor
break;
}
} // Get_Orientation
/***********************************************************************************************
* HCompressedAnimClass::Get_Transform -- returns the transform matrix for the given frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HCompressedAnimClass::Get_Transform( Matrix3D& mtx, int pividx, float frame ) const
{
struct NodeCompressedMotionStruct * motion = &NodeMotion[pividx];
switch(Flavor) {
case ANIM_FLAVOR_TIMECODED:
if (NodeMotion[pividx].tc.Q) {
Quaternion q;
q = NodeMotion[pividx].tc.Q->Get_QuatVector(frame);
::Build_Matrix3D(q,mtx);
}
else mtx.Make_Identity();
if (motion->tc.X) motion->tc.X->Get_Vector(frame, &(mtx[0][3]));
if (motion->tc.Y) motion->tc.Y->Get_Vector(frame, &(mtx[1][3]));
if (motion->tc.Z) motion->tc.Z->Get_Vector(frame, &(mtx[2][3]));
break;
case ANIM_FLAVOR_ADAPTIVE_DELTA:
if (NodeMotion[pividx].ad.Q) {
Quaternion q;
q = NodeMotion[pividx].ad.Q->Get_QuatVector(frame);
::Build_Matrix3D(q,mtx);
}
else mtx.Make_Identity();
if (motion->ad.X) motion->ad.X->Get_Vector(frame, &(mtx[0][3]));
if (motion->ad.Y) motion->ad.Y->Get_Vector(frame, &(mtx[1][3]));
if (motion->ad.Z) motion->ad.Z->Get_Vector(frame, &(mtx[2][3]));
break;
default:
WWASSERT(0); // unknown flavor
break;
}
}
/***********************************************************************************************
* HCompressedAnimClass::Get_Visibility -- return visibility state for given pivot/frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/19/98 GTH : Created. *
*=============================================================================================*/
bool HCompressedAnimClass::Get_Visibility(int pividx,float frame)
{
if (NodeMotion[pividx].Vis != NULL) {
return (NodeMotion[pividx].Vis->Get_Bit((int)frame) == 1);
}
// default to always visible...
return 1;
}
/***********************************************************************************************
* HAnimClass::Is_Node_Motion_Present -- return true if there is motion defined for this frame *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/23/99 EHC : Created. *
*=============================================================================================*/
bool HCompressedAnimClass::Is_Node_Motion_Present(int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
if (NodeMotion[pividx].vd.X != NULL) return true;
if (NodeMotion[pividx].vd.Y != NULL) return true;
if (NodeMotion[pividx].vd.Z != NULL) return true;
if (NodeMotion[pividx].vd.Q != NULL) return true;
if (NodeMotion[pividx].Vis != NULL) return true;
return false;
}
bool HCompressedAnimClass::Has_X_Translation (int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
return NodeMotion[pividx].vd.X != NULL;
}
bool HCompressedAnimClass::Has_Y_Translation (int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
return NodeMotion[pividx].vd.Y != NULL;
}
bool HCompressedAnimClass::Has_Z_Translation (int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
return NodeMotion[pividx].vd.Z != NULL;
}
bool HCompressedAnimClass::Has_Rotation (int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
return NodeMotion[pividx].vd.Q != NULL;
}
bool HCompressedAnimClass::Has_Visibility (int pividx)
{
WWASSERT((pividx >= 0) && (pividx < NumNodes));
return NodeMotion[pividx].Vis != NULL;
}
// eof - hcanim.cpp

View File

@@ -0,0 +1,139 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/hcanim.h 2 6/29/01 6:41p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D Library *
* *
* $Archive:: /Commando/Code/ww3d2/hcanim.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 6/27/01 7:35p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HCANIM_H
#define HCANIM_H
#include "always.h"
#include "quat.h"
#include "refcount.h"
#include "w3d_file.h"
#include "slist.h"
#include "vector.h"
#include "hanim.h"
struct NodeCompressedMotionStruct;
class TimeCodedMotionChannelClass;
class TimeCodedBitChannelClass;
class AdaptiveDeltaMotionChannelClass;
class HTreeClass;
class ChunkLoadClass;
class ChunkSaveClass;
/**********************************************************************************
Hierarchy Compressed Animation Class
Stores motion data to be applied to a HierarchyTree. Each frame
of the motion contains deltas from the HierarchyTree's base position
to the desired position.
**********************************************************************************/
class HCompressedAnimClass : public HAnimClass
{
public:
enum
{
OK,
LOAD_ERROR
};
HCompressedAnimClass(void);
~HCompressedAnimClass(void);
int Load_W3D(ChunkLoadClass & cload);
const char * Get_Name(void) const { return Name; }
const char * Get_HName(void) const { return HierarchyName; }
int Get_Num_Frames(void) { return NumFrames; }
float Get_Frame_Rate() { return FrameRate; }
float Get_Total_Time() { return (float)NumFrames / FrameRate; }
int Get_Flavor() { return Flavor; }
// Vector3 Get_Translation(int pividx,float frame);
// Quaternion Get_Orientation(int pividx,float frame);
void Get_Translation(Vector3& translation, int pividx,float frame) const;
void Get_Orientation(Quaternion& orientation, int pividx,float frame) const;
void Get_Transform(Matrix3D& transform, int pividx,float frame) const;
bool Get_Visibility(int pividx,float frame);
bool Is_Node_Motion_Present(int pividx);
int Get_Num_Pivots(void) const { return NumNodes; }
// Methods that test the presence of a certain motion channel.
bool Has_X_Translation (int pividx);
bool Has_Y_Translation (int pividx);
bool Has_Z_Translation (int pividx);
bool Has_Rotation (int pividx);
bool Has_Visibility (int pividx);
private:
char Name[2*W3D_NAME_LEN];
char HierarchyName[W3D_NAME_LEN];
int NumFrames;
int NumNodes;
int Flavor;
float FrameRate;
NodeCompressedMotionStruct * NodeMotion;
void Free(void);
bool read_channel(ChunkLoadClass & cload,TimeCodedMotionChannelClass * * newchan);
bool read_channel(ChunkLoadClass & cload,AdaptiveDeltaMotionChannelClass * * newchan);
void add_channel(TimeCodedMotionChannelClass * newchan);
void add_channel(AdaptiveDeltaMotionChannelClass * newchan);
bool read_bit_channel(ChunkLoadClass & cload,TimeCodedBitChannelClass * * newchan);
void add_bit_channel(TimeCodedBitChannelClass * newchan);
};
#endif // hcanim.h

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/hlod.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/25/01 1:37p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HLOD_H
#define HLOD_H
#ifndef ANIMOBJ_H
#include "animobj.h"
#endif
#ifndef VECTOR_H
#include "vector.h"
#endif
#ifndef SNAPPTS_H
#include "snappts.h"
#endif
#ifndef PROTO_H
#include "proto.h"
#endif
#ifndef W3DERR_H
#include "w3derr.h"
#endif
#ifndef __PROXY_H
#include "proxy.h"
#endif
class DistLODClass;
class HModelClass;
class HLodDefClass;
class HModelDefClass;
class ProxyArrayClass;
/*
HLodClass
This is an hierarchical, animatable level-of-detail model.
*/
class HLodClass : public W3DMPO, public Animatable3DObjClass
{
W3DMPO_GLUE(HLodClass)
public:
HLodClass(const HLodClass & src);
HLodClass(const char * name,RenderObjClass ** lods,int count);
HLodClass(const HLodDefClass & def);
HLodClass(const HModelDefClass & def);
HLodClass & operator = (const HLodClass &);
virtual ~HLodClass(void);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Cloning and Identification
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_HLOD; }
virtual int Get_Num_Polys(void) const;
/////////////////////////////////////////////////////////////////////////////
// HLod Interface - Editing and information
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Max_Screen_Size(int lod_index, float size);
virtual float Get_Max_Screen_Size(int lod_index) const;
virtual int Get_Lod_Count(void) const;
virtual int Get_Lod_Model_Count (int lod_index) const;
virtual RenderObjClass * Peek_Lod_Model (int lod_index, int model_index) const;
virtual RenderObjClass * Get_Lod_Model (int lod_index, int model_index) const;
virtual int Get_Lod_Model_Bone (int lod_index, int model_index) const;
virtual int Get_Additional_Model_Count(void) const;
virtual RenderObjClass * Peek_Additional_Model (int model_index) const;
virtual RenderObjClass * Get_Additional_Model (int model_index) const;
virtual int Get_Additional_Model_Bone (int model_index) const;
virtual void Add_Lod_Model(int lod, RenderObjClass * robj, int boneindex);
virtual bool Is_NULL_Lod_Included (void) const;
virtual void Include_NULL_Lod (bool include = true);
/////////////////////////////////////////////////////////////////////////////
// Proxy interface
/////////////////////////////////////////////////////////////////////////////
virtual int Get_Proxy_Count (void) const;
virtual bool Get_Proxy (int index, ProxyClass &proxy) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual void Notify_Added(SceneClass * scene);
virtual void Notify_Removed(SceneClass * scene);
virtual int Get_Num_Sub_Objects(void) const;
virtual RenderObjClass * Get_Sub_Object(int index) const;
virtual int Add_Sub_Object(RenderObjClass * subobj);
virtual int Remove_Sub_Object(RenderObjClass * robj);
virtual int Get_Num_Sub_Objects_On_Bone(int boneindex) const;
virtual RenderObjClass * Get_Sub_Object_On_Bone(int index,int boneindex) const;
virtual int Get_Sub_Object_Bone_Index(RenderObjClass * subobj) const;
virtual int Get_Sub_Object_Bone_Index(int LodIndex, int ModelIndex) const;
virtual int Add_Sub_Object_To_Bone(RenderObjClass * subobj,int bone_index);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Hierarchical Animation
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Animation(void);
virtual void Set_Animation( HAnimClass * motion,
float frame, int anim_mode = ANIM_MODE_MANUAL);
virtual void Set_Animation( HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage);
virtual void Set_Animation( HAnimComboClass * anim_combo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Collision Detection, Ray Tracing
/////////////////////////////////////////////////////////////////////////////
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Predictive LOD
/////////////////////////////////////////////////////////////////////////////
virtual void Prepare_LOD(CameraClass &camera);
virtual void Recalculate_Static_LOD_Factors(void);
virtual void Increment_LOD(void);
virtual void Decrement_LOD(void);
virtual float Get_Cost(void) const;
virtual float Get_Value(void) const;
virtual float Get_Post_Increment_Value(void) const;
virtual void Set_LOD_Level(int lod);
virtual int Get_LOD_Level(void) const;
virtual int Get_LOD_Count(void) const;
virtual void Set_LOD_Bias(float bias);
virtual int Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const;
virtual RenderObjClass * Get_Current_LOD(void);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Bounding Volumes
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual const SphereClass & Get_Bounding_Sphere(void) const;
virtual const AABoxClass & Get_Bounding_Box(void) const;
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Decals
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void Create_Decal(DecalGeneratorClass * generator);
virtual void Delete_Decal(uint32 decal_id);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Attributes, Options, Properties, etc
/////////////////////////////////////////////////////////////////////////////
// virtual void Set_Texture_Reduction_Factor(float trf);
virtual void Scale(float scale);
virtual void Scale(float scalex, float scaley, float scalez) { }
virtual int Get_Num_Snap_Points(void);
virtual void Get_Snap_Point(int index,Vector3 * set);
virtual void Set_Hidden(int onoff);
// (gth) TESTING DYNAMICALLY SWAPPING SKELETONS!
virtual void Set_HTree(HTreeClass * htree);
protected:
HLodClass(void);
void Free(void);
virtual void Update_Sub_Object_Transforms(void);
virtual void Update_Obj_Space_Bounding_Volumes(void);
protected:
class ModelNodeClass
{
public:
RenderObjClass * Model;
int BoneIndex;
bool operator == (const ModelNodeClass & that) { return (Model == that.Model) && (BoneIndex == that.BoneIndex); }
bool operator != (const ModelNodeClass & that) { return !operator == (that); }
};
class ModelArrayClass : public DynamicVectorClass<ModelNodeClass>
{
public:
ModelArrayClass(void) : MaxScreenSize(NO_MAX_SCREEN_SIZE), NonPixelCost(0.0f),
PixelCostPerArea(0.0f), BenefitFactor(0.0f) {}
float MaxScreenSize; // Maximum screen size for this LOD
float NonPixelCost; // Cost heuristics of LODS (w/o per-pixel cost)
float PixelCostPerArea; // PixelCostPerArea * area(normalized) + NonPixelCost = total Cost
float BenefitFactor; // BenefitFactor * area(normalized) = Benefit
};
// Lod Render Objects, basically one of the LOD Models will be rendered. Typically
// each model in an HLodModel will be a mesh or a "simple" HLod (one with a single LOD)
int LodCount;
int CurLod;
ModelArrayClass * Lod;
//
// An animating heirarchy can use a hidden CLASSID_OBBOX mesh to represent its bounding
// box as it animates. This is the sub object index of that mesh (if it exists).
//
int BoundingBoxIndex;
float * Cost; // Cost array (recalculated every frame)
float * Value; // Value array (recalculated every frame)
// Additional Models, these models have been linked to one of the bones in this
// model. They are all always rendered. They can be HLODs themselves in order
// to implement switching on sub models.
// NOTE: This uses ModelArrayClass for convenience, but MaxScreenSize,
// NonPixelCost, PixelCostPerArea, BenefitFactor are not used here.
ModelArrayClass AdditionalModels;
// possible array of snap points.
SnapPointsClass * SnapPoints;
// possible array of proxy objects (names and bone indexes for application defined usage)
ProxyArrayClass * ProxyArray;
// Current LOD Bias (affects recalculation of the Value array)
float LODBias;
};
/*
** Loaders for HLodClass
*/
class HLodLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_HLOD; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
/**
** HLodDefClass
** This description object is generated when reading a W3D_CHUNK_HLOD. It
** directly describes the contents of an HLod model.
*/
class HLodDefClass : public W3DMPO
{
W3DMPO_GLUE(HLodDefClass)
public:
HLodDefClass(void);
HLodDefClass(HLodClass &src_lod);
~HLodDefClass(void);
WW3DErrorType Load_W3D(ChunkLoadClass & cload);
WW3DErrorType Save(ChunkSaveClass & csave);
const char * Get_Name(void) const { return Name; }
void Initialize(HLodClass &src_lod);
protected:
/*
** Serializtion methods
*/
WW3DErrorType Save_Header (ChunkSaveClass &csave);
WW3DErrorType Save_Lod_Array (ChunkSaveClass &csave);
WW3DErrorType Save_Aggregate_Array(ChunkSaveClass & csave);
private:
/*
** SubObjectArrayClass
** Describes a level-of-detail in an HLod object. Note that this is
** a render object which will be exploded when the HLod is constructed (its
** sub-objects, if any, will be placed into the HLod).
*/
class SubObjectArrayClass
{
public:
SubObjectArrayClass(void);
~SubObjectArrayClass(void);
void Reset(void);
void operator = (const SubObjectArrayClass & that);
bool Load_W3D(ChunkLoadClass & cload);
bool Save_W3D(ChunkSaveClass & csave);
float MaxScreenSize;
int ModelCount;
char ** ModelName; // array of model names
int * BoneIndex; // array of bone indices
};
char * Name;
char * HierarchyTreeName;
int LodCount;
SubObjectArrayClass * Lod;
SubObjectArrayClass Aggregates;
ProxyArrayClass * ProxyArray;
void Free(void);
bool read_header(ChunkLoadClass & cload);
bool read_proxy_array(ChunkLoadClass & cload);
friend class HLodClass;
};
/*
** Prototype for HLod objects
*/
class HLodPrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(HLodPrototypeClass)
public:
HLodPrototypeClass( HLodDefClass *def ) { Definition = def; }
virtual const char * Get_Name(void) const { return Definition->Get_Name(); }
virtual int Get_Class_ID(void) const { return RenderObjClass::CLASSID_HLOD; }
virtual RenderObjClass * Create(void);
virtual void DeleteSelf() { delete this; }
HLodDefClass * Get_Definition(void) const { return Definition; }
protected:
virtual ~HLodPrototypeClass(void) { delete Definition; }
private:
HLodDefClass * Definition;
};
/*
** Instance of the loaders which the asset manager install
*/
extern HLodLoaderClass _HLodLoader;
#endif

View File

@@ -0,0 +1,112 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/hmdldef.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HMDLDEF_H
#define HMDLDEF_H
#include "always.h"
#include "w3d_file.h"
class FileClass;
class ChunkLoadClass;
class ChunkSaveClass;
class SnapPointsClass;
/*
** Each render object to be attached into the hierarchy model
** will be defined by an instance of the following structure.
** The HmdlNodeDefStruct associates a named render object with
** an indexed pivot/bone in the hierarchy tree.
*/
struct HmdlNodeDefStruct
{
char RenderObjName[2*W3D_NAME_LEN];
int PivotID;
};
/*
** HModelDefClass
**
** "Hierarchy Model Definition Class"
** This class serves as a blueprint for creating HierarchyModelClasses.
** The asset manager stores these objects internally and uses them to
** create instances of HierarchyModels for the user.
*/
class HModelDefClass
{
public:
enum {
OK,
LOAD_ERROR
};
HModelDefClass(void);
~HModelDefClass(void);
int Load_W3D(ChunkLoadClass & cload);
const char * Get_Name(void) const { return Name; }
private:
char Name[2*W3D_NAME_LEN]; // <basepose>.<modelname>
char ModelName[W3D_NAME_LEN]; // name of the model
char BasePoseName[W3D_NAME_LEN]; // name of the base pose (hierarchy tree)
int SubObjectCount;
HmdlNodeDefStruct * SubObjects;
SnapPointsClass * SnapPoints;
void Free(void);
bool read_header(ChunkLoadClass & cload);
bool read_connection(ChunkLoadClass & cload,HmdlNodeDefStruct * con,bool pre30);
bool read_mesh_connection(ChunkLoadClass & cload,int idx,bool pre30);
bool read_collision_connection(ChunkLoadClass & cload,int idx,bool pre30);
bool read_skin_connection(ChunkLoadClass & cload,int idx,bool pre30);
// HModelClass requires intimate knowlege of us
friend class HModelClass;
friend class HLodClass;
};
#endif

View File

@@ -0,0 +1,257 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/hmdldef.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* HModelDefClass::HModelDefClass -- Constructor *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hmdldef.h"
#include <assert.h>
#include <string.h>
#include "w3d_file.h"
#include "chunkio.h"
#include "snappts.h"
/***********************************************************************************************
* HModelDefClass::HModelDefClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/15/97 GTH : Created. *
*=============================================================================================*/
HModelDefClass::HModelDefClass(void) :
SubObjectCount(0),
SubObjects(NULL),
SnapPoints(NULL)
{
}
/***********************************************************************************************
* HModelDefClass::~HModelDefClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
HModelDefClass::~HModelDefClass(void)
{
Free();
}
/***********************************************************************************************
* HModelDefClass::Free -- de-allocate all memory in use *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void HModelDefClass::Free(void)
{
if (SubObjects != NULL) {
delete[] SubObjects;
SubObjects = NULL;
}
SubObjectCount = 0;
if (SnapPoints != NULL) {
SnapPoints->Release_Ref();
SnapPoints = NULL;
}
}
/***********************************************************************************************
* HModelDefClass::Load -- load a set of mesh connections from a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
int HModelDefClass::Load_W3D(ChunkLoadClass & cload)
{
bool pre30 = false;
int subobjcounter = 0;
Free();
/*
** Read the first chunk, it should be the header
*/
if (!cload.Open_Chunk()) {
return false;
}
if (cload.Cur_Chunk_ID() != W3D_CHUNK_HMODEL_HEADER) {
goto Error;
}
/*
** read in the header
*/
W3dHModelHeaderStruct header;
if (cload.Read(&header,sizeof(W3dHModelHeaderStruct)) != sizeof(W3dHModelHeaderStruct)) {
goto Error;
}
cload.Close_Chunk();
/*
** process the header info
*/
strncpy(ModelName,header.Name,W3D_NAME_LEN);
ModelName[W3D_NAME_LEN - 1] = 0;
strncpy(BasePoseName,header.HierarchyName,W3D_NAME_LEN);
BasePoseName[W3D_NAME_LEN-1] = 0;
strcpy(Name,ModelName);
/*
** Just allocate a node for the number of sub objects we're expecting
*/
SubObjectCount = header.NumConnections;
SubObjects = W3DNEWARRAY HmdlNodeDefStruct[SubObjectCount];
if (SubObjects == NULL) {
goto Error;
}
/*
** If this is pre-3.0 set a flag so that each render object's
** bone id will be incremented by one to account for the new
** root node added with version3.0 of the file format. Basically,
** I'm making all of the code assume that node 0 is the root and
** therefore, pre-3.0 files have to have it added and all of
** the indices adjusted
*/
if (header.Version < W3D_MAKE_VERSION(3,0)) {
pre30 = true;
}
/*
** Process the rest of the chunks
*/
subobjcounter = 0;
while (cload.Open_Chunk()) {
switch (cload.Cur_Chunk_ID()) {
case W3D_CHUNK_NODE:
case W3D_CHUNK_COLLISION_NODE:
case W3D_CHUNK_SKIN_NODE:
if (!read_connection(cload,&(SubObjects[subobjcounter]),pre30)) {
goto Error;
}
subobjcounter++;
break;
case W3D_CHUNK_POINTS:
SnapPoints = W3DNEW SnapPointsClass;
SnapPoints->Load_W3D(cload);
break;
default:
break;
}
cload.Close_Chunk();
}
return OK;
Error:
return LOAD_ERROR;
}
/***********************************************************************************************
* HModelDefClass::read_connection -- read a single connection from the file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
* 10/22/97 GH : Check for mesh connections with PivotID=-1 *
*=============================================================================================*/
bool HModelDefClass::read_connection(ChunkLoadClass & cload,HmdlNodeDefStruct * node,bool pre30)
{
W3dHModelNodeStruct con;
if (cload.Read(&con,sizeof(W3dHModelNodeStruct)) != sizeof(W3dHModelNodeStruct)) {
return false;
}
strcpy(node->RenderObjName,ModelName);
strcat(node->RenderObjName,".");
strcat(node->RenderObjName,con.RenderObjName);
if (pre30) {
if (con.PivotIdx == 65535) {
node->PivotID = 0;
} else {
node->PivotID = con.PivotIdx + 1;
}
} else {
assert(con.PivotIdx != 65535);
node->PivotID = con.PivotIdx;
}
return true;
}

View File

@@ -0,0 +1,742 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/hmorphanim.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 1/16/02 6:39p $*
* *
* $Revision:: 7 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hmorphanim.h"
#include "w3d_file.h"
#include "chunkio.h"
#include "assetmgr.h"
#include "htree.h"
#include "wwstring.h"
#include "textfile.h"
#include "simplevec.h"
TimeCodedMorphKeysClass::TimeCodedMorphKeysClass(void)
: CachedIdx (0)
{
}
TimeCodedMorphKeysClass::~TimeCodedMorphKeysClass(void)
{
Free();
}
void TimeCodedMorphKeysClass::Free(void)
{
Keys.Delete_All ();
CachedIdx = 0;
}
bool TimeCodedMorphKeysClass::Load_W3D(ChunkLoadClass & cload)
{
Free();
uint32 key_count = cload.Cur_Chunk_Length() / sizeof(W3dMorphAnimKeyStruct);
W3dMorphAnimKeyStruct w3dkey;
for (uint32 i=0; i<key_count; i++) {
cload.Read(&w3dkey,sizeof(w3dkey));
Keys.Add (MorphKeyStruct (w3dkey.MorphFrame, w3dkey.PoseFrame));
}
CachedIdx = 0;
return true;
}
bool TimeCodedMorphKeysClass::Save_W3D(ChunkSaveClass & csave)
{
W3dMorphAnimKeyStruct w3dkey;
for (int i=0; i<Keys.Count (); i++) {
w3dkey.MorphFrame = Keys[i].MorphFrame;
w3dkey.PoseFrame = Keys[i].PoseFrame;
csave.Write(&w3dkey,sizeof(w3dkey));
}
return true;
}
void TimeCodedMorphKeysClass::Add_Key (uint32 morph_frame, uint32 pose_frame)
{
Keys.Add (MorphKeyStruct (morph_frame, pose_frame));
return ;
}
void TimeCodedMorphKeysClass::Get_Morph_Info(float morph_frame,int * pose_frame0,int * pose_frame1,float * fraction)
{
if (morph_frame < 0.0f) {
*pose_frame0 = *pose_frame1 = Keys[0].PoseFrame;
*fraction = 0.0f;
return;
}
if (morph_frame >= Keys[Keys.Count ()-1].MorphFrame) {
*pose_frame0 = *pose_frame1 = Keys[Keys.Count ()-1].PoseFrame;
*fraction = 0.0f;
return;
}
int key_index = get_index(morph_frame);
*pose_frame0 = Keys[key_index].PoseFrame;
*pose_frame1 = Keys[key_index+1].PoseFrame;
*fraction = (morph_frame - Keys[key_index].MorphFrame) / (Keys[key_index+1].MorphFrame - Keys[key_index].MorphFrame);
}
uint32 TimeCodedMorphKeysClass::get_index(float frame)
{
assert(CachedIdx <= (uint32)Keys.Count ()-1);
float cached_frame = Keys[CachedIdx].MorphFrame;
// check if the requested time is in the cached interval or the following one
if (frame >= cached_frame) {
// special case for end packets
if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
// check if the requested time is still in the cached interval
if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
// do one time look-ahead before reverting to a search
CachedIdx++;
// again, special case the end interval
if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
// check if requested time is in this interval
if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
}
// nope, fall back to a binary search for the requested interval
CachedIdx = binary_search_index(frame);
return(CachedIdx);
}
uint32 TimeCodedMorphKeysClass::binary_search_index(float req_frame)
{
// special case first and last packet
if (req_frame < Keys[0].MorphFrame) return 0;
if (req_frame >= Keys[Keys.Count ()-1].MorphFrame) return Keys.Count ()-1;
int leftIdx = 0;
int rightIdx = Keys.Count ();
int idx,dx;
// binary search for the desired interval
for (;;) {
// if we've zeroed in on the interval, return the left index
dx = rightIdx - leftIdx;
if (dx == 1) {
WWASSERT(req_frame >= Keys[leftIdx].MorphFrame);
WWASSERT(req_frame < Keys[rightIdx].MorphFrame);
return leftIdx;
}
// otherwise, split our interval in half and descend into one of them
dx>>=1;
idx = leftIdx + dx;
if (req_frame < Keys[idx].MorphFrame) {
rightIdx = idx;
} else {
leftIdx = idx;
}
}
assert(0);
return(0);
}
/*********************************************************************************************
**
** HMorphAnimClass Implementation
**
*********************************************************************************************/
HMorphAnimClass::HMorphAnimClass(void) :
FrameCount(0),
FrameRate(0.0f),
ChannelCount(0),
NumNodes(0),
PoseData(NULL),
MorphKeyData(NULL),
PivotChannel(NULL)
{
memset(Name,0,sizeof(Name));
memset(AnimName,0,sizeof(AnimName));
memset(HierarchyName,0,sizeof(HierarchyName));
}
HMorphAnimClass::~HMorphAnimClass(void)
{
Free();
}
void HMorphAnimClass::Free(void)
{
if (PoseData != NULL) {
for (int i=0; i<ChannelCount; i++) {
REF_PTR_RELEASE(PoseData[i]);
}
delete[] PoseData;
PoseData = NULL;
}
if (MorphKeyData != NULL) {
delete[] MorphKeyData;
MorphKeyData = NULL;
}
if (PivotChannel != NULL) {
delete[] PivotChannel;
PivotChannel = NULL;
}
}
static int Build_List_From_String
(
const char * buffer,
const char * delimiter,
StringClass ** string_list
)
{
int count = 0;
WWASSERT (buffer != NULL);
WWASSERT (delimiter != NULL);
WWASSERT (string_list != NULL);
if ((buffer != NULL) &&
(delimiter != NULL) &&
(string_list != NULL))
{
int delim_len = ::strlen (delimiter);
//
// Determine how many entries there will be in the list
//
for (const char *entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
// Increment the count of entries
count ++;
}
if (count > 0) {
//
// Allocate enough StringClass objects to hold all the strings in the list
//
(*string_list) = W3DNEWARRAY StringClass[count];
//
// Parse the string and pull out its entries.
//
count = 0;
for (entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
//
// Copy this entry into its own string
//
StringClass entry_string = entry;
char *delim_start = ::strstr (entry_string, delimiter);
if (delim_start != NULL) {
delim_start[0] = 0;
}
//
// Add this entry to our list
//
if ((entry_string.Get_Length () > 0) || (count == 0)) {
(*string_list)[count++] = entry_string;
}
}
} else if (delim_len > 0) {
count = 1;
(*string_list) = W3DNEWARRAY StringClass[count];
(*string_list)[0] = buffer;
}
}
//
// Return the number of entries in our list
//
return count;
}
bool Is_Number (const char *str)
{
bool retval = true;
while (retval && str[0] != NULL){
retval = ((str[0] >= '0' && str[0] <= '9') || str[0] == '-' || str[0] == '.');
str ++;
}
return retval;
}
bool HMorphAnimClass::Import(const char *hierarchy_name, TextFileClass &text_desc)
{
bool retval = false;
Free ();
FrameCount = 0;
FrameRate = 30.0F;
//
// Copy the hierarchy name into a class variable
//
::strncpy (HierarchyName, hierarchy_name, W3D_NAME_LEN);
HierarchyName[W3D_NAME_LEN - 1] = 0;
//
// Attempt to load the new base pose
//
HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName);
WWASSERT (base_pose != NULL);
NumNodes = base_pose->Num_Pivots();
//
// Read the header from the file
//
StringClass header;
bool success = text_desc.Read_Line (header);
if (success) {
//
// Get the list of comma-delimited strings from the header
//
StringClass *column_list = NULL;
int column_count = Build_List_From_String (header, ",", &column_list);
//
// The first column header should be 'Frame#', all other headers
// should be channel animation names.
//
ChannelCount = column_count - 1;
WWASSERT (ChannelCount > 0);
if (ChannelCount > 0) {
//
// Allocate and initialize each animation channel
//
PoseData = W3DNEWARRAY HAnimClass *[ChannelCount];
MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
for (int index = 0; index < ChannelCount; index ++) {
StringClass channel_anim_name;
channel_anim_name.Format ("%s.%s", HierarchyName, (const char *)column_list[index + 1]);
PoseData[index] = WW3DAssetManager::Get_Instance()->Get_HAnim (channel_anim_name);
}
//
// Now read the animation data for each frame
//
StringClass frame_desc;
while (text_desc.Read_Line (frame_desc)) {
//
// Get the frame descriptions from this line
//
StringClass *channel_list = NULL;
int list_count = Build_List_From_String (frame_desc, ",", &channel_list);
WWASSERT (list_count > 0);
if (list_count > 0) {
//
// The first column contains the morph frame number
//
int frame = ::atoi (channel_list[0]);
//
// Now read the animation frame number for each channel
//
for (int index = 1; index < list_count; index ++) {
StringClass &channel_frame = channel_list[index];
//
// If this channel contains a valid number, then record
// its animation frame
//
if (::Is_Number (channel_frame)) {
MorphKeyData[index - 1].Add_Key (frame, ::atoi (channel_frame));
}
}
FrameCount = frame + 1;
}
//
// Cleanup
//
if (channel_list != NULL) {
delete [] channel_list;
channel_list = NULL;
}
}
//
// Allocate the pivot channel list
//
PivotChannel = W3DNEWARRAY uint32[NumNodes];
Resolve_Pivot_Channels ();
}
//
// Cleanup
//
if (column_list != NULL) {
delete [] column_list;
column_list = NULL;
}
}
return retval;
}
void HMorphAnimClass::Resolve_Pivot_Channels(void)
{
WWASSERT (PivotChannel != NULL);
//
// Loop over all the pivots in the HTree
//
for (int pivot = 0; pivot < NumNodes; pivot ++) {
PivotChannel[pivot] = 0;
//
// Find out which animation channel affects this pivot
//
for (int channel = 0; channel < ChannelCount; channel ++) {
if (PoseData[channel]->Is_Node_Motion_Present (pivot)) {
PivotChannel[pivot] = channel;
}
}
}
return ;
}
void HMorphAnimClass::Set_Name(const char * name)
{
//
// Copy the full name
//
::strcpy (Name, name);
//
// Try to find the separator (a period)
//
StringClass full_name = name;
char *separator = ::strchr (full_name, '.');
if (separator != NULL) {
//
// Null out the separator and copy the two names
// into our two buffers
//
separator[0] = 0;
::strcpy (AnimName, separator + 1);
::strcpy (HierarchyName, full_name);
}
return ;
}
void HMorphAnimClass::Free_Morph(void)
{
Free();
}
int HMorphAnimClass::Create_New_Morph(const int channels, HAnimClass *anim[])
{
// clean out the previous instance of this class
Free();
// set the number of channels
ChannelCount = channels;
// read in the animation header
if (anim == NULL) {
return LOAD_ERROR;
}
// set up info
// FrameCount = anim[0]->Get_Num_Frames();
FrameCount = 0;
FrameRate = anim[0]->Get_Frame_Rate();
NumNodes = anim[0]->Get_Num_Pivots();
// Set up the anim data for all the channels
PoseData = W3DNEWARRAY HAnimClass * [ChannelCount];
for(int i=0;i<ChannelCount;i++)
PoseData[i] = anim[i];
// Create a timecodekey array for each channel and initialize the pivot channels
MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
PivotChannel = W3DNEWARRAY uint32[NumNodes];
// Resolve the pivots so that they correspond to the proper morphing channels
memset(PivotChannel,0,NumNodes * sizeof(uint32));
Resolve_Pivot_Channels();
// Signal successful process
return OK;
}
int HMorphAnimClass::Load_W3D(ChunkLoadClass & cload)
{
Free();
// read in the animation header
W3dMorphAnimHeaderStruct header;
cload.Open_Chunk();
WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_HEADER);
cload.Read(&header,sizeof(header));
cload.Close_Chunk();
strncpy(AnimName,header.Name,sizeof(AnimName));
strncpy(HierarchyName,header.HierarchyName,sizeof(HierarchyName));
strcpy(Name,HierarchyName);
strcat(Name,".");
strcat(Name,AnimName);
HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName);
if (base_pose == NULL) {
return LOAD_ERROR;
}
NumNodes = base_pose->Num_Pivots();
FrameCount = header.FrameCount;
FrameRate = header.FrameRate;
ChannelCount = header.ChannelCount;
PoseData = W3DNEWARRAY HAnimClass * [ChannelCount];
MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
PivotChannel = W3DNEWARRAY uint32[NumNodes];
memset(PivotChannel,0,NumNodes * sizeof(uint32));
// read in the rest of the chunks
int cur_channel = 0;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case W3D_CHUNK_MORPHANIM_CHANNEL:
read_channel(cload,cur_channel++);
break;
case W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA:
cload.Read(PivotChannel,cload.Cur_Chunk_Length());
break;
};
cload.Close_Chunk();
}
return OK;
}
void HMorphAnimClass::read_channel(ChunkLoadClass & cload,int channel)
{
WWASSERT(channel >= 0);
WWASSERT(channel < ChannelCount);
StringClass anim_name;
cload.Open_Chunk();
WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_POSENAME);
cload.Read(anim_name.Get_Buffer(cload.Cur_Chunk_Length()),cload.Cur_Chunk_Length());
cload.Close_Chunk();
//StringClass channel_anim_name;
//channel_anim_name.Format ("%s.%s", HierarchyName, anim_name);
PoseData[channel] = WW3DAssetManager::Get_Instance()->Get_HAnim(anim_name);
WWASSERT(PoseData[channel] != NULL);
cload.Open_Chunk();
WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_KEYDATA);
MorphKeyData[channel].Load_W3D(cload);
cload.Close_Chunk();
}
int HMorphAnimClass::Save_W3D(ChunkSaveClass & csave)
{
// W3D objects write their own wrapper chunks
csave.Begin_Chunk(W3D_CHUNK_MORPH_ANIMATION);
// init the header data
W3dMorphAnimHeaderStruct header;
memset(&header,0,sizeof(header));
strncpy(header.Name,AnimName,sizeof(header.Name));
strncpy(header.HierarchyName,HierarchyName,sizeof(header.HierarchyName));
header.FrameCount = FrameCount;
header.FrameRate = FrameRate;
header.ChannelCount = ChannelCount;
// write out the animation header
csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_HEADER);
csave.Write(&header,sizeof(header));
csave.End_Chunk();
// write out the morph channels
for (int ci=0; ci<ChannelCount; ci++) {
csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_CHANNEL);
write_channel(csave,ci);
csave.End_Chunk();
}
// write out the pivot attachments
csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA);
csave.Write(PivotChannel,NumNodes * sizeof(uint32));
csave.End_Chunk();
csave.End_Chunk();
return OK;
}
void HMorphAnimClass::write_channel(ChunkSaveClass & csave,int channel)
{
WWASSERT(PoseData[channel] != NULL);
const char * pose_name = PoseData[channel]->Get_Name();
csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_POSENAME);
csave.Write(pose_name,strlen(pose_name) + 1);
csave.End_Chunk();
csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_KEYDATA);
MorphKeyData[channel].Save_W3D(csave);
csave.End_Chunk();
}
void HMorphAnimClass::Get_Translation(Vector3& trans,int pividx,float frame) const
{
int channel = PivotChannel[pividx];
int pose_frame0,pose_frame1;
float fraction;
MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
Vector3 t0;
PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
Vector3 t1;
PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
Vector3::Lerp(t0,t1,fraction,&trans);
}
void HMorphAnimClass::Get_Orientation(Quaternion& q, int pividx,float frame) const
{
int channel = PivotChannel[pividx];
int pose_frame0,pose_frame1;
float fraction;
MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
Quaternion q0;
PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
Quaternion q1;
PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
::Fast_Slerp(q,q0,q1,fraction);
}
void HMorphAnimClass::Get_Transform(Matrix3D& mtx,int pividx,float frame) const
{
int channel = PivotChannel[pividx];
int pose_frame0,pose_frame1;
float fraction;
MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
Quaternion q0;
PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
Quaternion q1;
PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
Quaternion q;
::Fast_Slerp(q,q0,q1,fraction);
::Build_Matrix3D(q,mtx);
Vector3 t0;
PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
Vector3 t1;
PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
Vector3 trans;
Vector3::Lerp(t0,t1,fraction,&trans);
mtx.Set_Translation(trans);
}
void HMorphAnimClass::Insert_Morph_Key(const int channel, uint32 morph_frame, uint32 pose_frame)
{
assert(channel<ChannelCount);
MorphKeyData[channel].Add_Key(morph_frame,pose_frame);
// update the framecount to reflect the newly added key
FrameCount = WWMath::Max(morph_frame,FrameCount);
}
void HMorphAnimClass::Release_Keys(void)
{
for(int i=0;i<ChannelCount;i++)
MorphKeyData[i].Free();
// update the framecount as 0
FrameCount = 0;
}

Some files were not shown because too many files have changed in this diff Show More