[Scummvm-git-logs] scummvm master -> fbdd7a3bfcbe64d4aff3be85e10d6d7718e6694d

aquadran noreply at scummvm.org
Wed Nov 6 14:18:18 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
fbdd7a3bfc WINTERMUTE: Restore effects material code from original. Added stub for effects in renderer.


Commit: fbdd7a3bfcbe64d4aff3be85e10d6d7718e6694d
    https://github.com/scummvm/scummvm/commit/fbdd7a3bfcbe64d4aff3be85e10d6d7718e6694d
Author: Paweł Kołodziejski (aquadran at gmail.com)
Date: 2024-11-06T15:18:13+01:00

Commit Message:
WINTERMUTE: Restore effects material code from original. Added stub for effects in renderer.

Changed paths:
  A engines/wintermute/base/gfx/3deffect.cpp
  A engines/wintermute/base/gfx/3deffect.h
  A engines/wintermute/base/gfx/3deffect_params.cpp
  A engines/wintermute/base/gfx/3deffect_params.h
    engines/wintermute/ad/ad_actor_3dx.cpp
    engines/wintermute/base/base_persistence_manager.cpp
    engines/wintermute/base/base_persistence_manager.h
    engines/wintermute/base/gfx/opengl/base_render_opengl3d.h
    engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h
    engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
    engines/wintermute/base/gfx/opengl/meshx_opengl.h
    engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
    engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
    engines/wintermute/base/gfx/xframe_node.cpp
    engines/wintermute/base/gfx/xframe_node.h
    engines/wintermute/base/gfx/xmaterial.cpp
    engines/wintermute/base/gfx/xmaterial.h
    engines/wintermute/base/gfx/xmesh.cpp
    engines/wintermute/base/gfx/xmesh.h
    engines/wintermute/base/gfx/xmodel.cpp
    engines/wintermute/base/gfx/xmodel.h
    engines/wintermute/module.mk


diff --git a/engines/wintermute/ad/ad_actor_3dx.cpp b/engines/wintermute/ad/ad_actor_3dx.cpp
index 2f9f1e4dadb..e79eace568c 100644
--- a/engines/wintermute/ad/ad_actor_3dx.cpp
+++ b/engines/wintermute/ad/ad_actor_3dx.cpp
@@ -48,6 +48,7 @@
 #include "engines/wintermute/base/gfx/3dshadow_volume.h"
 #include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
 #include "engines/wintermute/base/gfx/xmodel.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
 #include "engines/wintermute/base/gfx/xmath.h"
 #include "engines/wintermute/base/gfx/3dutils.h"
 #include "engines/wintermute/base/particles/part_emitter.h"
@@ -1831,12 +1832,10 @@ bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta
 	//////////////////////////////////////////////////////////////////////////
 	else if (strcmp(name, "SetEffect") == 0) {
 		stack->correctParams(2);
-		/*const char *materialName =*/ stack->pop()->getString();
-		/*const char *effectFilename =*/ stack->pop()->getString();
+		const char *materialName = stack->pop()->getString();
+		const char *effectFilename = stack->pop()->getString();
 
-		warning("AdActor3DX::scCallMethod D3DX effects are not supported");
-		//if (_xmodel && _xmodel->setMaterialEffect(materialName, effectFilename)) {
-		if (_xmodel) {
+		if (_xmodel && _xmodel->setMaterialEffect(materialName, effectFilename)) {
 			stack->pushBool(true);
 		} else {
 			stack->pushBool(false);
@@ -1849,12 +1848,10 @@ bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta
 	//////////////////////////////////////////////////////////////////////////
 	else if (strcmp(name, "RemoveEffect") == 0) {
 		stack->correctParams(1);
-		/*const char *materialName =*/ stack->pop()->getString();
+		const char *materialName = stack->pop()->getString();
 		stack->pop();
 
-		warning("AdActor3DX::scCallMethod D3DX effects are not supported");
-		// if (_xmodel && _xodel->removeMaterialEffect(materialName)) {
-		if (_xmodel) {
+		if (_xmodel && _xmodel->removeMaterialEffect(materialName)) {
 			stack->pushBool(true);
 		} else {
 			stack->pushBool(false);
@@ -1867,13 +1864,11 @@ bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta
 	//////////////////////////////////////////////////////////////////////////
 	else if (strcmp(name, "SetEffectParam") == 0) {
 		stack->correctParams(3);
-		/*const char *materialName =*/ stack->pop()->getString();
-		/*const char *paramName =*/ stack->pop()->getString();
-		/*ScValue *val =*/ stack->pop();
+		const char *materialName = stack->pop()->getString();
+		const char *paramName = stack->pop()->getString();
+		ScValue *val = stack->pop();
 
-		warning("AdActor3DX::scCallMethod D3DX effects are not supported");
-		// if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, val)) {
-		if (_xmodel) {
+		if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, val)) {
 			stack->pushBool(true);
 		} else {
 			stack->pushBool(false);
@@ -1886,16 +1881,14 @@ bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta
 	//////////////////////////////////////////////////////////////////////////
 	else if (strcmp(name, "SetEffectParamVector") == 0) {
 		stack->correctParams(6);
-		/*const char *materialName =*/ stack->pop()->getString();
-		/*const char *paramName =*/ stack->pop()->getString();
-		/*float x =*/ stack->pop()->getFloat();
-		/*float y =*/ stack->pop()->getFloat();
-		/*float z =*/ stack->pop()->getFloat();
-		/*float w =*/ stack->pop()->getFloat();
-
-		//if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(x, y, z, w))) {
-		warning("AdActor3DX::scCallMethod D3DX effects are not supported");
-		if (_xmodel) {
+		const char *materialName = stack->pop()->getString();
+		const char *paramName = stack->pop()->getString();
+		float x = stack->pop()->getFloat();
+		float y = stack->pop()->getFloat();
+		float z = stack->pop()->getFloat();
+		float w = stack->pop()->getFloat();
+
+		if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(x, y, z, w))) {
 			stack->pushBool(true);
 		} else {
 			stack->pushBool(false);
@@ -1908,18 +1901,16 @@ bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta
 	//////////////////////////////////////////////////////////////////////////
 	else if (strcmp(name, "SetEffectParamColor") == 0) {
 		stack->correctParams(3);
-		/*const char *materialName =*/ stack->pop()->getString();
-		/*const char *paramName =*/ stack->pop()->getString();
-		/*uint32 color =*/ stack->pop()->getInt();
+		const char *materialName = stack->pop()->getString();
+		const char *paramName = stack->pop()->getString();
+		uint32 color = stack->pop()->getInt();
 
-		//float r = RGBCOLGetR(color) / 255.0f;
-		//float g = RGBCOLGetG(color) / 255.0f;
-		//float b = RGBCOLGetB(color) / 255.0f;
-		//float a = RGBCOLGetA(color) / 255.0f;
+		float r = RGBCOLGetR(color) / 255.0f;
+		float g = RGBCOLGetG(color) / 255.0f;
+		float b = RGBCOLGetB(color) / 255.0f;
+		float a = RGBCOLGetA(color) / 255.0f;
 
-		//if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(r, g, b, a))) {
-		warning("AdActor3DX::scCallMethod D3DX effects are not supported");
-		if (_xmodel) {
+		if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(r, g, b, a))) {
 			stack->pushBool(true);
 		} else {
 			stack->pushBool(false);
@@ -2478,8 +2469,6 @@ bool AdActor3DX::updatePartEmitter() {
 
 //////////////////////////////////////////////////////////////////////////
 bool AdActor3DX::parseEffect(byte *buffer) {
-	warning("AdActor3DX::parseEffect D3DX effect are not implemented");
-
 	TOKEN_TABLE_START(commands)
 		TOKEN_TABLE(MATERIAL)
 		TOKEN_TABLE(EFFECT_FILE)
@@ -2509,7 +2498,9 @@ bool AdActor3DX::parseEffect(byte *buffer) {
 	}
 
 	if (effectFile && material) {
-		// TODO: Implement
+		if (!_xmodel->setMaterialEffect(material, effectFile)) {
+			_gameRef->LOG(0, "Error assigning effect to material '%s'", material);
+		}
 	}
 
 	delete[] effectFile;
diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp
index 7ab5c206c4f..b6a441cf00a 100644
--- a/engines/wintermute/base/base_persistence_manager.cpp
+++ b/engines/wintermute/base/base_persistence_manager.cpp
@@ -850,6 +850,34 @@ bool BasePersistenceManager::transferVector3d(const char *name, DXVector3 *val)
 	}
 }
 
+//////////////////////////////////////////////////////////////////////////
+// Vector4
+bool BasePersistenceManager::transferVector4d(const char *name, DXVector4 *val) {
+	if (_saving) {
+		putFloat(val->_x);
+		putFloat(val->_y);
+		putFloat(val->_z);
+		putFloat(val->_w);
+
+		if (_saveStream->err()) {
+			return STATUS_FAILED;
+		}
+
+		return STATUS_OK;
+	} else {
+		val->_x = getFloat();
+		val->_y = getFloat();
+		val->_z = getFloat();
+		val->_w = getFloat();
+
+		if (_loadStream->err()) {
+			return STATUS_FAILED;
+		}
+
+		return STATUS_OK;
+	}
+}
+
 //////////////////////////////////////////////////////////////////////////
 // Matrix4
 bool BasePersistenceManager::transferMatrix4(const char *name, DXMatrix *val) {
diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h
index 6eb9b0a35e0..abea8bad3d0 100644
--- a/engines/wintermute/base/base_persistence_manager.h
+++ b/engines/wintermute/base/base_persistence_manager.h
@@ -100,6 +100,7 @@ public:
 	bool transferVector2(const char *name, Vector2 *val);
 #ifdef ENABLE_WME3D
 	bool transferVector3d(const char *name, DXVector3 *val);
+	bool transferVector4d(const char *name, DXVector4 *val);
 	bool transferMatrix4(const char *name, DXMatrix *val);
 	bool transferAngle(const char *name, float *val);
 #endif
diff --git a/engines/wintermute/base/gfx/3deffect.cpp b/engines/wintermute/base/gfx/3deffect.cpp
new file mode 100644
index 00000000000..6d1e303230e
--- /dev/null
+++ b/engines/wintermute/base/gfx/3deffect.cpp
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#include "common/crc.h"
+
+#include "engines/wintermute/base/base_named_object.h"
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
+#include "engines/wintermute/utils/utils.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+Effect3D::Effect3D(BaseGame *inGame) : BaseClass(inGame) {
+	_effectHash = 0xFFFFFFFF;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Effect3D::~Effect3D() {
+	_effectHash = 0xFFFFFFFF;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Effect3D::invalidateDeviceObjects() {
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Effect3D::restoreDeviceObjects() {
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Effect3D::createFromFile(const Common::String &filename) {
+	uint32 size;
+	byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size);
+	if (!buffer) {
+		return false;
+	}
+
+	_filename = filename;
+
+	Common::CRC32 crc;
+	_effectHash = crc.crcFast(buffer, size);
+
+	delete[] buffer;
+
+	return true;
+}
+
+} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/3deffect.h b/engines/wintermute/base/gfx/3deffect.h
new file mode 100644
index 00000000000..abb68c44b08
--- /dev/null
+++ b/engines/wintermute/base/gfx/3deffect.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#ifndef WINTERMUTE_3D_EFFECT_H
+#define WINTERMUTE_3D_EFFECT_H
+
+#include "engines/wintermute/base/base_named_object.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+class Effect3D : public BaseClass {
+public:
+	Effect3D(BaseGame *inGame);
+	~Effect3D();
+
+	bool createFromFile(const Common::String &filename);
+	uint32 getEffectHash() { return _effectHash; }
+	bool invalidateDeviceObjects();
+	bool restoreDeviceObjects();
+	const char *getFileName() { return _filename.c_str(); }
+
+private:
+	Common::String _filename;
+	uint32 _effectHash;
+};
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/3deffect_params.cpp b/engines/wintermute/base/gfx/3deffect_params.cpp
new file mode 100644
index 00000000000..397a8d8ddbb
--- /dev/null
+++ b/engines/wintermute/base/gfx/3deffect_params.cpp
@@ -0,0 +1,200 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#include "common/crc.h"
+
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/gfx/3deffect_params.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::Effect3DParam::Effect3DParam() {
+	setDefaultValues();
+}
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::Effect3DParam::Effect3DParam(const char *paramName) {
+	setDefaultValues();
+	_paramName = paramName;
+}
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::Effect3DParam::~Effect3DParam() {
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setValue(char *val) {
+	_type = EP_STRING;
+	_valString = val;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setValue(int val) {
+	_type = EP_INT;
+	_valInt = val;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setValue(float val) {
+	_type = EP_FLOAT;
+	_valFloat = val;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setValue(bool val) {
+	_type = EP_BOOL;
+	_valBool = val;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setValue(DXVector4 val) {
+	_type = EP_VECTOR;
+	_valVector = val;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::Effect3DParam::setDefaultValues() {
+	_paramName = "";
+	_valString = "";
+	_valInt = 0;
+	_valFloat = 0;
+	_valBool = 0;
+	_valVector = DXVector4(0, 0, 0, 0);
+
+	_type = EP_UNKNOWN;
+
+	_initialized = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Effect3DParams::Effect3DParam::persist(BasePersistenceManager *persistMgr) {
+	persistMgr->transferString(TMEMBER(_paramName));
+	persistMgr->transferSint32(TMEMBER_INT(_type));
+	persistMgr->transferString(TMEMBER(_valString));
+	persistMgr->transferSint32(TMEMBER(_valInt));
+	persistMgr->transferFloat(TMEMBER(_valFloat));
+	persistMgr->transferVector4d(TMEMBER(_valVector));
+	persistMgr->transferBool(TMEMBER(_valBool));
+
+	if (!persistMgr->getIsSaving()) {
+		_initialized = false;
+	}
+
+	return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::Effect3DParams() {
+}
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::~Effect3DParams() {
+	clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::clear() {
+	for (size_t i = 0; i < _params.size(); i++) {
+		delete _params[i];
+		_params[i] = nullptr;
+	}
+
+	_params.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Effect3DParams::persist(BasePersistenceManager *persistMgr) {
+	if (persistMgr->getIsSaving()) {
+		uint32 numItems = _params.size();
+		persistMgr->transferUint32(TMEMBER(numItems));
+
+		for (uint32 i = 0; i < numItems; i++) {
+			_params[i]->persist(persistMgr);
+		}
+	} else {
+		uint32 numItems = 0;
+		persistMgr->transferUint32(TMEMBER(numItems));
+
+		for (uint32 i = 0; i < numItems; i++) {
+			Effect3DParam *param = new Effect3DParam();
+			param->persist(persistMgr);
+
+			_params.add(param);
+		}
+	}
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::setParam(const char *paramName, ScValue *val) {
+	Effect3DParam *param = getParamByName(paramName);
+
+	switch (val->getType()) {
+	case VAL_INT:
+		param->setValue(val->getInt());
+	break;
+	case VAL_FLOAT:
+		param->setValue((float)val->getFloat());
+	break;
+	case VAL_BOOL:
+		param->setValue(val->getBool());
+	break;
+	default:
+		param->setValue(val->getString());
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Effect3DParams::setParam(const char *paramName, DXVector4 val) {
+	Effect3DParam *param = getParamByName(paramName);
+	param->setValue(val);
+}
+
+//////////////////////////////////////////////////////////////////////////
+Effect3DParams::Effect3DParam *Effect3DParams::getParamByName(const char *paramName) {
+	Effect3DParam *param = nullptr;
+
+	for (uint32 i = 0; i < _params.size(); i++) {
+		if (_params[i]->getParamName() && strcmp(paramName, _params[i]->getParamName()) == 0) {
+			param = _params[i];
+			break;
+		}
+	}
+
+	if (!param) {
+		param = new Effect3DParam(paramName);
+		_params.add(param);
+	}
+
+	return param;
+}
+
+} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/3deffect_params.h b/engines/wintermute/base/gfx/3deffect_params.h
new file mode 100644
index 00000000000..285529eaab1
--- /dev/null
+++ b/engines/wintermute/base/gfx/3deffect_params.h
@@ -0,0 +1,96 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#ifndef WINTERMUTE_3D_EFFECT_PARAMS_H
+#define WINTERMUTE_3D_EFFECT_PARAMS_H
+
+#include "engines/wintermute/base/base_game.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+class Effect3DParams {
+public:
+
+	//////////////////////////////////////////////////////////////////////////
+	class Effect3DParam {
+	public:
+
+		enum ParamType {
+			EP_UNKNOWN,
+			EP_STRING,
+			EP_FLOAT,
+			EP_INT,
+			EP_BOOL,
+			EP_VECTOR
+		};
+
+		Effect3DParam();
+		Effect3DParam(const char *paramName);
+		~Effect3DParam();
+
+		void setValue(char *val);
+		void setValue(int val);
+		void setValue(float val);
+		void setValue(bool val);
+		void setValue(DXVector4 val);
+
+		const char *getParamName() const { return _paramName.c_str(); }
+
+		bool persist(BasePersistenceManager *persistMgr);
+
+	private:
+		void setDefaultValues();
+		ParamType _type;
+		Common::String _paramName;
+		bool _initialized;
+
+		Common::String _valString;
+		int _valInt;
+		float _valFloat;
+		DXVector4 _valVector;
+		bool _valBool;
+	};
+
+
+	//////////////////////////////////////////////////////////////////////////
+	Effect3DParams();
+	~Effect3DParams();
+
+	bool persist(BasePersistenceManager *persistMgr);
+	void clear();
+	void setParam(const char *paramName, ScValue *val);
+	void setParam(const char *paramName, DXVector4 Val);
+
+private:
+	Effect3DParam *getParamByName(const char *paramName);
+	BaseArray<Effect3DParam *> _params;
+};
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/opengl/base_render_opengl3d.h b/engines/wintermute/base/gfx/opengl/base_render_opengl3d.h
index c9511b689cb..cefad6f67dd 100644
--- a/engines/wintermute/base/gfx/opengl/base_render_opengl3d.h
+++ b/engines/wintermute/base/gfx/opengl/base_render_opengl3d.h
@@ -41,6 +41,11 @@ namespace Wintermute {
 class BaseSurfaceOpenGL3D;
 
 class BaseRenderOpenGL3D : public BaseRenderer3D {
+	friend class BaseSurfaceOpenGL3D;
+	friend class Mesh3DSOpenGL;
+	friend class XMeshOpenGL;
+	friend class ShadowVolumeOpenGL;
+
 	struct SpriteVertex {
 		float x;
 		float y;
@@ -146,7 +151,7 @@ public:
 
 	bool setViewport3D(DXViewport *viewport) override;
 
-private:
+protected:
 	SimpleShadowVertex _simpleShadow[4]{};
 	Common::Array<DXVector4> _lightPositions;
 	Common::Array<DXVector3> _lightDirections;
diff --git a/engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h b/engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h
index c9cfde0c0ce..986cdaf5411 100644
--- a/engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h
+++ b/engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h
@@ -42,6 +42,11 @@ namespace Wintermute {
 class BaseSurfaceOpenGL3D;
 
 class BaseRenderOpenGL3DShader : public BaseRenderer3D {
+	friend class BaseSurfaceOpenGL3DShader;
+	friend class Mesh3DSOpenGLShader;
+	friend class XMeshOpenGLShader;
+	friend class ShadowVolumeOpenGLShader;
+
 	struct SpriteVertex {
 		float x;
 		float y;
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
index 2ee163dd8c4..097f193e20f 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl.cpp
@@ -26,6 +26,8 @@
  */
 
 #include "engines/wintermute/base/gfx/xmaterial.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
+#include "engines/wintermute/base/gfx/3deffect_params.h"
 #include "engines/wintermute/base/gfx/skin_mesh_helper.h"
 #include "engines/wintermute/base/base_game.h"
 #include "engines/wintermute/base/gfx/base_renderer3d.h"
@@ -35,6 +37,7 @@
 #if defined(USE_OPENGL_GAME)
 
 #include "engines/wintermute/base/gfx/opengl/base_surface_opengl3d.h"
+#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
 #include "engines/wintermute/base/gfx/opengl/meshx_opengl.h"
 
 namespace Wintermute {
@@ -96,23 +99,27 @@ bool XMeshOpenGL::render(XModel *model) {
 	}
 
 	for (uint32 i = 0; i < numAttrs; i++) {
-		int materialIndex = attrs[i]._attribId;
-		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, _materials[materialIndex]->_material._diffuse._data);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, _materials[materialIndex]->_material._diffuse._data);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, _materials[materialIndex]->_material._specular._data);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, _materials[materialIndex]->_material._emissive._data);
-		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, _materials[materialIndex]->_material._power);
-
+		Material *mat = _materials[attrs[i]._attribId];
 		bool textureEnable = false;
-		if (_materials[materialIndex]->getSurface()) {
+		if (mat->getSurface()) {
 			textureEnable = true;
 			glEnable(GL_TEXTURE_2D);
-			static_cast<BaseSurfaceOpenGL3D *>(_materials[materialIndex]->getSurface())->setTexture();
+			static_cast<BaseSurfaceOpenGL3D *>(mat->getSurface())->setTexture();
 		} else {
 			glDisable(GL_TEXTURE_2D);
 			glBindTexture(GL_TEXTURE_2D, 0);
 		}
 
+		if (mat->getEffect()) {
+			renderEffect(mat);
+		} else {
+			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat->_material._diffuse._data);
+			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat->_material._diffuse._data);
+			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat->_material._specular._data);
+			glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat->_material._emissive._data);
+			glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mat->_material._power);
+		}
+
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glEnableClientState(GL_NORMAL_ARRAY);
 		if (textureEnable)
@@ -144,6 +151,14 @@ bool XMeshOpenGL::renderFlatShadowModel() {
 	return true;
 }
 
+void XMeshOpenGL::renderEffect(Material *material) {
+	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material->_material._diffuse._data);
+	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material->_material._diffuse._data);
+	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material->_material._specular._data);
+	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material->_material._emissive._data);
+	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->_material._power);
+}
+
 } // namespace Wintermute
 
 #endif // defined(USE_OPENGL_GAME)
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl.h b/engines/wintermute/base/gfx/opengl/meshx_opengl.h
index 952938b6229..be9837c7d70 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl.h
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl.h
@@ -30,6 +30,9 @@
 
 #include "engines/wintermute/base/gfx/xmesh.h"
 
+class Effect3D;
+class Effect3DParams;
+
 #if defined(USE_OPENGL_GAME)
 
 namespace Wintermute {
@@ -41,6 +44,9 @@ public:
 
 	bool render(XModel *model) override;
 	bool renderFlatShadowModel() override;
+
+private:
+	void renderEffect(Material *material);
 };
 
 } // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
index 74f343225fd..9294b1b7f25 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
@@ -26,6 +26,8 @@
  */
 
 #include "engines/wintermute/base/gfx/xmaterial.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
+#include "engines/wintermute/base/gfx/3deffect_params.h"
 #include "engines/wintermute/base/gfx/skin_mesh_helper.h"
 #include "engines/wintermute/base/gfx/base_renderer3d.h"
 #include "engines/wintermute/base/base_game.h"
@@ -35,6 +37,7 @@
 #if defined(USE_OPENGL_SHADERS)
 
 #include "engines/wintermute/base/gfx/opengl/base_surface_opengl3d.h"
+#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d_shader.h"
 #include "engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h"
 
 namespace Wintermute {
@@ -125,23 +128,24 @@ bool XMeshOpenGLShader::render(XModel *model) {
 	_shader->enableVertexAttribute("texcoord", _vertexBuffer, 2, GL_FLOAT, false, 4 * vertexSize, 4 * textureOffset);
 	_shader->enableVertexAttribute("normal", _vertexBuffer, 3, GL_FLOAT, false, 4 * vertexSize, 4 * normalOffset);
 
-	_shader->use(true);
-
 	for (uint32 i = 0; i < numAttrs; i++) {
-		int materialIndex = attrs[i]._attribId;
-
-		if (_materials[materialIndex]->getSurface()) {
+		Material *mat = _materials[attrs[i]._attribId];
+		if (mat->getSurface()) {
 			glEnable(GL_TEXTURE_2D);
-			static_cast<BaseSurfaceOpenGL3D *>(_materials[materialIndex]->getSurface())->setTexture();
+			static_cast<BaseSurfaceOpenGL3D *>(mat->getSurface())->setTexture();
 		} else {
 			glDisable(GL_TEXTURE_2D);
 			glBindTexture(GL_TEXTURE_2D, 0);
 		}
 
-		// wme does not seem to care about specular or emissive light values
-		Math::Vector4d diffuse(_materials[materialIndex]->_material._diffuse._data);
-		_shader->setUniform("diffuse", diffuse);
-		_shader->setUniform("ambient", diffuse);
+		if (mat->getEffect()) {
+			renderEffect(mat);
+		} else {
+			Math::Vector4d diffuse(mat->_material._diffuse._data);
+			_shader->use(true);
+			_shader->setUniform("diffuse", diffuse);
+			_shader->setUniform("ambient", diffuse);
+		}
 
 		size_t offsetFace = 4 * attrsTable->_ptr[i]._faceStart * 3;
 		glDrawElements(GL_TRIANGLES, attrsTable->_ptr[i]._faceCount * 3, GL_UNSIGNED_INT, (void *)offsetFace);
@@ -193,6 +197,13 @@ bool XMeshOpenGLShader::update(FrameNode *parentFrame) {
 	return true;
 }
 
+void XMeshOpenGLShader::renderEffect(Material *material) {
+	Math::Vector4d diffuse(material->_material._diffuse._data);
+	_shader->use(true);
+	_shader->setUniform("diffuse", diffuse);
+	_shader->setUniform("ambient", diffuse);
+}
+
 } // namespace Wintermute
 
 #endif // defined(USE_OPENGL_SHADERS)
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
index 5f63ba0a63f..49790ff6e0e 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
@@ -30,6 +30,9 @@
 
 #include "engines/wintermute/base/gfx/xmesh.h"
 
+class Effect3D;
+class Effect3DParams;
+
 #if defined(USE_OPENGL_SHADERS)
 
 #include "graphics/opengl/shader.h"
@@ -46,6 +49,9 @@ public:
 	bool renderFlatShadowModel() override;
 	bool update(FrameNode *parentFrame) override;
 
+private:
+	void renderEffect(Material *material);
+
 protected:
 	GLuint _vertexBuffer;
 	GLuint _indexBuffer;
diff --git a/engines/wintermute/base/gfx/xframe_node.cpp b/engines/wintermute/base/gfx/xframe_node.cpp
index dd2b4320b94..e055eeccdad 100644
--- a/engines/wintermute/base/gfx/xframe_node.cpp
+++ b/engines/wintermute/base/gfx/xframe_node.cpp
@@ -444,6 +444,32 @@ bool FrameNode::setMaterialTheora(char *matName, VideoTheoraPlayer *theora) {
 	return true;
 }
 
+//////////////////////////////////////////////////////////////////////////
+bool FrameNode::setMaterialEffect(char *matName, Effect3D *effect, Effect3DParams *params) {
+	for (uint32 i = 0; i < _meshes.size(); i++) {
+		_meshes[i]->setMaterialEffect(matName, effect, params);
+	}
+
+	for (uint32 i = 0; i < _frames.size(); i++) {
+		_frames[i]->setMaterialEffect(matName, effect, params);
+	}
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool FrameNode::removeMaterialEffect(const char *matName) {
+	for (uint32 i = 0; i < _meshes.size(); i++) {
+		_meshes[i]->removeMaterialEffect(matName);
+	}
+
+	for (uint32 i = 0; i < _frames.size(); i++) {
+		_frames[i]->removeMaterialEffect(matName);
+	}
+
+	return true;
+}
+
 //////////////////////////////////////////////////////////////////////////
 bool FrameNode::invalidateDeviceObjects() {
 	for (uint32 i = 0; i < _meshes.size(); i++) {
diff --git a/engines/wintermute/base/gfx/xframe_node.h b/engines/wintermute/base/gfx/xframe_node.h
index 13df5ede1cc..b150c176709 100644
--- a/engines/wintermute/base/gfx/xframe_node.h
+++ b/engines/wintermute/base/gfx/xframe_node.h
@@ -42,6 +42,8 @@ namespace Wintermute {
 class XModel;
 class XFileData;
 class BaseSprite;
+class Effect3D;
+class Effect3DParams;
 
 class FrameNode : public BaseNamedObject {
 public:
@@ -71,6 +73,8 @@ public:
 
 	bool setMaterialSprite(char *matName, BaseSprite *sprite);
 	bool setMaterialTheora(char *matName, VideoTheoraPlayer *theora);
+	bool setMaterialEffect(char *matName, Effect3D *effect, Effect3DParams *params);
+	bool removeMaterialEffect(const char *matName);
 
 	bool invalidateDeviceObjects();
 	bool restoreDeviceObjects();
diff --git a/engines/wintermute/base/gfx/xmaterial.cpp b/engines/wintermute/base/gfx/xmaterial.cpp
index 9321744bb3c..feeec5b7c0e 100644
--- a/engines/wintermute/base/gfx/xmaterial.cpp
+++ b/engines/wintermute/base/gfx/xmaterial.cpp
@@ -30,6 +30,7 @@
 #include "engines/wintermute/base/base_surface_storage.h"
 #include "engines/wintermute/base/gfx/base_surface.h"
 #include "engines/wintermute/base/gfx/xmaterial.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
 #include "engines/wintermute/base/gfx/xfile_loader.h"
 #include "engines/wintermute/dcgf.h"
 #include "engines/wintermute/utils/path_util.h"
@@ -48,6 +49,8 @@ Material::Material(BaseGame *inGame) : BaseNamedObject(inGame) {
 	_ownedSurface = false;
 	_sprite = nullptr;
 	_theora = nullptr;
+	_effect = nullptr;
+	_params = nullptr;
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -58,16 +61,21 @@ Material::~Material() {
 
 	_sprite = nullptr; // ref only
 	_theora = nullptr;
+	_effect = nullptr;
+	_params = nullptr;
 }
 
 //////////////////////////////////////////////////////////////////////////
 bool Material::invalidateDeviceObjects() {
-	// as long as we don't support D3DX effects, there is nothing to be done here
+	if (_effect)
+		return _effect->invalidateDeviceObjects();
 	return true;
 }
 
 //////////////////////////////////////////////////////////////////////////
 bool Material::restoreDeviceObjects() {
+	if (_effect)
+		return _effect->restoreDeviceObjects();
 	return true;
 }
 
@@ -129,6 +137,25 @@ bool Material::setTheora(VideoTheoraPlayer *theora, bool adoptName) {
 	return true;
 }
 
+//////////////////////////////////////////////////////////////////////////
+bool Material::setEffect(Effect3D *effect, Effect3DParams *params, bool adoptName) {
+	if (!effect) {
+		_effect = nullptr;
+		_params = nullptr;
+		return true;
+	}
+
+	if (adoptName) {
+		setName(PathUtil::getFileNameWithoutExtension(effect->getFileName()).c_str());
+	}
+	_textureFilename = effect->getFileName();
+
+	_effect = effect;
+	_params = params;
+
+	return true;
+}
+
 //////////////////////////////////////////////////////////////////////////
 BaseSurface *Material::getSurface() {
 	if (_theora) {
diff --git a/engines/wintermute/base/gfx/xmaterial.h b/engines/wintermute/base/gfx/xmaterial.h
index fc22bdb2ef3..87a11e6485c 100644
--- a/engines/wintermute/base/gfx/xmaterial.h
+++ b/engines/wintermute/base/gfx/xmaterial.h
@@ -33,6 +33,8 @@
 
 namespace Wintermute {
 
+class Effect3D;
+class Effect3DParams;
 class BaseSprite;
 class BaseSurface;
 class VideoTheoraPlayer;
@@ -48,7 +50,11 @@ public:
 	bool setTexture(const Common::String &filename, bool adoptName = false);
 	bool setSprite(BaseSprite *sprite, bool adoptName = false);
 	bool setTheora(VideoTheoraPlayer *theora, bool adoptName = false);
+	bool setEffect(Effect3D *effect, Effect3DParams *params, bool adoptName = false);
+
 	BaseSurface *getSurface();
+	Effect3D *getEffect() { return _effect; }
+	Effect3DParams *getEffectParams() { return _params; }
 
 	bool invalidateDeviceObjects();
 	bool restoreDeviceObjects();
@@ -59,6 +65,8 @@ private:
 	bool _ownedSurface;
 	BaseSprite *_sprite;
 	VideoTheoraPlayer *_theora;
+	Effect3D *_effect;
+	Effect3DParams *_params;
 };
 
 } // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xmesh.cpp b/engines/wintermute/base/gfx/xmesh.cpp
index d09b7b71204..c26e7c60f66 100644
--- a/engines/wintermute/base/gfx/xmesh.cpp
+++ b/engines/wintermute/base/gfx/xmesh.cpp
@@ -34,6 +34,7 @@
 #include "engines/wintermute/base/gfx/xmodel.h"
 #include "engines/wintermute/base/gfx/xbuffer.h"
 #include "engines/wintermute/base/gfx/xskinmesh.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
 #include "engines/wintermute/base/gfx/3dutils.h"
 #include "engines/wintermute/base/base_engine.h"
 #include "engines/wintermute/utils/path_util.h"
@@ -317,13 +318,33 @@ bool XMesh::setMaterialSprite(const Common::String &matName, BaseSprite *sprite)
 //////////////////////////////////////////////////////////////////////////
 bool XMesh::setMaterialTheora(const Common::String &matName, VideoTheoraPlayer *theora) {
 	for (uint32 i = 0; i < _materials.size(); i++) {
-		if (_materials[i]->getName() && scumm_stricmp(_materials[i]->getName(),  matName.c_str()) == 0) {
+		if (_materials[i]->getName() && scumm_stricmp(_materials[i]->getName(), matName.c_str()) == 0) {
 			_materials[i]->setTheora(theora);
 		}
 	}
 	return true;
 }
 
+//////////////////////////////////////////////////////////////////////////
+bool XMesh::setMaterialEffect(const Common::String &matName, Effect3D *effect, Effect3DParams *params) {
+	for (uint32 i = 0; i < _materials.size(); i++) {
+		if (_materials[i]->getName() && scumm_stricmp(_materials[i]->getName(), matName.c_str()) == 0) {
+			_materials[i]->setEffect(effect, params);
+		}
+	}
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XMesh::removeMaterialEffect(const Common::String &matName) {
+	for (uint32 i = 0; i < _materials.size(); i++) {
+		if (_materials[i]->getName() && scumm_stricmp(_materials[i]->getName(), matName.c_str()) == 0) {
+			_materials[i]->setEffect(nullptr, nullptr);
+		}
+	}
+	return true;
+}
+
 //////////////////////////////////////////////////////////////////////////
 bool XMesh::invalidateDeviceObjects() {
 	if (_skinMesh) {
diff --git a/engines/wintermute/base/gfx/xmesh.h b/engines/wintermute/base/gfx/xmesh.h
index 905d5b4e85b..e7d45837e6c 100644
--- a/engines/wintermute/base/gfx/xmesh.h
+++ b/engines/wintermute/base/gfx/xmesh.h
@@ -44,6 +44,8 @@ class ShadowVolume;
 class VideoTheoraPlayer;
 class SkinMeshHelper;
 class DXMesh;
+class Effect3D;
+class Effect3DParams;
 struct XMeshObject;
 
 class XMesh : public BaseNamedObject {
@@ -66,6 +68,8 @@ public:
 
 	bool setMaterialSprite(const Common::String &matName, BaseSprite *sprite);
 	bool setMaterialTheora(const Common::String &matName, VideoTheoraPlayer *theora);
+	bool setMaterialEffect(const Common::String &matName, Effect3D *effect, Effect3DParams *params);
+	bool removeMaterialEffect(const Common::String &matName);
 
 	bool invalidateDeviceObjects();
 	bool restoreDeviceObjects();
diff --git a/engines/wintermute/base/gfx/xmodel.cpp b/engines/wintermute/base/gfx/xmodel.cpp
index 1385496a679..078104886f1 100644
--- a/engines/wintermute/base/gfx/xmodel.cpp
+++ b/engines/wintermute/base/gfx/xmodel.cpp
@@ -39,6 +39,7 @@
 #include "engines/wintermute/base/gfx/xframe_node.h"
 #include "engines/wintermute/base/gfx/xmaterial.h"
 #include "engines/wintermute/base/gfx/xmodel.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
 #include "engines/wintermute/base/gfx/xfile.h"
 #include "engines/wintermute/base/gfx/xfile_loader.h"
 #include "engines/wintermute/dcgf.h"
@@ -858,6 +859,97 @@ bool XModel::setMaterialTheora(const char *materialName, const char *theoraFilen
 	return true;
 }
 
+//////////////////////////////////////////////////////////////////////////
+bool XModel::setMaterialEffect(const char *materialName, const char *effectFilename) {
+	if (!materialName || !effectFilename)
+		return false;
+	if (!_rootFrame)
+		return false;
+
+
+	Effect3D *effect = new Effect3D(_gameRef);
+	if (!effect->createFromFile(effectFilename)) {
+		delete effect;
+		return false;
+	}
+
+	XModelMatSprite *matSprite = nullptr;
+	for (uint32 i = 0 ; i < _matSprites.size(); i++) {
+		if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) {
+			matSprite = _matSprites[i];
+			break;
+		}
+	}
+	if (matSprite) {
+		matSprite->setEffect(effect);
+	} else {
+		matSprite = new XModelMatSprite(materialName, effect);
+		_matSprites.add(matSprite);
+	}
+	_rootFrame->setMaterialEffect(matSprite->_matName, matSprite->_effect, matSprite->_effectParams);
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XModel::removeMaterialEffect(const char *materialName) {
+	if (!materialName)
+		return false;
+	if (!_rootFrame)
+		return false;
+
+	for (uint32 i = 0; i < _matSprites.size(); i++) {
+		if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) {
+			delete _matSprites[i];
+			_matSprites[i] = nullptr;
+			_matSprites.remove_at(i);
+			_rootFrame->removeMaterialEffect(materialName);
+			return true;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XModel::setMaterialEffectParam(const char *materialName, const char *paramName, ScValue *val) {
+	if (!materialName)
+		return false;
+	if (!_rootFrame)
+		return false;
+
+
+	for (uint32 i = 0 ; i < _matSprites.size(); i++) {
+		if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) {
+			if (_matSprites[i]->_effectParams) {
+				_matSprites[i]->_effectParams->setParam(paramName, val);
+				return true;
+			} else
+				return false;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XModel::setMaterialEffectParam(const char *materialName, const char *paramName, DXVector4 val) {
+	if (!materialName)
+		return false;
+	if (!_rootFrame)
+		return false;
+
+
+	for (uint32 i = 0; i < _matSprites.size(); i++) {
+		if (scumm_stricmp(_matSprites[i]->_matName, materialName) == 0) {
+			if (_matSprites[i]->_effectParams) {
+				_matSprites[i]->_effectParams->setParam(paramName, val);
+				return true;
+			} else
+				return false;
+		}
+	}
+	return false;
+}
+
 //////////////////////////////////////////////////////////////////////////
 bool XModel::initializeSimple() {
 	if (!_rootFrame) {
@@ -871,8 +963,18 @@ bool XModel::initializeSimple() {
 		} else if (_matSprites[i]->_sprite) {
 			_rootFrame->setMaterialSprite(_matSprites[i]->_matName, _matSprites[i]->_sprite);
 		}
+
+		if (_matSprites[i]->_effectFile) {
+			Effect3D *effect = new Effect3D(_gameRef);
+			if (effect->createFromFile(_matSprites[i]->_effectFile)) {
+				_matSprites[i]->_effect = effect;
+				_rootFrame->setMaterialEffect(_matSprites[i]->_matName, _matSprites[i]->_effect, _matSprites[i]->_effectParams);
+			} else {
+				delete effect;
+				effect = nullptr;
+			}
+		}
 	}
-	// TODO: Effects
 
 	if (_parentModel) {
 		findBones(false, _parentModel);
diff --git a/engines/wintermute/base/gfx/xmodel.h b/engines/wintermute/base/gfx/xmodel.h
index 365f97640f9..b00b4f06930 100644
--- a/engines/wintermute/base/gfx/xmodel.h
+++ b/engines/wintermute/base/gfx/xmodel.h
@@ -31,6 +31,8 @@
 #include "engines/wintermute/base/base_object.h"
 #include "engines/wintermute/base/base_sprite.h"
 #include "engines/wintermute/base/gfx/xmath.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
+#include "engines/wintermute/base/gfx/3deffect_params.h"
 #include "engines/wintermute/coll_templ.h"
 #include "engines/wintermute/math/rect32.h"
 #include "engines/wintermute/video/video_theora_player.h"
@@ -48,6 +50,8 @@ class FrameNode;
 class Material;
 class ShadowVolume;
 class XFileData;
+class Effect3D;
+class Effect3DParams;
 
 #define X_NUM_ANIMATION_CHANNELS 10
 
@@ -56,33 +60,64 @@ private:
 	class XModelMatSprite {
 	public:
 		char *_matName;
+		char *_effectFile;
 		BaseSprite *_sprite;
 		VideoTheoraPlayer *_theora;
+		Effect3D *_effect;
+		Effect3DParams *_effectParams;
 
 		XModelMatSprite() {
 			_matName = nullptr;
 			_sprite = nullptr;
 			_theora = nullptr;
+			_effect = nullptr;
+			_effectFile = nullptr;
+			_effectParams = nullptr;
 		}
 
 		XModelMatSprite(const char *matName, BaseSprite *sprite) {
 			_theora = nullptr;
 			_matName = nullptr;
+			_effect = nullptr;
 			BaseUtils::setString(&_matName, matName);
 			_sprite = sprite;
+			_effectFile = nullptr;
+			_effectParams = nullptr;
 		}
 
 		XModelMatSprite(const char *matName, VideoTheoraPlayer *theora) {
 			_sprite = nullptr;
 			_matName = nullptr;
+			_effect = nullptr;
 			BaseUtils::setString(&_matName, matName);
 			_theora = theora;
+			_effectFile = nullptr;
+			_effectParams = nullptr;
+		}
+
+		XModelMatSprite(const char *matName, Effect3D *effect) {
+			_sprite = nullptr;
+			_matName = nullptr;
+			_theora = nullptr;
+			BaseUtils::setString(&_matName, matName);
+			_effect = effect;
+			_effectFile = nullptr;
+			_effectParams = new Effect3DParams();
 		}
 
 		~XModelMatSprite() {
 			delete[] _matName;
+			_matName = nullptr;
+			delete _effectFile;
+			_effectFile = nullptr;
 			delete _sprite;
+			_sprite = nullptr;
 			delete _theora;
+			_theora = nullptr;
+			delete _effect;
+			_effect = nullptr;
+			delete _effectParams;
+			_effectParams = nullptr;
 		}
 
 		bool setSprite(BaseSprite *sprite) {
@@ -96,6 +131,7 @@ private:
 
 		bool setTheora(VideoTheoraPlayer *theora) {
 			delete _theora;
+			_theora = nullptr;
 			delete _sprite;
 			_sprite = nullptr;
 			_theora = theora;
@@ -103,12 +139,54 @@ private:
 			return true;
 		}
 
+		bool setEffect(Effect3D *effect) {
+			delete _effect;
+			_effect = effect;
+
+			if (!_effectParams)
+				_effectParams = new Effect3DParams();
+			else
+				_effectParams->clear();
+
+			return true;
+		}
+
 		bool persist(BasePersistenceManager *persistMgr) {
 			persistMgr->transferCharPtr(TMEMBER(_matName));
 			persistMgr->transferPtr(TMEMBER(_sprite));
 
 			persistMgr->transferPtr(TMEMBER(_theora));
 
+			if (persistMgr->getIsSaving()) {
+				char *effectFileName = nullptr;
+				if (_effect)
+					BaseUtils::setString(&effectFileName, _effect->getFileName());
+				else
+					effectFileName = nullptr;
+
+				persistMgr->transferCharPtr(TMEMBER(effectFileName));
+				delete[] effectFileName;
+			} else {
+				persistMgr->transferCharPtr(TMEMBER(_effectFile));
+			}
+
+			if (persistMgr->getIsSaving()) {
+				bool hasParams = _effectParams != nullptr;
+				persistMgr->transferBool(TMEMBER(hasParams));
+
+				if (hasParams)
+					_effectParams->persist(persistMgr);
+			} else {
+				bool hasParams;
+				persistMgr->transferBool(TMEMBER(hasParams));
+
+				if (hasParams) {
+					_effectParams = new Effect3DParams();
+					_effectParams->persist(persistMgr);
+				} else
+					_effectParams = nullptr;
+			}
+
 			return true;
 		}
 	};
@@ -161,6 +239,10 @@ public:
 
 	bool setMaterialSprite(const char *materialName, const char *spriteFilename);
 	bool setMaterialTheora(const char *materialName, const char *theoraFilename);
+	bool setMaterialEffect(const char *materialName, const char *effectFilename);
+	bool removeMaterialEffect(const char *materialName);
+	bool setMaterialEffectParam(const char *materialName, const char *paramName, ScValue *val);
+	bool setMaterialEffectParam(const char *materialName, const char *paramName, DXVector4 val);
 	bool initializeSimple();
 
 	bool invalidateDeviceObjects() override;
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 7bde9e2ae52..28dcb973f48 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -162,6 +162,8 @@ MODULE_OBJS += \
 	ad/ad_waypoint_group3d.o \
 	base/gfx/3dcamera.o \
 	base/gfx/3dlight.o \
+	base/gfx/3deffect.o \
+	base/gfx/3deffect_params.o \
 	base/gfx/3dface.o \
 	base/gfx/3dloader_3ds.o \
 	base/gfx/3dmesh.o \




More information about the Scummvm-git-logs mailing list