Bootstrap decomp of Tgl rendering library (#293)

* Bootstrap decomp of D3DRM rendering code

* This PR kicks off work on decompiling the D3D Retained Mode (D3DRM)
  rendering part of the codebase.

* High level overview:

* There is a base IMxDirect3DRMObject class which all of the D3DRM
  rendering objects inherit from. Its only virtual method is one to get
  the underlying object handle.

* A hierarchy of abstract classes inherits from this base class, which
  I've called "IMxDirect3DRM<class>". These classes only have pure
  virtual methods on them and don't contain any data.

* Each one of the abstract classes has exactly one concrete
  implementation, which I've called "MxDirect3DRM<class>". These classes
  have exactly one piece of data, which is a pointer to the underlying
  D3D Retained Mode object.

* If the classes need to store additional data, they store it in a
  userdata blob which is attached to the D3DRM object rather than the
  additional data being stored in the class itself.

* I've worked out about twice this many classes related to D3DRM
  rendering so far but the PR was getting large enough as is, so I'm
  cutting it here for now.

* I decomped sufficiently many methods of these classe to convince
  myself that the above observations are correct. About 60% of the
  decomped methods here are perfect matches, including at least one
  non-trivial method per class.

* Formatting

* Restructure changes using Tgl naming / details

* Restructure the changes to use the naming that we know from Tgl.

* Fill in some parts of the implementation I couldn't initially figure
  out using the details from Tgl (got more 100% matches).

* Move d3drm link requirement

* Fixups FloatMatrix -> FloatMatrix4

* Fix order

* Full fix for ordering problems

* Put back accidentally removed include.

* Fix call which should have been Release

* Use new and delete for DeepClone

* Missing Tgl:: on CreateRenderer

* Revert change to bool return value.

* Rename Something -> Unk

* Return paramter naming convention to what Tgl used

* Add scalar ddtor to verify inline destructor

* Fix order

* Change malloc/free -> new/delete in Tgl

* Remove duplicate destructor.

* Check all inline destructors

* Fix dtor comments

* Third time's the charm

* Alphabetical sort

* Decomp adjustments

* Add d3drm files to clang-format

---------

Co-authored-by: Christian Semmler <mail@csemmler.com>
This commit is contained in:
Mark Langen 2023-12-07 04:10:42 -08:00 committed by GitHub
parent 8bf0bde6b9
commit 260772e374
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1976 additions and 186 deletions

View file

@ -19,6 +19,7 @@ jobs:
LEGO1/*.cpp LEGO1/*.h \
LEGO1/realtime/*.cpp LEGO1/realtime/*.h \
LEGO1/tgl/*.h \
LEGO1/tgl/d3drm/*.cpp LEGO1/tgl/d3drm/*.h \
LEGO1/viewmanager/*.cpp LEGO1/viewmanager/*.h
python-format:

View file

@ -210,6 +210,15 @@ add_library(lego1 SHARED
LEGO1/score.cpp
LEGO1/scorestate.cpp
LEGO1/skateboard.cpp
LEGO1/tgl/d3drm/camera.cpp
LEGO1/tgl/d3drm/device.cpp
LEGO1/tgl/d3drm/group.cpp
LEGO1/tgl/d3drm/light.cpp
LEGO1/tgl/d3drm/mesh.cpp
LEGO1/tgl/d3drm/renderer.cpp
LEGO1/tgl/d3drm/texture.cpp
LEGO1/tgl/d3drm/unk.cpp
LEGO1/tgl/d3drm/view.cpp
LEGO1/towtrack.cpp
LEGO1/towtrackmissionstate.cpp
LEGO1/viewmanager/viewmanager.cpp
@ -241,7 +250,7 @@ if (ISLE_USE_DX5)
endif()
# Link libraries
target_link_libraries(lego1 PRIVATE ddraw dsound dxguid dinput winmm)
target_link_libraries(lego1 PRIVATE ddraw dsound dxguid dinput winmm d3drm)
# Make sure filenames are ALL CAPS
set_property(TARGET lego1 PROPERTY OUTPUT_NAME LEGO1)

View file

@ -0,0 +1,33 @@
#include "impl.h"
using namespace TglImpl;
DECOMP_SIZE_ASSERT(Camera, 0x4);
DECOMP_SIZE_ASSERT(CameraImpl, 0x8);
// SYNTHETIC: LEGO1 0x100a2560
// TglImpl::CameraImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a36f0
void* CameraImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a3700
Result CameraImpl::SetTransformation(const FloatMatrix4& matrix)
{
D3DRMMATRIX4D helper;
D3DRMMATRIX4D* pTransformation = Translate(matrix, helper);
D3DVECTOR position;
Result result;
Result result2;
result2 = ResultVal(m_data->GetPosition(0, &position));
result = ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *pTransformation));
// The did this second call just to assert on the return value
result2 = ResultVal(m_data->GetPosition(0, &position));
return result;
}

View file

@ -0,0 +1,85 @@
#include "impl.h"
#include <d3drmwin.h>
using namespace TglImpl;
// SYNTHETIC: LEGO1 0x100a22c0
// TglImpl::DeviceImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a2bf0
void* DeviceImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a2c00
unsigned long DeviceImpl::GetWidth()
{
return m_data->GetWidth();
}
// FUNCTION: LEGO1 0x100a2c10
unsigned long DeviceImpl::GetHeight()
{
return m_data->GetHeight();
}
// FUNCTION: LEGO1 0x100a2c20
Result DeviceImpl::SetColorModel(ColorModel)
{
return Success;
}
// FUNCTION: LEGO1 0x100a2c30
Result DeviceImpl::SetShadingModel(ShadingModel model)
{
// Doesn't match well even though we know this is exactly
// the original code thanks to the jump table.
D3DRMRENDERQUALITY renderQuality = Translate(model);
return ResultVal(m_data->SetQuality(renderQuality));
}
// FUNCTION: LEGO1 0x100a2ca0
Result DeviceImpl::SetShadeCount(unsigned long shadeCount)
{
return ResultVal(m_data->SetShades(shadeCount));
}
// FUNCTION: LEGO1 0x100a2cc0
Result DeviceImpl::SetDither(int dither)
{
return ResultVal(m_data->SetDither(dither));
}
// Probably wrong, not sure what's going on in this method.
// FUNCTION: LEGO1 0x100a2ce0
void DeviceImpl::InitFromD3DDevice(Device*)
{
// Device argument is intentionally unused.
IDirect3DRMWinDevice* winDevice;
if (ResultVal(m_data->QueryInterface(IID_IDirect3DRMWinDevice, (LPVOID*) &winDevice))) {
m_data->InitFromD3D((LPDIRECT3D) &winDevice, (LPDIRECT3DDEVICE) m_data);
winDevice->Release();
}
}
// Really don't know what's going on here. Seems it will call down to Init
// but the decomp suggests it otherwise looks the same as InitFromD3D but Init
// takes widly different parameters.
// FUNCTION: LEGO1 0x100a2d20
void DeviceImpl::InitFromWindowsDevice(Device*)
{
// Device argument is intentionally unused.
IDirect3DRMWinDevice* winDevice;
if (SUCCEEDED(m_data->QueryInterface(IID_IDirect3DRMWinDevice, (LPVOID*) &winDevice))) {
// m_data->Init(??);
winDevice->Release();
}
}
// FUNCTION: LEGO1 0x100a2d60
Result DeviceImpl::Update()
{
return ResultVal(m_data->Update());
}

122
LEGO1/tgl/d3drm/group.cpp Normal file
View file

@ -0,0 +1,122 @@
#include "impl.h"
using namespace TglImpl;
// SYNTHETIC: LEGO1 0x100a2480
// TglImpl::GroupImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a31d0
void* GroupImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a31e0
Result GroupImpl::SetTransformation(const FloatMatrix4& matrix)
{
D3DRMMATRIX4D helper;
D3DRMMATRIX4D* d3dMatrix = Translate(matrix, helper);
return ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *d3dMatrix));
}
// FUNCTION: LEGO1 0x100a3240
Result GroupImpl::SetColor(float r, float g, float b, float a)
{
// The first instruction makes no sense here:
// cmp dword ptr [esp + 0x10], 0
// This compares a, which we know is a float because it immediately
// gets passed into D3DRMCreateColorRGBA, but does the comparison
// as though it's an int??
if (*reinterpret_cast<int*>(&a) > 0) {
D3DCOLOR color = D3DRMCreateColorRGBA(r, g, b, a);
return ResultVal(m_data->SetColor(color));
}
else {
return ResultVal(m_data->SetColorRGB(r, a, b));
}
}
// FUNCTION: LEGO1 0x100a32b0
Result GroupImpl::SetTexture(const Texture* pTexture)
{
IDirect3DRMTexture* pD3DTexture = pTexture ? static_cast<const TextureImpl*>(pTexture)->ImplementationData() : NULL;
return ResultVal(m_data->SetTexture(pD3DTexture));
}
// FUNCTION: LEGO1 0x100a32e0
Result GroupImpl::GetTexture(Texture*& pTexture)
{
IDirect3DRMTexture* pD3DTexture;
TextureImpl* holder = new TextureImpl();
Result result = ResultVal(m_data->GetTexture(&pD3DTexture));
if (result) {
// Seems to actually call the first virtual method of holder here
// but that doesn't make any sense since it passes three arguments
// to the method (self + string constant? + an offset?).
// This line makes the start of the function match and is what I
// would expect to see there but it clearly isn't what's actually
// there.
holder->SetImplementation(pD3DTexture);
}
pTexture = holder;
return Success;
}
// FUNCTION: LEGO1 0x100a33c0
Result GroupImpl::SetMaterialMode(MaterialMode mode)
{
D3DRMMATERIALMODE d3dMode;
switch (mode) {
case FromParent:
d3dMode = D3DRMMATERIAL_FROMPARENT;
break;
case FromFrame:
d3dMode = D3DRMMATERIAL_FROMFRAME;
break;
case FromMesh:
d3dMode = D3DRMMATERIAL_FROMMESH;
break;
}
return ResultVal(m_data->SetMaterialMode(d3dMode));
}
// FUNCTION: LEGO1 0x100a3410
Result GroupImpl::Add(const Mesh* pMesh)
{
const MeshImpl* pMeshImpl = static_cast<const MeshImpl*>(pMesh);
return ResultVal(m_data->AddVisual(pMeshImpl->ImplementationData()->groupMesh));
}
// FUNCTION: LEGO1 0x100a3430
Result GroupImpl::Add(const Group* pGroup)
{
const GroupImpl* pGroupImpl = static_cast<const GroupImpl*>(pGroup);
return ResultVal(m_data->AddVisual(pGroupImpl->m_data));
}
// FUNCTION: LEGO1 0x100a3450
Result GroupImpl::Remove(const Group* pGroup)
{
const GroupImpl* pGroupImpl = static_cast<const GroupImpl*>(pGroup);
return ResultVal(m_data->DeleteVisual(pGroupImpl->m_data));
}
// FUNCTION: LEGO1 0x100a3480
Result GroupImpl::Remove(const Mesh* pMesh)
{
const MeshImpl* pMeshImpl = static_cast<const MeshImpl*>(pMesh);
return ResultVal(m_data->DeleteVisual(pMeshImpl->ImplementationData()->groupMesh));
}
// FUNCTION: LEGO1 0x100a34b0 STUB
Result GroupImpl::RemoveAll()
{
return Error;
}
// FUNCTION: LEGO1 0x100a34c0 STUB
Result GroupImpl::Unknown()
{
return Error;
}

467
LEGO1/tgl/d3drm/impl.h Normal file
View file

@ -0,0 +1,467 @@
#include "../../decomp.h"
#include "../tgl.h"
#include <d3drm.h>
// Forward declare D3D types
struct IDirect3DRM;
struct IDirect3DRMDevice;
struct IDirect3DRMViewport;
struct IDirect3DRMFrame;
struct IDirect3DRMMesh;
struct IDirect3DRMMeshBuilder;
struct IDirect3DRMTexture;
namespace TglImpl
{
using namespace Tgl;
// Utility function used by implementations
inline Result ResultVal(HRESULT result)
{
return SUCCEEDED(result) ? Success : Error;
}
// Forward declare implementations
class RendererImpl;
class DeviceImpl;
class ViewImpl;
class LightImpl;
class CameraImpl;
class GroupImpl;
class MeshImpl;
class TextureImpl;
class UnkImpl;
// VTABLE 0x100db910
class RendererImpl : public Renderer {
public:
RendererImpl() : m_data(0) {}
~RendererImpl() { Destroy(); };
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Device* CreateDevice(const DeviceDirect3DCreateData&);
virtual Device* CreateDevice(const DeviceDirectDrawCreateData&);
// vtable+0x10
virtual View* CreateView(
const Device*,
const Camera*,
unsigned long x,
unsigned long y,
unsigned long width,
unsigned long height
);
virtual Camera* CreateCamera();
virtual Light* CreateLight(LightType, float r, float g, float b);
virtual Group* CreateGroup(const Group* pParent);
// vtable+0x20
virtual Unk* CreateUnk();
virtual Texture* CreateTexture();
virtual Texture* CreateTexture(
int width,
int height,
int bitsPerTexel,
const void* pTexels,
int pTexelsArePersistent,
int paletteEntryCount,
const PaletteEntry* pEntries
);
virtual Result SetTextureDefaultShadeCount(unsigned long);
// vtable+0x30
virtual Result SetTextureDefaultColorCount(unsigned long);
public:
inline Result Create();
inline void Destroy();
private:
IDirect3DRM* m_data;
};
// VTABLE 0x100db988
class DeviceImpl : public Device {
public:
DeviceImpl() : m_data(0) {}
~DeviceImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual unsigned long GetWidth();
virtual unsigned long GetHeight();
// vtable+0x10
virtual Result SetColorModel(ColorModel);
virtual Result SetShadingModel(ShadingModel);
virtual Result SetShadeCount(unsigned long);
virtual Result SetDither(int);
// vtable+0x20
virtual Result Update();
virtual void InitFromD3DDevice(Device*);
virtual void InitFromWindowsDevice(Device*);
inline IDirect3DRMDevice* ImplementationData() const { return m_data; }
friend class RendererImpl;
private:
IDirect3DRMDevice* m_data;
};
// VTABLE 0x100db9e8
class ViewImpl : public View {
public:
ViewImpl() : m_data(0) {}
~ViewImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result Add(const Light*);
virtual Result Remove(const Light*);
// vtable+0x10
virtual Result SetCamera(const Camera*);
virtual Result SetProjection(ProjectionType);
virtual Result SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees);
virtual Result SetBackgroundColor(float r, float g, float b);
// vtable+0x20
virtual Result GetBackgroundColor(float* r, float* g, float* b);
virtual Result Clear();
virtual Result Render(const Light*);
virtual Result ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height);
// vtable+0x30
virtual Result TransformWorldToScreen(const float world[3], float screen[4]);
virtual Result TransformScreenToWorld(const float screen[4], float world[3]);
virtual Result Pick(
unsigned long x,
unsigned long y,
const Group** ppGroupsToPickFrom,
int groupsToPickFromCount,
const Group**& rppPickedGroups,
int& rPickedGroupCount
);
inline IDirect3DRMViewport* ImplementationData() const { return m_data; }
static Result ViewportCreateAppData(IDirect3DRM*, IDirect3DRMViewport*, IDirect3DRMFrame*);
friend class RendererImpl;
private:
IDirect3DRMViewport* m_data;
};
// VTABLE 0x100dbad8
class CameraImpl : public Camera {
public:
CameraImpl() : m_data(0) {}
~CameraImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetTransformation(const FloatMatrix4&);
inline IDirect3DRMFrame* ImplementationData() const { return m_data; }
friend class RendererImpl;
private:
IDirect3DRMFrame* m_data;
};
// VTABLE 0x100dbaf8
class LightImpl : public Light {
public:
LightImpl() : m_data(0) {}
~LightImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetTransformation(const FloatMatrix4&);
virtual Result SetColor(float r, float g, float b);
inline IDirect3DRMFrame* ImplementationData() const { return m_data; }
friend class RendererImpl;
private:
IDirect3DRMFrame* m_data;
};
// VTABLE 0x100dbb88
class MeshImpl : public Mesh {
public:
MeshImpl() : m_data(0) {}
~MeshImpl()
{
if (m_data) {
delete m_data;
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetColor(float r, float g, float b, float a);
virtual Result SetTexture(const Texture*);
// vtable+0x10
virtual Result GetTexture(Texture*&);
virtual Result SetTextureMappingMode(ProjectionType);
virtual Result SetShadingModel(ShadingModel);
virtual Mesh* DeepClone(Unk*);
// vtable+0x20
virtual Mesh* ShallowClone(Unk*);
struct MeshData {
IDirect3DRMMesh* groupMesh;
int groupIndex;
};
inline MeshData* ImplementationData() const { return m_data; }
friend class RendererImpl;
private:
MeshData* m_data;
};
// VTABLE 0x100dba68
class GroupImpl : public Group {
public:
GroupImpl() : m_data(0) {}
~GroupImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetTransformation(const FloatMatrix4&);
virtual Result SetColor(float r, float g, float b, float a);
// vtable+0x10
virtual Result SetTexture(const Texture*);
virtual Result GetTexture(Texture*&);
virtual Result SetMaterialMode(MaterialMode);
virtual Result Add(const Group*);
// vtable+0x20
virtual Result Add(const Mesh*);
virtual Result Remove(const Group*);
virtual Result Remove(const Mesh*);
virtual Result RemoveAll();
// vtable+0x30
virtual Result Unknown();
friend class RendererImpl;
private:
IDirect3DRMFrame* m_data;
};
// VTABLE 0x100dbb18
class UnkImpl : public Unk {
public:
UnkImpl() : m_data(0) {}
~UnkImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetMeshData(
unsigned long faceCount,
unsigned long vertexCount,
const float (*pPositions)[3],
const float (*pNormals)[3],
const float (*pTextureCoordinates)[2],
unsigned long vertexPerFaceCount,
unsigned long* pFaceData
);
virtual Result GetBoundingBox(float min[3], float max[3]);
// vtable+0x10
virtual Unk* Clone();
inline IDirect3DRMMesh* ImplementationData() const { return m_data; }
friend class RendererImpl;
private:
IDirect3DRMMesh* m_data;
};
// No vtable, this is just a simple wrapper around D3DRMIMAGE
class TglD3DRMIMAGE {
public:
TglD3DRMIMAGE(
int width,
int height,
int depth,
void* pBuffer,
int useBuffer,
int paletteSize,
PaletteEntry* pEntries
);
~TglD3DRMIMAGE() { Destroy(); }
Result CreateBuffer(int width, int height, int depth, void* pBuffer, int useBuffer);
void Destroy();
void FillRowsOfTexture(int y, int height, char* content);
Result InitializePalette(int paletteSize, PaletteEntry* pEntries);
D3DRMIMAGE m_image;
int m_texelsAllocatedByClient;
};
// VTABLE 0x100dbb48
class TextureImpl : public Texture {
public:
TextureImpl() : m_data(0) {}
~TextureImpl()
{
if (m_data) {
m_data->Release();
m_data = NULL;
}
}
virtual void* ImplementationDataPtr();
// vtable+0x08
virtual Result SetTexels(int width, int height, int bitsPerTexel, void* pTexels);
virtual void FillRowsOfTexture(int y, int height, void* pBuffer);
// vtable+0x10
virtual Result Changed(int texelsChanged, int paletteChanged);
virtual Result GetBufferAndPalette(
int* pWidth,
int* pHeight,
int* pDepth,
void** ppBuffer,
int* ppPaletteSize,
PaletteEntry** ppPalette
);
virtual Result SetPalette(int entryCount, PaletteEntry* entries);
inline IDirect3DRMTexture* ImplementationData() const { return m_data; }
inline void SetImplementation(IDirect3DRMTexture* pData) { m_data = pData; }
friend class RendererImpl;
static Result SetImage(IDirect3DRMTexture* pSelf, TglD3DRMIMAGE* pImage);
private:
IDirect3DRMTexture* m_data;
};
// Translation helpers
inline D3DRMRENDERQUALITY Translate(ShadingModel tglShadingModel)
{
D3DRMRENDERQUALITY renderQuality;
switch (tglShadingModel) {
case Wireframe:
renderQuality = D3DRMRENDER_WIREFRAME;
break;
case UnlitFlat:
renderQuality = D3DRMRENDER_UNLITFLAT;
break;
case Flat:
renderQuality = D3DRMRENDER_FLAT;
break;
case Gouraud:
renderQuality = D3DRMRENDER_GOURAUD;
break;
case Phong:
renderQuality = D3DRMRENDER_PHONG;
break;
default:
renderQuality = D3DRMRENDER_FLAT;
break;
}
return renderQuality;
}
inline D3DRMPROJECTIONTYPE Translate(ProjectionType tglProjectionType)
{
D3DRMPROJECTIONTYPE projectionType;
switch (tglProjectionType) {
case Perspective:
projectionType = D3DRMPROJECT_PERSPECTIVE;
break;
case Orthographic:
projectionType = D3DRMPROJECT_ORTHOGRAPHIC;
break;
default:
projectionType = D3DRMPROJECT_PERSPECTIVE;
break;
}
return projectionType;
}
// Yes this function serves no purpose, originally they intended it to
// convert from doubles to floats but ended up using floats throughout
// the software stack.
inline D3DRMMATRIX4D* Translate(const FloatMatrix4& tglMatrix4x4, D3DRMMATRIX4D& rD3DRMMatrix4x4)
{
for (int i = 0; i < (sizeof(rD3DRMMatrix4x4) / sizeof(rD3DRMMatrix4x4[0])); i++) {
for (int j = 0; j < (sizeof(rD3DRMMatrix4x4[0]) / sizeof(rD3DRMMatrix4x4[0][0])); j++) {
rD3DRMMatrix4x4[i][j] = D3DVAL(tglMatrix4x4[i][j]);
}
}
return &rD3DRMMatrix4x4;
}
} /* namespace TglImpl */

33
LEGO1/tgl/d3drm/light.cpp Normal file
View file

@ -0,0 +1,33 @@
#include "impl.h"
using namespace TglImpl;
DECOMP_SIZE_ASSERT(Light, 0x4);
DECOMP_SIZE_ASSERT(LightImpl, 0x8);
// SYNTHETIC: LEGO1 0x100a2640
// TglImpl::LightImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a3770
void* LightImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a3780
Result LightImpl::SetTransformation(const FloatMatrix4& matrix)
{
D3DRMMATRIX4D helper;
D3DRMMATRIX4D* d3dMatrix = Translate(matrix, helper);
return ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *d3dMatrix));
}
// FUNCTION: LEGO1 0x100a37e0
Result LightImpl::SetColor(float r, float g, float b)
{
IDirect3DRMLightArray* lightArray;
IDirect3DRMLight* light;
m_data->GetLights(&lightArray);
lightArray->GetElement(0, &light);
return ResultVal(light->SetColorRGB(r, g, b));
}

158
LEGO1/tgl/d3drm/mesh.cpp Normal file
View file

@ -0,0 +1,158 @@
#include "impl.h"
using namespace TglImpl;
DECOMP_SIZE_ASSERT(D3DRMVERTEX, 0x24);
DECOMP_SIZE_ASSERT(Mesh, 0x4);
DECOMP_SIZE_ASSERT(MeshImpl, 0x8);
// SYNTHETIC: LEGO1 0x100a3d80
// TglImpl::MeshImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a3ed0
void* MeshImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a3ee0
Result MeshImpl::SetColor(float r, float g, float b, float a)
{
// The first instruction makes no sense here:
// cmp dword ptr [esp + 0x10], 0
// This compares a, which we know is a float because it immediately
// gets passed into D3DRMCreateColorRGBA, but does the comparison
// as though it's an int??
if (*reinterpret_cast<int*>(&a) > 0) {
D3DCOLOR color = D3DRMCreateColorRGBA(r, g, b, a);
return ResultVal(m_data->groupMesh->SetGroupColor(m_data->groupIndex, color));
}
else {
return ResultVal(m_data->groupMesh->SetGroupColorRGB(m_data->groupIndex, r, g, b));
}
}
// FUNCTION: LEGO1 0x100a3f50
Result MeshImpl::SetTexture(const Texture* pTexture)
{
IDirect3DRMTexture* texture = pTexture ? static_cast<const TextureImpl*>(pTexture)->ImplementationData() : NULL;
return ResultVal(m_data->groupMesh->SetGroupTexture(m_data->groupIndex, texture));
}
// FUNCTION: LEGO1 0x100a3f80
Result MeshImpl::SetTextureMappingMode(ProjectionType projType)
{
if (projType == Perspective) {
return ResultVal(m_data->groupMesh->SetGroupMapping(m_data->groupIndex, D3DRMMAP_PERSPCORRECT));
}
else {
return ResultVal(m_data->groupMesh->SetGroupMapping(m_data->groupIndex, 0));
}
}
// FUNCTION: LEGO1 0x100a3fc0
Result MeshImpl::SetShadingModel(ShadingModel model)
{
D3DRMRENDERQUALITY mode;
switch (model) {
case Wireframe:
mode = D3DRMRENDER_WIREFRAME;
break;
case UnlitFlat:
mode = D3DRMRENDER_UNLITFLAT;
break;
case Flat:
mode = D3DRMRENDER_FLAT;
break;
case Gouraud:
mode = D3DRMRENDER_GOURAUD;
break;
case Phong:
mode = D3DRMRENDER_PHONG;
break;
}
return ResultVal(m_data->groupMesh->SetGroupQuality(m_data->groupIndex, mode));
}
// FUNCTION: LEGO1 0x100a4030
Mesh* MeshImpl::DeepClone(Unk* pUnk)
{
// Create group
MeshImpl* newMesh = new MeshImpl();
MeshData* data = new MeshData();
newMesh->m_data = data;
// Query information from old group
DWORD dataSize;
unsigned int vcount, fcount, vperface;
m_data->groupMesh->GetGroup(m_data->groupIndex, &vcount, &fcount, &vperface, &dataSize, NULL);
unsigned int* faceBuffer = new unsigned int[dataSize];
m_data->groupMesh->GetGroup(m_data->groupIndex, &vcount, &fcount, &vperface, &dataSize, faceBuffer);
// We expect vertex to be sized 0x24, checked at start of file.
D3DRMVERTEX* vertexBuffer = new D3DRMVERTEX[vcount];
m_data->groupMesh->GetVertices(m_data->groupIndex, 0, vcount, vertexBuffer);
LPDIRECT3DRMTEXTURE textureRef;
m_data->groupMesh->GetGroupTexture(m_data->groupIndex, &textureRef);
D3DRMMAPPING mapping = m_data->groupMesh->GetGroupMapping(m_data->groupIndex);
D3DRMRENDERQUALITY quality = m_data->groupMesh->GetGroupQuality(m_data->groupIndex);
D3DCOLOR color = m_data->groupMesh->GetGroupColor(m_data->groupIndex);
// Push information to new group
UnkImpl* target = static_cast<UnkImpl*>(pUnk);
D3DRMGROUPINDEX index;
target->ImplementationData()->AddGroup(vcount, fcount, vperface, faceBuffer, &index);
newMesh->m_data->groupIndex = index;
target->ImplementationData()->SetVertices(index, 0, vcount, vertexBuffer);
target->ImplementationData()->SetGroupTexture(index, textureRef);
target->ImplementationData()->SetGroupMapping(index, mapping);
target->ImplementationData()->SetGroupQuality(index, quality);
Result result = ResultVal(target->ImplementationData()->SetGroupColor(index, color));
// Cleanup
delete[] faceBuffer;
delete[] vertexBuffer;
if (result == Error) {
delete newMesh;
newMesh = NULL;
}
return newMesh;
}
// FUNCTION: LEGO1 0x100a4240
Mesh* MeshImpl::ShallowClone(Unk* pUnk)
{
MeshImpl* newGroup = new MeshImpl();
MeshData* newData = new MeshData();
newGroup->m_data = newData;
if (newData) {
newData->groupIndex = m_data->groupIndex;
newData->groupMesh = static_cast<UnkImpl*>(pUnk)->ImplementationData();
}
else {
delete newGroup;
newGroup = NULL;
}
return newGroup;
}
// FUNCTION: LEGO1 0x100a4330
Result MeshImpl::GetTexture(Texture*& rpTexture)
{
IDirect3DRMTexture* texture;
TextureImpl* holder = new TextureImpl();
Result result = ResultVal(m_data->groupMesh->GetGroupTexture(m_data->groupIndex, &texture));
if (result) {
// Seems to actually call the first virtual method of holder here
// but that doesn't make any sense since it passes three arguments
// to the method (self + string constant? + an offset?).
// This line makes the start of the function match and is what I
// would expect to see there but it clearly isn't what's actually
// there.
holder->SetImplementation(texture);
}
rpTexture = holder;
return Success;
}

View file

@ -0,0 +1,332 @@
#include "impl.h"
using namespace TglImpl;
// FUNCTION: LEGO1 0x100a15e0
Renderer* Tgl::CreateRenderer()
{
RendererImpl* renderer = new RendererImpl();
if (!renderer->Create()) {
delete renderer;
renderer = NULL;
}
return renderer;
}
// SYNTHETIC: LEGO1 0x100a16d0
// TglImpl::RendererImpl::`scalar deleting destructor'
// GLOBAL: LEGO1 0x1010103c
IDirect3DRM* g_pD3DRM = NULL;
// Inlined only
Result RendererImpl::Create()
{
if (g_pD3DRM) {
g_pD3DRM->AddRef();
}
else {
LPDIRECT3DRM handle;
Direct3DRMCreate(&handle);
handle->QueryInterface(IID_IDirect3DRM2, (LPVOID*) &g_pD3DRM);
}
m_data = g_pD3DRM;
return (m_data != NULL) ? Success : Error;
}
inline void RendererDestroy(IDirect3DRM* pRenderer)
{
int refCount = pRenderer->Release();
if (refCount <= 0) {
g_pD3DRM = NULL;
}
}
// Inlined only
void RendererImpl::Destroy()
{
if (m_data) {
RendererDestroy(m_data);
m_data = NULL;
}
}
// FUNCTION: LEGO1 0x100a1894
Device* RendererImpl::CreateDevice(const DeviceDirect3DCreateData& data)
{
DeviceImpl* device = new DeviceImpl();
HRESULT result = m_data->CreateDeviceFromD3D(data.m_pDirect3D, data.m_pDirect3DDevice, &device->m_data);
if (!SUCCEEDED(result)) {
delete device;
device = NULL;
}
return device;
}
// GLOBAL: LEGO1 0x10101040
static int gSetBufferCount = 1;
// FUNCTION: LEGO1 0x100a1900
Device* RendererImpl::CreateDevice(const DeviceDirectDrawCreateData& data)
{
DeviceImpl* device = new DeviceImpl();
HRESULT result = m_data->CreateDeviceFromSurface(
const_cast<LPGUID>(data.m_driverGUID),
data.m_pDirectDraw,
data.m_pBackBuffer,
&device->m_data
);
if (SUCCEEDED(result) && data.m_pBackBuffer && gSetBufferCount) {
device->m_data->SetBufferCount(2);
}
if (!SUCCEEDED(result)) {
delete device;
device = NULL;
}
return device;
}
inline Result RendererCreateView(
IDirect3DRM* pRenderer,
IDirect3DRMDevice* pDevice,
IDirect3DRMFrame* pCamera,
IDirect3DRMViewport*& rpView,
unsigned long x,
unsigned long y,
unsigned long width,
unsigned long height
)
{
Result result = ResultVal(pRenderer->CreateViewport(pDevice, pCamera, x, y, width, height, &rpView));
if (Succeeded(result)) {
result = ViewImpl::ViewportCreateAppData(pRenderer, rpView, pCamera);
if (!Succeeded(result)) {
rpView->Release();
rpView = NULL;
}
}
return result;
}
// FUNCTION: LEGO1 0x100a1a00
View* RendererImpl::CreateView(
const Device* pDevice,
const Camera* pCamera,
unsigned long x,
unsigned long y,
unsigned long width,
unsigned long height
)
{
ViewImpl* view = new ViewImpl();
Result result = RendererCreateView(
m_data,
static_cast<const DeviceImpl*>(pDevice)->m_data,
static_cast<const CameraImpl*>(pCamera)->m_data,
view->m_data,
x,
y,
width,
height
);
if (!result) {
delete view;
view = NULL;
}
return view;
}
inline Result RendererCreateGroup(IDirect3DRM* pRenderer, IDirect3DRMFrame* pParent, IDirect3DRMFrame*& rpGroup)
{
Result result = ResultVal(pRenderer->CreateFrame(NULL, &rpGroup));
if (Succeeded(result) && pParent) {
result = ResultVal(pParent->AddVisual(rpGroup));
if (!Succeeded(result)) {
rpGroup->Release();
rpGroup = NULL;
}
}
return result;
}
// FUNCTION: LEGO1 0x100a1b20
Group* RendererImpl::CreateGroup(const Group* pParent)
{
GroupImpl* group = new GroupImpl();
Result result =
RendererCreateGroup(m_data, pParent ? static_cast<const GroupImpl*>(pParent)->m_data : NULL, group->m_data);
if (!result) {
delete group;
group = NULL;
}
return group;
}
// FUNCTION: LEGO1 0x100a1c30
Camera* RendererImpl::CreateCamera()
{
CameraImpl* camera = new CameraImpl();
if (FAILED(m_data->CreateFrame(NULL, &camera->m_data))) {
delete camera;
camera = NULL;
}
return camera;
}
// FUNCTION: LEGO1 0x100a1cf0
Light* RendererImpl::CreateLight(LightType type, float r, float g, float b)
{
LightImpl* newLight = new LightImpl();
D3DRMLIGHTTYPE translatedType;
switch (type) {
case Ambient:
translatedType = D3DRMLIGHT_AMBIENT;
break;
case Point:
translatedType = D3DRMLIGHT_POINT;
break;
case Spot:
translatedType = D3DRMLIGHT_SPOT;
break;
case Directional:
translatedType = D3DRMLIGHT_DIRECTIONAL;
break;
case ParallelPoint:
translatedType = D3DRMLIGHT_PARALLELPOINT;
break;
default:
translatedType = D3DRMLIGHT_AMBIENT;
}
LPDIRECT3DRMFRAME frame;
Result result = ResultVal(m_data->CreateFrame(NULL, &frame));
if (Succeeded(result)) {
LPDIRECT3DRMLIGHT d3dLight;
result = ResultVal(m_data->CreateLightRGB(translatedType, r, g, b, &d3dLight));
if (!Succeeded(result)) {
frame->Release();
}
else {
result = ResultVal(frame->AddLight(d3dLight));
if (!Succeeded(result)) {
d3dLight->Release();
frame->Release();
}
else {
d3dLight->Release();
newLight->m_data = frame;
}
}
}
if (!Succeeded(result)) {
delete newLight;
newLight = NULL;
}
return newLight;
}
// FUNCTION: LEGO1 0x100a1e90
Unk* RendererImpl::CreateUnk()
{
// Note: I'm fairly certain that Unknown is not what Tgl calls a
// "Mesh", because the methods on Mesh in the Tgl leak line up much
// more closely with a different vtable than the one assigned in
// this method (meaning this method is not creating a Mesh).
// Maybe this method is something like CreateMeshBuilder where the
// Mesh data type in the Tgl leak was split into builder/result?
UnkImpl* unknown = new UnkImpl();
if (FAILED(m_data->CreateMesh(&unknown->m_data))) {
delete unknown;
unknown = NULL;
}
return unknown;
}
inline Result RendererCreateTexture(
IDirect3DRM* renderer,
IDirect3DRMTexture*& texture,
int width,
int height,
int bytesPerPixel,
void* pBuffer,
int useBuffer,
int paletteSize,
PaletteEntry* pEntries
)
{
TglD3DRMIMAGE* image;
Result result;
image = new TglD3DRMIMAGE(width, height, bytesPerPixel, pBuffer, useBuffer, paletteSize, pEntries);
result = ResultVal(renderer->CreateTexture(&image->m_image, &texture));
if (Succeeded(result)) {
result = TextureImpl::SetImage(texture, image);
if (!Succeeded(result)) {
texture->Release();
texture = NULL;
delete image;
}
}
else {
delete image;
}
return result;
}
// FUNCTION: LEGO1 0x100a1f50
Texture* RendererImpl::CreateTexture(
int width,
int height,
int bitsPerTexel,
const void* pTexels,
int texelsArePersistent,
int paletteEntryCount,
const PaletteEntry* pEntries
)
{
TextureImpl* texture = new TextureImpl();
if (!Succeeded(RendererCreateTexture(
m_data,
texture->m_data,
width,
height,
bitsPerTexel,
const_cast<void*>(pTexels),
texelsArePersistent,
paletteEntryCount,
const_cast<PaletteEntry*>(pEntries)
))) {
delete texture;
texture = NULL;
}
return texture;
}
// FUNCTION: LEGO1 0x100a20d0
Texture* RendererImpl::CreateTexture()
{
TextureImpl* texture = new TextureImpl();
if (!Succeeded(RendererCreateTexture(m_data, texture->m_data, 0, 0, 0, NULL, FALSE, 0, NULL))) {
delete texture;
texture = NULL;
}
return texture;
}
// FUNCTION: LEGO1 0x100a2270
Result RendererImpl::SetTextureDefaultShadeCount(unsigned long shadeCount)
{
return ResultVal(m_data->SetDefaultTextureShades(shadeCount));
}
// FUNCTION: LEGO1 0x100a2290
Result RendererImpl::SetTextureDefaultColorCount(unsigned long colorCount)
{
return ResultVal(m_data->SetDefaultTextureColors(colorCount));
}
// FUNCTION: LEGO1 0x100a22b0
void* RendererImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}

196
LEGO1/tgl/d3drm/texture.cpp Normal file
View file

@ -0,0 +1,196 @@
#include "impl.h"
using namespace TglImpl;
DECOMP_SIZE_ASSERT(TglD3DRMIMAGE, 0x40);
inline TglD3DRMIMAGE* TextureGetImage(IDirect3DRMTexture* pTexture)
{
return reinterpret_cast<TglD3DRMIMAGE*>(pTexture->GetAppData());
}
// Forward declare to satisfy order check
void TextureDestroyCallback(IDirect3DRMObject* pObject, void* pArg);
// FUNCTION: LEGO1 0x100a12a0
Result TextureImpl::SetImage(IDirect3DRMTexture* pSelf, TglD3DRMIMAGE* pImage)
{
unsigned long appData;
Result result;
appData = reinterpret_cast<unsigned long>(pImage);
// This is here because in the original code they asserted
// on the return value being NULL.
TextureGetImage(pSelf);
result = ResultVal(pSelf->SetAppData(appData));
if (Succeeded(result) && pImage) {
result = ResultVal(pSelf->AddDestroyCallback(TextureDestroyCallback, NULL));
if (!Succeeded(result)) {
pSelf->SetAppData(0);
}
}
return result;
}
// FUNCTION: LEGO1 0x100a1300
void TextureDestroyCallback(IDirect3DRMObject* pObject, void* pArg)
{
TglD3DRMIMAGE* pImage = reinterpret_cast<TglD3DRMIMAGE*>(pObject->GetAppData());
delete pImage;
pObject->SetAppData(0);
}
// FUNCTION: LEGO1 0x100a1330
TglD3DRMIMAGE::TglD3DRMIMAGE(
int width,
int height,
int depth,
void* pBuffer,
int useBuffer,
int paletteSize,
PaletteEntry* pEntries
)
{
m_image.aspectx = 1;
m_image.aspecty = 1;
m_image.width = 0;
m_image.height = 0;
m_image.depth = 0;
m_image.rgb = 0;
m_image.bytes_per_line = 0;
m_image.buffer1 = NULL;
m_image.buffer2 = NULL;
m_image.red_mask = 0xFF;
m_image.green_mask = 0xFF;
m_image.blue_mask = 0xFF;
m_image.alpha_mask = 0xFF;
m_image.palette_size = 0;
m_image.palette = NULL;
m_texelsAllocatedByClient = 0;
if (pBuffer != NULL) {
CreateBuffer(width, height, depth, pBuffer, useBuffer);
}
if (pEntries != NULL) {
InitializePalette(paletteSize, pEntries);
}
}
// FUNCTION: LEGO1 0x100a13b0
void TglD3DRMIMAGE::Destroy()
{
if (m_texelsAllocatedByClient == 0) {
delete m_image.buffer1;
}
delete m_image.palette;
}
// FUNCTION: LEGO1 0x100a13e0 STUB
Result TglD3DRMIMAGE::CreateBuffer(int width, int height, int depth, void* pBuffer, int useBuffer)
{
return Error;
}
// FUNCTION: LEGO1 0x100a1510
void TglD3DRMIMAGE::FillRowsOfTexture(int y, int height, char* pContent)
{
// The purpose is clearly this but I can't get the assembly to line up.
memcpy((char*) m_image.buffer1 + (y * m_image.bytes_per_line), pContent, height * m_image.bytes_per_line);
}
// FUNCTION: LEGO1 0x100a1550
Result TglD3DRMIMAGE::InitializePalette(int paletteSize, PaletteEntry* pEntries)
{
// This function is a 100% match if the PaletteEntry class is copied
// into into the TglD3DRMIMAGE class instead of being a global struct.
if (m_image.palette_size != paletteSize) {
if (m_image.palette != NULL) {
delete m_image.palette;
m_image.palette = NULL;
m_image.palette_size = 0;
}
if (paletteSize > 0) {
m_image.palette = new D3DRMPALETTEENTRY[paletteSize];
m_image.palette_size = paletteSize;
}
}
if (paletteSize > 0) {
for (int i = 0; i < paletteSize; i++) {
m_image.palette[i].red = pEntries[i].m_red;
m_image.palette[i].green = pEntries[i].m_green;
m_image.palette[i].blue = pEntries[i].m_blue;
m_image.palette[i].flags = D3DRMPALETTE_READONLY;
}
}
return Success;
}
// SYNTHETIC: LEGO1 0x100a2800
// TglImpl::TextureImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a3c10
Result TextureImpl::SetTexels(int width, int height, int bitsPerTexel, void* pTexels)
{
TglD3DRMIMAGE* image = TextureGetImage(m_data);
Result result = image->CreateBuffer(width, height, bitsPerTexel, pTexels, TRUE);
if (Succeeded(result)) {
result = ResultVal(m_data->Changed(TRUE, FALSE));
}
return result;
}
// FUNCTION: LEGO1 0x100a3c60
void TextureImpl::FillRowsOfTexture(int y, int height, void* pBuffer)
{
TglD3DRMIMAGE* image = TextureGetImage(m_data);
image->FillRowsOfTexture(y, height, (char*) pBuffer);
}
// FUNCTION: LEGO1 0x100a3c90
Result TextureImpl::Changed(int texelsChanged, int paletteChanged)
{
return ResultVal(m_data->Changed(texelsChanged, paletteChanged));
}
// FUNCTION: LEGO1 0x100a3d00
Result TextureImpl::GetBufferAndPalette(
int* width,
int* height,
int* depth,
void** pBuffer,
int* paletteSize,
PaletteEntry** pEntries
)
{
// Something really doesn't match here, not sure what's up.
TglD3DRMIMAGE* image = TextureGetImage(m_data);
*width = image->m_image.width;
*height = image->m_image.height;
*depth = image->m_image.depth;
*pBuffer = image->m_image.buffer1;
*paletteSize = image->m_image.palette_size;
for (int i = 0; i < image->m_image.palette_size; i++) {
pEntries[i]->m_red = image->m_image.palette[i].red;
pEntries[i]->m_green = image->m_image.palette[i].green;
pEntries[i]->m_blue = image->m_image.palette[i].blue;
}
return Success;
}
// FUNCTION: LEGO1 0x100a3d40
Result TextureImpl::SetPalette(int entryCount, PaletteEntry* pEntries)
{
// Not 100% confident this is supposed to directly be forwarding arguments,
// but it probably is given FillRowsOfTexture matches doing that.
TglD3DRMIMAGE* image = TextureGetImage(m_data);
image->InitializePalette(entryCount, pEntries);
m_data->Changed(FALSE, TRUE);
return Success;
}
// FUNCTION: LEGO1 0x100a3d70
void* TextureImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}

57
LEGO1/tgl/d3drm/unk.cpp Normal file
View file

@ -0,0 +1,57 @@
#include "impl.h"
using namespace TglImpl;
DECOMP_SIZE_ASSERT(Unk, 0x4);
DECOMP_SIZE_ASSERT(UnkImpl, 0x8);
// SYNTHETIC: LEGO1 0x100a2720
// TglImpl::UnkImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a3830
void* UnkImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a3840 STUB
Result UnkImpl::SetMeshData(
unsigned long faceCount,
unsigned long vertexCount,
const float (*pPositions)[3],
const float (*pNormals)[3],
const float (*pTextureCoordinates)[2],
unsigned long vertexPerFaceCount,
unsigned long* pFaceData
)
{
return Error;
}
// FUNCTION: LEGO1 0x100a3ae0
Result UnkImpl::GetBoundingBox(float min[3], float max[3])
{
D3DRMBOX box;
Result result = ResultVal(m_data->GetBox(&box));
if (result == Success) {
min[0] = box.min.x;
min[1] = box.min.y;
min[2] = box.min.z;
max[0] = box.max.x;
max[1] = box.max.y;
max[2] = box.max.z;
}
return result;
}
// FUNCTION: LEGO1 0x100a3b40
Unk* UnkImpl::Clone()
{
UnkImpl* mesh = new UnkImpl();
int ret = m_data->Clone(0, IID_IDirect3DRMMeshBuilder, (void**) &mesh->m_data);
if (ret < 0) {
delete mesh;
mesh = NULL;
}
return mesh;
}

349
LEGO1/tgl/d3drm/view.cpp Normal file
View file

@ -0,0 +1,349 @@
#include "impl.h"
using namespace TglImpl;
struct ViewportAppData {
ViewportAppData(IDirect3DRM* pRenderer);
~ViewportAppData();
IDirect3DRMFrame* m_pLightFrame;
IDirect3DRMFrame* m_pCamera;
IDirect3DRMFrame* m_pLastRenderedFrame;
float m_backgroundColorRed;
float m_backgroundColorGreen;
float m_backgroundColorBlue;
};
DECOMP_SIZE_ASSERT(ViewportAppData, 0x18);
// FUNCTION: LEGO1 0x100a10b0
ViewportAppData::ViewportAppData(IDirect3DRM* pRenderer)
{
pRenderer->CreateFrame(NULL, &m_pLightFrame);
m_pCamera = NULL;
m_pLastRenderedFrame = NULL;
m_backgroundColorRed = 0.0f;
m_backgroundColorGreen = 0.0f;
m_backgroundColorBlue = 0.0f;
}
// FUNCTION: LEGO1 0x100a10e0
ViewportAppData::~ViewportAppData()
{
IDirect3DRMFrameArray* pChildFrames;
IDirect3DRMFrame* pChildFrame = NULL;
m_pLightFrame->GetChildren(&pChildFrames);
for (int i = 0; i < (int) pChildFrames->GetSize(); i++) {
pChildFrames->GetElement(i, &pChildFrame);
m_pLightFrame->DeleteChild(pChildFrame);
pChildFrame->Release(); // GetElement() does AddRef()
}
pChildFrames->Release();
m_pLightFrame->Release();
}
// Forward declare to satisfy order check
void ViewportDestroyCallback(IDirect3DRMObject* pObject, void* pArg);
// FUNCTION: LEGO1 0x100a1160
Result ViewImpl::ViewportCreateAppData(IDirect3DRM* pDevice, IDirect3DRMViewport* pView, IDirect3DRMFrame* pCamera)
{
ViewportAppData* data = new ViewportAppData(pDevice);
data->m_pCamera = pCamera;
Result result = ResultVal(pView->SetAppData(reinterpret_cast<unsigned long>(data)));
if (Succeeded(result)) {
result = ResultVal(pView->AddDestroyCallback(ViewportDestroyCallback, data));
}
if (!Succeeded(result)) {
delete data;
pView->SetAppData(0);
}
return result;
}
inline Result ViewRestoreFrameAfterRender(
IDirect3DRMFrame* pFrame,
IDirect3DRMFrame* pCamera,
IDirect3DRMFrame* pLightFrame
)
{
Result result = Success;
if (pFrame) {
// remove camera and light frame from frame that was rendered
// this doesn't destroy the camera as it is still the camera of the viewport...
result = ResultVal(pFrame->DeleteChild(pCamera));
result = ResultVal(pFrame->DeleteChild(pLightFrame));
// decrease frame's ref count (it was increased in ViewPrepareFrameForRender())
pFrame->Release();
}
return result;
}
// FUNCTION: LEGO1 0x100a1240
void ViewportDestroyCallback(IDirect3DRMObject* pObject, void* pArg)
{
ViewportAppData* pViewportAppData = reinterpret_cast<ViewportAppData*>(pArg);
ViewRestoreFrameAfterRender(
pViewportAppData->m_pLastRenderedFrame,
pViewportAppData->m_pCamera,
pViewportAppData->m_pLightFrame
);
delete pViewportAppData;
}
// FUNCTION: LEGO1 0x100a1290
Result ViewportPickImpl(
IDirect3DRMViewport* pViewport,
int x,
int y,
const Group** ppGroupsToPickFrom,
int groupsToPickFromCount,
const Group**& rppPickedGroups,
int& rPickedGroupCount
)
{
// Left unimplemented in shipped game.
return Error;
}
inline ViewportAppData* ViewportGetData(IDirect3DRMViewport* pViewport)
{
return reinterpret_cast<ViewportAppData*>(pViewport->GetAppData());
}
inline IDirect3DRMFrame* ViewportGetLightFrame(IDirect3DRMViewport* pViewport)
{
return ViewportGetData(pViewport)->m_pLightFrame;
}
// SYNTHETIC: LEGO1 0x100a23a0
// TglImpl::ViewImpl::`scalar deleting destructor'
// FUNCTION: LEGO1 0x100a2d80
void* ViewImpl::ImplementationDataPtr()
{
return reinterpret_cast<void*>(&m_data);
}
// FUNCTION: LEGO1 0x100a2d90
Result ViewImpl::Add(const Light* pLight)
{
const LightImpl* light = static_cast<const LightImpl*>(pLight);
IDirect3DRMFrame* frame = light->ImplementationData();
return ResultVal(ViewportGetLightFrame(m_data)->AddChild(frame));
}
// FUNCTION: LEGO1 0x100a2dc0
Result ViewImpl::Remove(const Light* pLight)
{
const LightImpl* light = static_cast<const LightImpl*>(pLight);
IDirect3DRMFrame* frame = light->ImplementationData();
return ResultVal(ViewportGetLightFrame(m_data)->DeleteChild(frame));
}
// FUNCTION: LEGO1 0x100a2df0
Result ViewImpl::SetCamera(const Camera* pCamera)
{
const CameraImpl* camera = static_cast<const CameraImpl*>(pCamera);
IDirect3DRMFrame* frame = camera->ImplementationData();
ViewportAppData* pViewportAppData;
Result result;
pViewportAppData = reinterpret_cast<ViewportAppData*>(m_data->GetAppData());
result = ViewRestoreFrameAfterRender(
pViewportAppData->m_pLastRenderedFrame,
pViewportAppData->m_pCamera,
pViewportAppData->m_pLightFrame
);
pViewportAppData->m_pCamera = frame;
pViewportAppData->m_pLastRenderedFrame = 0;
return ResultVal(m_data->SetCamera(frame));
}
// FUNCTION: LEGO1 0x100a2e70
Result ViewImpl::SetProjection(ProjectionType type)
{
return ResultVal(m_data->SetProjection(Translate(type)));
}
// FUNCTION: LEGO1 0x100a2eb0
Result ViewImpl::SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees)
{
float field = frontClippingDistance * tan(DegreesToRadians(degrees / 2));
Result result;
result = ResultVal(m_data->SetFront(frontClippingDistance));
if (Succeeded(result)) {
result = ResultVal(m_data->SetBack(backClippingDistance));
}
if (Succeeded(result)) {
result = ResultVal(m_data->SetField(field));
}
return result;
}
// FUNCTION: LEGO1 0x100a2f30
Result ViewImpl::SetBackgroundColor(float r, float g, float b)
{
Result ret = Success;
// Note, this method in the shipped game is very diverged from
// the Tgl leak code.
ViewportAppData* data = ViewportGetData(m_data);
data->m_backgroundColorRed = r;
data->m_backgroundColorGreen = g;
data->m_backgroundColorBlue = b;
if (data->m_pLastRenderedFrame) {
ret = ResultVal(data->m_pLastRenderedFrame->SetSceneBackgroundRGB(r, g, b));
}
return ret;
}
// FUNCTION: LEGO1 0x100a2f80
Result ViewImpl::GetBackgroundColor(float* r, float* g, float* b)
{
ViewportAppData* data = ViewportGetData(m_data);
*r = data->m_backgroundColorRed;
*g = data->m_backgroundColorGreen;
*b = data->m_backgroundColorBlue;
return Success;
}
// FUNCTION: LEGO1 0x100a2fb0
Result ViewImpl::Clear()
{
return ResultVal(m_data->Clear());
}
inline Result ViewPrepareFrameForRender(
IDirect3DRMFrame* pFrame,
IDirect3DRMFrame* pCamera,
IDirect3DRMFrame* pLightFrame,
float backgroundRed,
float backgroundGreen,
float backgroundBlue
)
{
Result result = Success;
if (pFrame) {
// set background color
result = ResultVal(pFrame->SetSceneBackgroundRGB(backgroundRed, backgroundGreen, backgroundBlue));
// add camera to frame to be rendered
result = ResultVal(pFrame->AddChild(pCamera));
// add light frame to frame to be rendered
result = ResultVal(pFrame->AddChild(pLightFrame));
// increase ref count of frame to ensure it does not get deleted underneath us
pFrame->AddRef();
}
return result;
}
// FUNCTION: LEGO1 0x100a2fd0
Result ViewImpl::Render(const Light* pCamera)
{
ViewportAppData* appdata = ViewportGetData(m_data);
IDirect3DRMFrame* light = static_cast<const LightImpl*>(pCamera)->ImplementationData();
IDirect3DRMFrame* lastRendered = appdata->m_pLastRenderedFrame;
if (light != lastRendered) {
if (lastRendered) {
lastRendered->DeleteChild(appdata->m_pCamera);
// Some other call goes here, not sure what.
lastRendered->Release();
}
appdata->m_pLastRenderedFrame = light;
if (light) {
light->SetSceneBackgroundRGB(
appdata->m_backgroundColorRed,
appdata->m_backgroundColorGreen,
appdata->m_backgroundColorBlue
);
light->AddChild(appdata->m_pCamera);
// Some other call goes here, not sure what.
light->AddRef();
}
}
return ResultVal(m_data->Render(light));
}
// FUNCTION: LEGO1 0x100a3080
Result ViewImpl::ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height)
{
return ResultVal(m_data->ForceUpdate(x, y, x + width - 1, y + height - 1));
}
// FUNCTION: LEGO1 0x100a30c0
Result ViewImpl::Pick(
unsigned long x,
unsigned long y,
const Group** ppGroupsToPickFrom,
int groupsToPickFromCount,
const Group**& rppPickedGroups,
int& rPickedGroupCount
)
{
return ViewportPickImpl(
m_data,
x,
y,
ppGroupsToPickFrom,
groupsToPickFromCount,
rppPickedGroups,
rPickedGroupCount
);
}
// FUNCTION: LEGO1 0x100a30f0
Result ViewImpl::TransformWorldToScreen(const float world[3], float screen[4])
{
D3DRMVECTOR4D d3dRMScreen;
D3DVECTOR d3dRMWorld;
d3dRMWorld.x = world[0];
d3dRMWorld.y = world[1];
d3dRMWorld.z = world[2];
Result result;
result = ResultVal(m_data->Transform(&d3dRMScreen, &d3dRMWorld));
if (Succeeded(result)) {
screen[0] = d3dRMScreen.x;
screen[1] = d3dRMScreen.y;
screen[2] = d3dRMScreen.z;
screen[3] = d3dRMScreen.w;
}
return result;
}
// FUNCTION: LEGO1 0x100a3160
Result ViewImpl::TransformScreenToWorld(const float screen[4], float world[3])
{
// 100% match minus instruction reordering.
D3DVECTOR d3dRMWorld;
D3DRMVECTOR4D d3dScreen;
d3dScreen.x = screen[0];
d3dScreen.y = screen[1];
d3dScreen.z = screen[2];
d3dScreen.w = screen[3];
Result result;
result = ResultVal(m_data->InverseTransform(&d3dRMWorld, &d3dScreen));
if (Succeeded(result)) {
world[0] = d3dRMWorld.x;
world[1] = d3dRMWorld.y;
world[2] = d3dRMWorld.z;
}
return result;
}

View file

@ -1,27 +1,22 @@
#ifndef TGL_H
#define TGL_H
#ifdef _WIN32
#ifndef _tgl_h
#define _tgl_h
#include "tglvector.h"
#define NOMINMAX // to avoid conflict with STL
#include <d3d.h>
#include <ddraw.h>
#include <windows.h> // HWND
#endif /* _WIN32 */
#include "tglVector.h"
#include <windows.h>
namespace Tgl
{
// ???
enum ColorModel {
// Note: Not used in shipped game, no way to verify contents.
Ramp,
RGB
};
// ???
enum ShadingModel {
Wireframe,
UnlitFlat,
@ -30,7 +25,6 @@ enum ShadingModel {
Phong
};
// ?????
enum LightType {
Ambient,
Point,
@ -39,7 +33,6 @@ enum LightType {
ParallelPoint
};
// ???
enum ProjectionType {
Perspective,
Orthographic
@ -50,39 +43,39 @@ enum TextureMappingMode {
PerspectiveCorrect
};
// Not in the Tgl leak, inferred from the assembly
enum MaterialMode {
FromParent,
FromFrame,
FromMesh,
};
struct PaletteEntry {
unsigned char m_red;
unsigned char m_green;
unsigned char m_blue;
};
#ifdef _WIN32
struct DeviceDirectDrawCreateData {
const GUID* m_driverGUID;
HWND m_hWnd; // ??? derive from m_pDirectDraw
IDirectDraw* m_pDirectDraw;
IDirectDrawSurface* m_pFrontBuffer; // ??? derive from m_pDirectDraw
IDirectDrawSurface* m_pBackBuffer;
IDirectDrawPalette* m_pPalette; // ??? derive from m_pDirectDraw
int m_isFullScreen; // ??? derive from m_pDirectDraw
};
struct DeviceDirect3DCreateData {
IDirect3D* m_pDirect3D;
IDirect3DDevice* m_pDirect3DDevice;
};
#else
struct DeviceDirectDrawCreateData {
const GUID* m_driverGUID;
HWND m_hWnd;
IDirectDraw* m_pDirectDraw;
IDirectDrawSurface* m_pFrontBuffer;
IDirectDrawSurface* m_pBackBuffer;
struct DeviceDirectDrawCreateData {};
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Result (return value type)
// These have possibly been removed in the shipped game
// (Put them back if we can verify when we find a callsite
// which constructs this type)
// IDirectDrawPalette* m_pPalette;
// int m_isFullScreen;
};
// Result type used for all methods in the Tgl API
enum Result {
Error = 0,
Success = 1
@ -93,10 +86,7 @@ inline int Succeeded(Result result)
return (result == Success);
}
//////////////////////////////////////////////////////////////////////////////
//
// Forward declarations
class Renderer;
class Object;
class Device;
@ -106,30 +96,24 @@ class Camera;
class Group;
class Mesh;
class Texture;
class Unk;
//////////////////////////////////////////////////////////////////////////////
//
// Object
// VTABLE 0x100db980
class Object {
public:
virtual ~Object() {}
// returns pointer to implementation data
virtual void* ImplementationDataPtr() = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Renderer
// ??? for now until we figured out how an app should pass the Renderer around
Renderer* CreateRenderer();
// VTABLE 0x100db948
class Renderer : public Object {
public:
virtual Device* CreateDevice(const DeviceDirectDrawCreateData&) = 0;
// vtable+0x08
virtual Device* CreateDevice(const DeviceDirect3DCreateData&) = 0;
virtual Device* CreateDevice(const DeviceDirectDrawCreateData&) = 0;
// vtable+0x10
virtual View* CreateView(
const Device*,
const Camera*,
@ -139,34 +123,12 @@ class Renderer : public Object {
unsigned long height
) = 0;
virtual Camera* CreateCamera() = 0;
virtual Light* CreateLight(LightType, double r, double g, double b) = 0;
virtual Light* CreateLight(LightType, float r, float g, float b) = 0;
virtual Group* CreateGroup(const Group* pParent = 0) = 0;
// pTextureCoordinates is pointer to array of vertexCount elements
// (each element being two floats), or NULL
// pFaceData is faceCount tuples, each of format
// [vertex1index, ... vertexNindex], where N = vertexPerFaceCount
virtual Mesh* CreateMesh(
unsigned long vertexCount,
const float (*pVertices)[3],
const float (*pTextureCoordinates)[2],
unsigned long faceCount,
unsigned long vertexPerFaceCount,
unsigned long* pFaceData
) = 0;
// pTextureCoordinates is pointer to array of vertexCount elements
// (each element being two floats), or NULL
// pFaceData is:
// [face1VertexCount face1Vertex1index, ... face1VertexMindex
// face2VertexCount face2Vertex1index, ... face2VertexNindex
// ...
// 0]
virtual Mesh* CreateMesh(
unsigned long vertexCount,
const float (*pVertices)[3],
const float (*pTextureCoordinates)[2],
unsigned long* pFaceData
) = 0;
// vtable+0x20
virtual Unk* CreateUnk() = 0;
virtual Texture* CreateTexture() = 0;
virtual Texture* CreateTexture(
int width,
int height,
@ -176,61 +138,54 @@ class Renderer : public Object {
int paletteEntryCount,
const PaletteEntry* pEntries
) = 0;
virtual Texture* CreateTexture() = 0;
virtual Result SetTextureDefaultShadeCount(unsigned long) = 0;
// vtable+0x30
virtual Result SetTextureDefaultColorCount(unsigned long) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Device
Renderer* CreateRenderer();
// VTABLE 0x100db9b8
class Device : public Object {
public:
// vtable+0x08
virtual unsigned long GetWidth() = 0;
virtual unsigned long GetHeight() = 0;
// vtable+0x10
virtual Result SetColorModel(ColorModel) = 0;
virtual Result SetShadingModel(ShadingModel) = 0;
virtual Result SetShadeCount(unsigned long) = 0;
virtual Result SetDither(int) = 0;
// vtable+0x20
virtual Result Update() = 0;
// ??? should this be handled by app ???
// ??? this needs to be called when the window on which the device is ...
// is being activated
virtual void HandleActivate(int bActivate) = 0;
// ??? this needs to be called when the window on which this device is based
// needs to be repainted
virtual void HandlePaint(void*) = 0;
#ifdef _DEBUG
virtual unsigned long GetDrawnTriangleCount() = 0;
#endif
virtual void InitFromD3DDevice(Device*) = 0;
virtual void InitFromWindowsDevice(Device*) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// View
// VTABLE 0x100dba28
class View : public Object {
public:
virtual Result Add(const Light*) = 0;
virtual Result Remove(const Light*) = 0;
// vtable+0x10
virtual Result SetCamera(const Camera*) = 0;
virtual Result SetProjection(ProjectionType) = 0;
virtual Result SetFrustrum(double frontClippingDistance, double backClippingDistance, double degrees) = 0;
virtual Result SetBackgroundColor(double r, double g, double b) = 0;
virtual Result SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees) = 0;
virtual Result SetBackgroundColor(float r, float g, float b) = 0;
// vtable+0x20
virtual Result GetBackgroundColor(float* r, float* g, float* b) = 0;
virtual Result Clear() = 0;
virtual Result Render(const Group*) = 0;
// ??? needed for fine grain control when using DirectDraw/D3D ???
virtual Result Render(const Light*) = 0;
virtual Result ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height) = 0;
// ??? for now: used by Mesh Cost calculation
virtual Result TransformWorldToScreen(const double world[3], double screen[4]) = 0;
// vtable+0x30
virtual Result TransformWorldToScreen(const float world[3], float screen[4]) = 0;
virtual Result TransformScreenToWorld(const float screen[4], float world[3]) = 0;
// Pick():
// x, y:
@ -264,102 +219,94 @@ class View : public Object {
) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Camera
// VTABLE 0x100dbae8
class Camera : public Object {
public:
#if 0
virtual Result SetPosition(const double[3]) = 0;
virtual Result SetOrientation(const double direction[3],
const double up[3]) = 0;
#endif
virtual Result SetTransformation(const FloatMatrix4&) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Light
// VTABLE 0x100dbb08
class Light : public Object {
public:
#if 0
virtual Result SetPosition(const double[3]) = 0;
virtual Result SetOrientation(const double direction[3],
const double up[3]) = 0;
#endif
virtual Result SetTransformation(const FloatMatrix4&) = 0;
virtual Result SetColor(float r, float g, float b) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Group
class Group : public Object {
public:
#if 0
virtual Result SetPosition(const double[3]) = 0;
virtual Result SetOrientation(const double direction[3],
const double up[3]) = 0;
#endif
// TODO: The type was changed from `FloatMatrix` to `Matrix` to make code in UpdateWorldData match.
// However, this is unlikely to be correct and will have to be figured out at some point.
virtual Result SetTransformation(const Matrix4&) = 0;
// ??? not yet fully implemented
virtual Result SetColor(double r, double g, double b) = 0;
virtual Result SetTexture(const Texture*) = 0;
virtual Result Add(const Group*) = 0;
virtual Result Add(const Mesh*) = 0;
virtual Result Remove(const Group*) = 0;
virtual Result Remove(const Mesh*) = 0;
virtual Result RemoveAll() = 0;
// ??? for now: used by Mesh Cost calculation
virtual Result TransformLocalToWorld(const double local[3], double world[3]) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Mesh
// VTABLE 0x100dbbb0
class Mesh : public Object {
public:
// ??? also on Group
virtual Result SetColor(double r, double g, double b) = 0;
virtual Result SetColor(float r, float g, float b, float a) = 0;
virtual Result SetTexture(const Texture*) = 0;
virtual Result SetTextureMappingMode(TextureMappingMode) = 0;
virtual Result GetTexture(Texture*&) = 0;
virtual Result SetTextureMappingMode(ProjectionType) = 0;
virtual Result SetShadingModel(ShadingModel) = 0;
#ifdef _DEBUG
virtual Result GetBoundingBox(float min[3], float max[3]) = 0;
virtual unsigned long GetFaceCount() = 0;
virtual unsigned long GetVertexCount() = 0;
#endif
// Clone data in underlying group
virtual Mesh* DeepClone(Unk*) = 0;
// Just get another Group pointing to the same underlying data
virtual Mesh* ShallowClone(Unk*) = 0;
};
//////////////////////////////////////////////////////////////////////////////
//
// Texture
// VTABLE 0x100dbaa0
class Group : public Object {
public:
virtual Result SetTransformation(const FloatMatrix4&) = 0;
virtual Result SetColor(float r, float g, float b, float a) = 0;
virtual Result SetTexture(const Texture*) = 0;
virtual Result GetTexture(Texture*&) = 0;
virtual Result SetMaterialMode(MaterialMode) = 0;
virtual Result Add(const Group*) = 0;
virtual Result Add(const Mesh*) = 0;
virtual Result Remove(const Group*) = 0;
virtual Result Remove(const Mesh*) = 0;
virtual Result RemoveAll() = 0;
// This is TransformLocalToWorld in the leak, however it seems
// to have been replaced by something else in the shipped code.
virtual Result Unknown() = 0;
};
// Don't know what this is. Seems like another Tgl object which
// was not in the leaked Tgl code. My suspicion is that it's
// some kind of builder class for creating meshes.
// VTABLE 0x100dbb30
class Unk : public Object {
public:
virtual Result SetMeshData(
unsigned long faceCount,
unsigned long vertexCount,
const float (*pPositions)[3],
const float (*pNormals)[3],
const float (*pTextureCoordinates)[2],
unsigned long vertexPerFaceCount,
unsigned long* pFaceData
) = 0;
virtual Result GetBoundingBox(float min[3], float max[3]) = 0;
virtual Unk* Clone() = 0;
};
// VTABLE 0x100dbb68
class Texture : public Object {
public:
virtual Result SetTexels(
int width,
int height,
int bitsPerTexel,
const void* pTexels,
int pTexelsArePersistent
) = 0;
virtual Result SetPalette(int entryCount, const PaletteEntry* pEntries) = 0;
};
// vtable+0x08
virtual Result SetTexels(int width, int height, int bitsPerTexel, void* pTexels) = 0;
virtual void FillRowsOfTexture(int y, int height, void* pBuffer) = 0;
//////////////////////////////////////////////////////////////////////////////
// vtable+0x10
virtual Result Changed(int texelsChanged, int paletteChanged) = 0;
virtual Result GetBufferAndPalette(
int* pWidth,
int* pHeight,
int* pDepth,
void** ppBuffer,
int* pPaletteSize,
PaletteEntry** ppPalette
) = 0;
virtual Result SetPalette(int entryCount, PaletteEntry* pEntries) = 0;
};
} // namespace Tgl
#endif // TGL_H
#endif /* _tgl_h */

View file

@ -1,7 +1,10 @@
#ifndef TGLVECTOR_H
#define TGLVECTOR_H
#ifndef _tglVector_h
#define _tglVector_h
// Note: This file is almost an exact copy of the one from
// the leak but using floats instead of doubles, hence the
// strange formatting in some places.
#include "math.h" // ??? sin() in RotateAroundY()
#include "math.h" // sin() in RotateAroundY()
#include <stddef.h> // offsetof()
@ -142,7 +145,6 @@ class RotationY : public FloatMatrix4 {
public:
RotationY(float radians);
};
//////////////////////////////////////////////////////////////////////////////
//
// Transformation matrices implementation
@ -274,4 +276,4 @@ inline RotationY::RotationY(float radians)
} // namespace Tgl
#endif // TLGVECTOR_H
#endif /* _tglVector_h */

View file

@ -27,8 +27,7 @@ void ViewROI::UpdateWorldData(const Matrix4Data& parent2world)
{
OrientableROI::UpdateWorldData(parent2world);
if (geometry) {
// Tgl::FloatMatrix4 tgl_mat;
Matrix4 mat;
Tgl::FloatMatrix4 mat;
SETMAT4(mat, m_local2world.GetMatrix());
Tgl::Result result = geometry->SetTransformation(mat);
// assert(Tgl::Succeeded(result));