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

mduggan noreply at scummvm.org
Wed Feb 15 12:47:02 UTC 2023


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

Summary:
cfdb19120f TETRAEDGE: Add basic particle parsing for Syberia 2
f5b648de20 TETRAEDGE: Texture loading features for Syberia 2


Commit: cfdb19120f01796d9a9950b13cb30d895fc1e1da
    https://github.com/scummvm/scummvm/commit/cfdb19120f01796d9a9950b13cb30d895fc1e1da
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-15T21:44:27+09:00

Commit Message:
TETRAEDGE: Add basic particle parsing for Syberia 2

They are still not used for anything, but basic data is now loaded.

Changed paths:
  A engines/tetraedge/game/particle_xml_parser.cpp
  A engines/tetraedge/game/particle_xml_parser.h
  A engines/tetraedge/te/te_particle.cpp
  A engines/tetraedge/te/te_particle.h
  A engines/tetraedge/te/te_xml_parser.cpp
  A engines/tetraedge/te/te_xml_parser.h
    engines/tetraedge/game/character_settings_xml_parser.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/in_game_scene_xml_parser.cpp
    engines/tetraedge/game/in_game_scene_xml_parser.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/scene_lights_xml_parser.cpp
    engines/tetraedge/game/scene_lights_xml_parser.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_camera_xml_parser.cpp
    engines/tetraedge/te/te_camera_xml_parser.h
    engines/tetraedge/te/te_text_layout_xml_parser.cpp
    engines/tetraedge/te/te_text_layout_xml_parser.h
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/character_settings_xml_parser.cpp b/engines/tetraedge/game/character_settings_xml_parser.cpp
index 0ced45aa8d9..b081263367f 100644
--- a/engines/tetraedge/game/character_settings_xml_parser.cpp
+++ b/engines/tetraedge/game/character_settings_xml_parser.cpp
@@ -170,8 +170,14 @@ bool CharacterSettingsXmlParser::textCallback(const Common::String &val) {
 }
 
 bool CharacterSettingsXmlParser::handleUnknownKey(ParserNode *node) {
-	warning("TODO: CharacterSettingsXmlParser handle unknown key %s", node->name.c_str());
-	return true;
+	if (node->values.contains("animFile")) {
+		const Common::String &animFile = node->values["animFile"];
+		warning("TODO: CharacterSettingsXmlParser handle mapping %s -> %s",
+			node->name.c_str(), animFile.c_str());
+		return true;
+	}
+	parserError("Unknown key");
+	return false;
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index fd6cd61584e..7a6a08b1d84 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -32,6 +32,7 @@
 #include "tetraedge/game/character.h"
 #include "tetraedge/game/characters_shadow.h"
 #include "tetraedge/game/object3d.h"
+#include "tetraedge/game/particle_xml_parser.h"
 #include "tetraedge/game/scene_lights_xml_parser.h"
 
 #include "tetraedge/te/te_bezier_curve.h"
@@ -695,6 +696,17 @@ bool InGameScene::loadXml(const Common::String &zone, const Common::String &scen
 	if (!parser.parse())
 		error("InGameScene::loadXml: Can't parse %s", node.getPath().c_str());
 
+	Common::Path pxmlpath = _sceneFileNameBase(zone, scene).joinInPlace("particles.xml");
+	Common::FSNode pnode = g_engine->getCore()->findFile(pxmlpath);
+	if (pnode.isReadable()) {
+		ParticleXmlParser pparser;
+		pparser._scene = this;
+		if (!pparser.loadFile(pnode))
+			error("InGameScene::loadXml: Can't load %s", pnode.getPath().c_str());
+		if (!pparser.parse())
+			error("InGameScene::loadXml: Can't parse %s", pnode.getPath().c_str());
+	}
+
 	return true;
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 1829ad7c43c..26897c20d1f 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -35,6 +35,7 @@
 #include "tetraedge/te/te_scene.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_particle.h"
 #include "tetraedge/te/te_pick_mesh2.h"
 
 namespace Tetraedge {
@@ -90,6 +91,14 @@ public:
 		TeVector3f32 _scale;
 	};
 
+	struct Flamme {
+		Common::String _name;
+		TeVector3f32 _center;
+		TeVector3f32 _yMax;
+		TeVector3f32 _offsetMin;
+		TeVector3f32 _offsetMax;
+	};
+
 	void activateAnchorZone(const Common::String &name, bool val);
 	void addAnchorZone(const Common::String &s1, const Common::String &name, float radius);
 	void addBlockingObject(const Common::String &obj) {
@@ -220,6 +229,7 @@ public:
 	void setWaitTime(float usecs) { _waitTime = usecs; }
 	TeTimer &waitTimeTimer() { return _waitTimeTimer; }
 	Common::Array<Common::SharedPtr<TeLight>> &lights() { return _lights; }
+	Common::Array<TeIntrusivePtr<TeParticle>> &particles() { return _particles; }
 
 	// Note: Zone name and scene name are only set in Syberia 2
 	const Common::String getZoneName() const { return _zoneName; }
@@ -258,8 +268,10 @@ private:
 	Common::Array<Object> _objects;
 	Common::Array<TeIntrusivePtr<TeBezierCurve>> _bezierCurves;
 	Common::Array<Dummy> _dummies;
+	Common::Array<Flamme> _flammes;
 	Common::Array<TeIntrusivePtr<TeModel>> _zoneModels;
 	Common::Array<TeIntrusivePtr<TeModel>> _masks;
+	Common::Array<TeIntrusivePtr<TeParticle>> _particles;
 
 	TeIntrusivePtr<TeModel> _playerCharacterModel;
 	TeIntrusivePtr<TeBezierCurve> _curve;
diff --git a/engines/tetraedge/game/in_game_scene_xml_parser.cpp b/engines/tetraedge/game/in_game_scene_xml_parser.cpp
index 9329dfa7e4f..3adc56e9f2c 100644
--- a/engines/tetraedge/game/in_game_scene_xml_parser.cpp
+++ b/engines/tetraedge/game/in_game_scene_xml_parser.cpp
@@ -121,6 +121,36 @@ bool InGameSceneXmlParser::parserCallback_noCollisionSlide(ParserNode *node) {
 	return true;
 }
 
+bool InGameSceneXmlParser::parserCallback_flamme(ParserNode *node) {
+	_scene->_flammes.push_back(InGameScene::Flamme());
+	return true;
+}
+
+bool InGameSceneXmlParser::parserCallback_name(ParserNode *node) {
+	_scene->_flammes.back()._name = node->values["value"];
+	return true;
+}
+
+bool InGameSceneXmlParser::parserCallback_center(ParserNode *node) {
+	_scene->_flammes.back()._center = parsePoint(node);
+	return true;
+}
+
+bool InGameSceneXmlParser::parserCallback_yMax(ParserNode *node) {
+	_scene->_flammes.back()._yMax = parsePoint(node);
+	return true;
+}
+
+bool InGameSceneXmlParser::parserCallback_offsetMin(ParserNode *node) {
+	_scene->_flammes.back()._offsetMin = parsePoint(node);
+	return true;
+}
+
+bool InGameSceneXmlParser::parserCallback_offsetMax(ParserNode *node) {
+	_scene->_flammes.back()._offsetMax = parsePoint(node);
+	return true;
+}
+
 bool InGameSceneXmlParser::closedKeyCallback(ParserNode *node) {
 	_textNodeType = TextNodeNone;
 	if (node->name == "pathZone") {
@@ -147,9 +177,8 @@ bool InGameSceneXmlParser::textCallback(const Common::String &val) {
 			return false;
 		}
 		_fmzGridSize = sz;
+		break;
 	}
-	// fall through
-	// FIXME: Is this intentional or break missing?
 	default:
 		parserError("Unexpected text block");
 		return false;
diff --git a/engines/tetraedge/game/in_game_scene_xml_parser.h b/engines/tetraedge/game/in_game_scene_xml_parser.h
index d895d4418be..5e0c382320d 100644
--- a/engines/tetraedge/game/in_game_scene_xml_parser.h
+++ b/engines/tetraedge/game/in_game_scene_xml_parser.h
@@ -19,19 +19,22 @@
  *
  */
 
-#include "common/hashmap.h"
-#include "common/str.h"
-#include "common/formats/xmlparser.h"
-#include "tetraedge/game/in_game_scene.h"
-
 #ifndef TETRAEDGE_GAME_IN_GAME_SCENE_XML_PARSER_H
 #define TETRAEDGE_GAME_IN_GAME_SCENE_XML_PARSER_H
 
+#include "tetraedge/te/te_xml_parser.h"
+#include "tetraedge/game/in_game_scene.h"
+
 namespace Tetraedge {
 
-class InGameSceneXmlParser : public Common::XMLParser {
+/**
+ * XML Parser for in game scene files in Syberia 2.
+ * Sybeira 1 uses a binary format, see InGameScene::load.
+ */
+class InGameSceneXmlParser : public TeXmlParser {
 public:
-	// Parser
+	// NOTE: This doesn't handle snowCustom tag which was
+	// added in original but commented out in every place.
 	CUSTOM_XML_PARSER(InGameSceneXmlParser) {
 		XML_KEY(scene)
 			XML_KEY(camera)
@@ -84,6 +87,31 @@ public:
 			XML_KEY(light)
 				XML_PROP(name, true)
 			KEY_END()
+			XML_KEY(flamme)
+				XML_KEY(name)
+					XML_PROP(value, true)
+				KEY_END()
+				XML_KEY(center)
+					XML_PROP(x, true)
+					XML_PROP(y, true)
+					XML_PROP(z, true)
+				KEY_END()
+				XML_KEY(yMax)
+					XML_PROP(x, true)
+					XML_PROP(y, true)
+					XML_PROP(z, true)
+				KEY_END()
+				XML_KEY(offsetMin)
+					XML_PROP(x, true)
+					XML_PROP(y, true)
+					XML_PROP(z, true)
+				KEY_END()
+				XML_KEY(offsetMax)
+					XML_PROP(x, true)
+					XML_PROP(y, true)
+					XML_PROP(z, true)
+				KEY_END()
+			KEY_END()
 			XML_KEY(collisionSlide)
 			KEY_END()
 			XML_KEY(noCollisionSlide)
@@ -111,6 +139,14 @@ public:
 	bool parserCallback_collisionSlide(ParserNode *node);
 	bool parserCallback_noCollisionSlide(ParserNode *node);
 
+	// Flamme and its children.
+	bool parserCallback_flamme(ParserNode *node);
+	bool parserCallback_name(ParserNode *node);
+	bool parserCallback_center(ParserNode *node);
+	bool parserCallback_yMax(ParserNode *node);
+	bool parserCallback_offsetMin(ParserNode *node);
+	bool parserCallback_offsetMax(ParserNode *node);
+
 	virtual bool closedKeyCallback(ParserNode *node) override;
 	virtual bool textCallback(const Common::String &val) override;
 
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 92f52b706c1..3dcb45b095e 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -30,6 +30,7 @@
 #include "tetraedge/to_lua.h"
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_lua_thread.h"
+#include "tetraedge/te/te_particle.h"
 
 namespace Tetraedge {
 
@@ -2425,22 +2426,22 @@ static int tolua_ExportedFunctions_ActivateMask00(lua_State *L) {
 // Not your imagination, the implementation of these two is quite different to the others.
 static int tolua_GetParticleIndex(lua_State *L) {
 	Common::String s1(tolua_tostring(L, 1, nullptr));
-	warning("TODO: GetParticleIndex(%s)", s1.c_str());
-	tolua_pushnumber(L, 0);
-	//int idx = TeParticle::getIndex(s1);
-	//tolua_pushnumber(L, idx);
+	int idx = TeParticle::getIndex(s1);
+	tolua_pushnumber(L, idx);
 	return 1;
 }
 
 static int tolua_EnableParticle(lua_State *L) {
 	double d1 = tolua_tonumber(L, 1, 0.0);
-	/*
+	if (d1 < 0) {
+		warning("EnableParticle: Invalid particle %d requested", (int)d1);
+		return 0;
+	}
 	TeParticle *p = (TeParticle *)TeParticle::getIndexedParticle((int)d1);
 	if (p) {
 		double d2 = tolua_tonumber(L, 2, 1.0);
-		p->enable((int)d2 != 0);
-	}*/
-	warning("TODO: EnableParticle(%d)", (int)d1);
+		p->setEnabled((int)d2 != 0);
+	}
 	return 0;
 }
 
diff --git a/engines/tetraedge/game/particle_xml_parser.cpp b/engines/tetraedge/game/particle_xml_parser.cpp
new file mode 100644
index 00000000000..e3d2ac10a53
--- /dev/null
+++ b/engines/tetraedge/game/particle_xml_parser.cpp
@@ -0,0 +1,126 @@
+/* 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/>.
+ *
+ */
+
+#include "tetraedge/game/particle_xml_parser.h"
+
+namespace Tetraedge {
+
+bool ParticleXmlParser::parserCallback_particle(ParserNode *node) {
+	_scene->particles().push_back(new TeParticle(_scene));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_name(ParserNode *node) {
+	_scene->particles().back()->setName(node->values["value"]);
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_texture(ParserNode *node) {
+	_scene->particles().back()->loadTexture(node->values["value"]);
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_position(ParserNode *node) {
+	_scene->particles().back()->setPosition(parsePoint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_direction(ParserNode *node) {
+	_scene->particles().back()->setDirection(parsePoint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_size(ParserNode *node) {
+	_scene->particles().back()->setSize(parseDouble(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_volumesize(ParserNode *node) {
+	_scene->particles().back()->setVolumeSize(parsePoint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_startcolor(ParserNode *node) {
+	TeColor col;
+	if (parseCol(node, col)) {
+		_scene->particles().back()->setStartColor(col);
+		return true;
+	}
+	return false;
+}
+
+bool ParticleXmlParser::parserCallback_endcolor(ParserNode *node) {
+	TeColor col;
+	if (parseCol(node, col)) {
+		_scene->particles().back()->setEndColor(col);
+		return true;
+	}
+	return false;
+}
+
+bool ParticleXmlParser::parserCallback_colortime(ParserNode *node) {
+	_scene->particles().back()->setColorTime(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_time(ParserNode *node) {
+	_scene->particles().back()->setTime(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_period(ParserNode *node) {
+	_scene->particles().back()->setPeriod(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_particleperperiod(ParserNode *node) {
+	_scene->particles().back()->setParticlePerPeriod(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_startloop(ParserNode *node) {
+	_scene->particles().back()->setStartLoop(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_enabled(ParserNode *node) {
+	_scene->particles().back()->setEnabled(parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_gravity(ParserNode *node) {
+	_scene->particles().back()->setGravity(parseDouble(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_randomdirection(ParserNode *node) {
+	_scene->particles().back()->setRandomDir((bool)parseUint(node));
+	return true;
+}
+
+bool ParticleXmlParser::parserCallback_orientation(ParserNode *node) {
+	_scene->particles().back()->setOrientation(parsePoint(node));
+	return true;
+}
+
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/particle_xml_parser.h b/engines/tetraedge/game/particle_xml_parser.h
new file mode 100644
index 00000000000..a39230c88e7
--- /dev/null
+++ b/engines/tetraedge/game/particle_xml_parser.h
@@ -0,0 +1,154 @@
+/* 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/>.
+ *
+ */
+
+#ifndef TETRAEDGE_GAME_PARTICLE_XML_PARSER_H
+#define TETRAEDGE_GAME_PARTICLE_XML_PARSER_H
+
+#include "tetraedge/game/in_game_scene.h"
+#include "tetraedge/te/te_xml_parser.h"
+
+namespace Tetraedge {
+
+class ParticleXmlParser : public TeXmlParser {
+public:
+	CUSTOM_XML_PARSER(ParticleXmlParser) {
+		XML_KEY(particle)
+			XML_KEY(name)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(texture)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(position)
+				XML_PROP(x, true)
+				XML_PROP(y, true)
+				XML_PROP(z, true)
+			KEY_END()
+			XML_KEY(direction)
+				XML_PROP(x, true)
+				XML_PROP(y, true)
+				XML_PROP(z, true)
+			KEY_END()
+			XML_KEY(size)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(volumesize)
+				XML_PROP(x, true)
+				XML_PROP(y, true)
+				XML_PROP(z, true)
+			KEY_END()
+			XML_KEY(startcolor)
+				XML_PROP(r, true)
+				XML_PROP(g, true)
+				XML_PROP(b, true)
+				XML_PROP(a, true)
+			KEY_END()
+			XML_KEY(endcolor)
+				XML_PROP(r, true)
+				XML_PROP(g, true)
+				XML_PROP(b, true)
+				XML_PROP(a, true)
+			KEY_END()
+			XML_KEY(colortime)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(time)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(period)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(particleperperiod)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(startloop)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(enabled)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(gravity)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(randomdirection)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(orientation)
+				XML_PROP(x, true)
+				XML_PROP(y, true)
+				XML_PROP(z, true)
+			KEY_END()
+		KEY_END()
+	} PARSER_END()
+
+	bool parserCallback_particle(ParserNode *node);
+	bool parserCallback_name(ParserNode *node);
+	bool parserCallback_texture(ParserNode *node);
+	bool parserCallback_position(ParserNode *node);
+	bool parserCallback_direction(ParserNode *node);
+	bool parserCallback_size(ParserNode *node);
+	bool parserCallback_volumesize(ParserNode *node);
+	bool parserCallback_startcolor(ParserNode *node);
+	bool parserCallback_endcolor(ParserNode *node);
+	bool parserCallback_colortime(ParserNode *node);
+	bool parserCallback_time(ParserNode *node);
+	bool parserCallback_period(ParserNode *node);
+	bool parserCallback_particleperperiod(ParserNode *node);
+	bool parserCallback_startloop(ParserNode *node);
+	bool parserCallback_enabled(ParserNode *node);
+	bool parserCallback_gravity(ParserNode *node);
+	bool parserCallback_randomdirection(ParserNode *node);
+	bool parserCallback_orientation(ParserNode *node);
+
+public:
+	InGameScene *_scene;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_PARTICLE_XML_PARSER_H
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
index 34cce550195..c6b01c8ee77 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.cpp
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -29,24 +29,6 @@ bool SceneLightsXmlParser::parserCallback_Global(ParserNode *node) {
 	return true;
 }
 
-bool SceneLightsXmlParser::parseCol(ParserNode *node, TeColor &colout) {
-	uint r = node->values["r"].asUint64();
-	uint g = node->values["g"].asUint64();
-	uint b = node->values["b"].asUint64();
-	uint a;
-	if (node->values.contains("a"))
-		a = node->values["a"].asUint64();
-	else
-		a = 0xff;
-
-	if (r > 255 || g > 255 || b > 255 || a > 255) {
-		parserError("Invalid color values");
-		return false;
-	}
-	colout = TeColor(r, g, b, a);
-	return true;
-}
-
 bool SceneLightsXmlParser::parserCallback_Ambient(ParserNode *node) {
 	// can appear under either global or light
 	TeColor col;
@@ -79,16 +61,13 @@ bool SceneLightsXmlParser::parserCallback_Light(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Position(ParserNode *node) {
-	float x = atof(node->values["x"].c_str());
-	float y = atof(node->values["y"].c_str());
-	float z = atof(node->values["z"].c_str());
-	_lights->back()->setPosition3d(TeVector3f32(x, y, z));
+	_lights->back()->setPosition3d(parsePoint(node));
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_Direction(ParserNode *node) {
-	float h = (atof(node->values["h"].c_str()) * M_PI) / 180.0;
-	float v = (atof(node->values["v"].c_str()) * M_PI) / 180.0;
+	float h = (float)((parseDouble(node, "h") * M_PI) / 180.0);
+	float v = (float)((parseDouble(node, "v") * M_PI) / 180.0);
 	_lights->back()->setPositionRadial(TeVector2f32(h, v));
 	return true;
 }
@@ -112,9 +91,9 @@ bool SceneLightsXmlParser::parserCallback_Specular(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
-	float c = atof(node->values["constant"].c_str());
-	float l = atof(node->values["linear"].c_str());
-	float q = atof(node->values["quadratic"].c_str());
+	float c = parseDouble(node, "constant");
+	float l = parseDouble(node, "linear");
+	float q = parseDouble(node, "quadratic");
 	if (c < 0 || l < 0 || q < 0)
 		warning("Loaded invalid lighting attenuation vals %f %f %f", c, l, q);
 	_lights->back()->setConstAtten(c);
@@ -124,7 +103,7 @@ bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
-	float cutoff = atof(node->values["value"].c_str());
+	float cutoff = parseDouble(node);
 	if (cutoff < 0.0f || (cutoff > 90.0f && cutoff != 180.0f))
 		warning("Loaded invalid lighting cutoff value %f", cutoff);
 	_lights->back()->setCutoff((cutoff * M_PI) / 180.0);
@@ -132,7 +111,7 @@ bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Exponent(ParserNode *node) {
-	float expon = atof(node->values["value"].c_str());
+	float expon = parseDouble(node);
 	if (expon < 0.0f || expon > 128.0f)
 		warning("Loaded invalid lighting exponent value %f", expon);
 	_lights->back()->setExponent(expon);
@@ -140,7 +119,7 @@ bool SceneLightsXmlParser::parserCallback_Exponent(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_DisplaySize(ParserNode *node) {
-	_lights->back()->setDisplaySize(atof(node->values["value"].c_str()));
+	_lights->back()->setDisplaySize(parseDouble(node));
 	return true;
 }
 
@@ -156,17 +135,17 @@ bool SceneLightsXmlParser::parserCallback_SourceLight(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Fov(ParserNode *node) {
-	_shadowFov = atof(node->values["value"].c_str());
+	_shadowFov = parseDouble(node);
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_NearPlane(ParserNode *node) {
-	_shadowNearPlane = atof(node->values["value"].c_str());
+	_shadowNearPlane = parseDouble(node);
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_FarPlane(ParserNode *node) {
-	_shadowFarPlane = atof(node->values["value"].c_str());
+	_shadowFarPlane = parseDouble(node);
 	return true;
 }
 
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.h b/engines/tetraedge/game/scene_lights_xml_parser.h
index ef701e3c8f7..f66fb5b7c08 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.h
+++ b/engines/tetraedge/game/scene_lights_xml_parser.h
@@ -25,11 +25,11 @@
 #include "common/formats/xmlparser.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_vector3f32.h"
-
+#include "tetraedge/te/te_xml_parser.h"
 
 namespace Tetraedge {
 
-class SceneLightsXmlParser : public Common::XMLParser {
+class SceneLightsXmlParser : public TeXmlParser {
 public:
 	void setLightArray(Common::Array<Common::SharedPtr<TeLight>> *lights) {
 		_lights = lights;
@@ -153,8 +153,6 @@ private:
 	bool parserCallback_FarPlane(ParserNode *node);
 	bool parserCallback_Color(ParserNode *node);
 
-	bool parseCol(ParserNode *node, TeColor &colout);
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 500b51585c1..b600b0fedcf 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -37,6 +37,7 @@ MODULE_OBJS := \
 	game/objectif.o \
 	game/options_menu.o \
 	game/owner_error_menu.o \
+	game/particle_xml_parser.o \
 	game/question2.o \
 	game/scene_lights_xml_parser.o \
 	game/splash_screens.o \
@@ -86,6 +87,7 @@ MODULE_OBJS := \
 	te/te_object.o \
 	te/te_obp.o \
 	te/te_palette.o \
+	te/te_particle.o \
 	te/te_pick_mesh2.o \
 	te/te_png.o \
 	te/te_quaternion.o \
@@ -113,6 +115,7 @@ MODULE_OBJS := \
 	te/te_vector2s32.o \
 	te/te_vector3f32.o \
 	te/te_visual_fade.o \
+	te/te_xml_parser.o \
 	te/te_xml_gui.o \
 	metaengine.o
 
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index b910cbf874a..4a0285e90b4 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -47,9 +47,6 @@ bool Te3DTexture::hasAlpha() const {
 TeIntrusivePtr<Te3DTexture> Te3DTexture::load2(const Common::FSNode &node, bool alphaOnly) {
 	const Common::String fullPath = node.getPath() + ".3dtex";
 
-	if (alphaOnly)
-		warning("TODO: Handle alphaOnly in Te3DTexture::load2");
-
 	TeResourceManager *resMgr = g_engine->getResourceManager();
 	if (!resMgr->exists(fullPath)) {
 		TeIntrusivePtr<Te3DTexture> retval(makeInstance());
@@ -58,6 +55,10 @@ TeIntrusivePtr<Te3DTexture> Te3DTexture::load2(const Common::FSNode &node, bool
 		bool result = retval->load(node);
 		if (!result)
 			warning("Failed loading texture %s", node.getPath().c_str());
+
+		if (alphaOnly)
+			warning("TODO: Handle alphaOnly in Te3DTexture::load2");
+
 		retval->setAccessName(fullPath);
 		resMgr->addResource(retval.get());
 		return retval;
diff --git a/engines/tetraedge/te/te_camera_xml_parser.cpp b/engines/tetraedge/te/te_camera_xml_parser.cpp
index 22749f87bd5..4d0292a278c 100644
--- a/engines/tetraedge/te/te_camera_xml_parser.cpp
+++ b/engines/tetraedge/te/te_camera_xml_parser.cpp
@@ -24,10 +24,7 @@
 namespace Tetraedge {
 
 bool TeCameraXmlParser::parserCallback_position(ParserNode *node) {
-	float x = atof(node->values["x"].c_str());
-	float y = atof(node->values["y"].c_str());
-	float z = atof(node->values["z"].c_str());
-	_cam->setPosition(TeVector3f32(x, y, z));
+	_cam->setPosition(parsePoint(node));
 	return true;
 }
 
@@ -42,30 +39,27 @@ bool TeCameraXmlParser::parserCallback_rotation(ParserNode *node) {
 }
 
 bool TeCameraXmlParser::parserCallback_scale(ParserNode *node) {
-	float x = atof(node->values["x"].c_str());
-	float y = atof(node->values["y"].c_str());
-	float z = atof(node->values["z"].c_str());
-	_cam->setScale(TeVector3f32(x, y, z));
+	_cam->setScale(parsePoint(node));
 	return true;
 }
 
 bool TeCameraXmlParser::parserCallback_fov(ParserNode *node) {
-	_cam->setFov(atof(node->values["value"].c_str()));
+	_cam->setFov(parseDouble(node));
 	return true;
 }
 
 bool TeCameraXmlParser::parserCallback_aspect(ParserNode *node) {
-	_cam->setAspectRatio(atof(node->values["value"].c_str()));
+	_cam->setAspectRatio(parseDouble(node));
 	return true;
 }
 
 bool TeCameraXmlParser::parserCallback_near(ParserNode *node) {
-	_cam->setOrthoNear(atof(node->values["value"].c_str()));
+	_cam->setOrthoNear(parseDouble(node));
 	return true;
 }
 
 bool TeCameraXmlParser::parserCallback_far(ParserNode *node) {
-	_cam->setOrthoFar(atof(node->values["value"].c_str()));
+	_cam->setOrthoFar(parseDouble(node));
 	return true;
 }
 
diff --git a/engines/tetraedge/te/te_camera_xml_parser.h b/engines/tetraedge/te/te_camera_xml_parser.h
index 51da23e6707..efd61d2aeea 100644
--- a/engines/tetraedge/te/te_camera_xml_parser.h
+++ b/engines/tetraedge/te/te_camera_xml_parser.h
@@ -22,14 +22,12 @@
 #ifndef TETRAEDGE_TE_TE_CAMERA_XML_PARSER_H
 #define TETRAEDGE_TE_TE_CAMERA_XML_PARSER_H
 
-#include "common/str.h"
-#include "common/formats/xmlparser.h"
-
+#include "tetraedge/te/te_xml_parser.h"
 #include "tetraedge/te/te_camera.h"
 
 namespace Tetraedge {
 
-class TeCameraXmlParser : public Common::XMLParser {
+class TeCameraXmlParser : public TeXmlParser {
 public:
 	// Parser
 	CUSTOM_XML_PARSER(TeCameraXmlParser) {
diff --git a/engines/tetraedge/te/te_particle.cpp b/engines/tetraedge/te/te_particle.cpp
new file mode 100644
index 00000000000..75044845f4e
--- /dev/null
+++ b/engines/tetraedge/te/te_particle.cpp
@@ -0,0 +1,84 @@
+/* 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/>.
+ *
+ */
+
+#include "tetraedge/te/te_particle.h"
+
+namespace Tetraedge {
+
+TeParticle::TeParticle(TeScene *scene) : _scene(scene), _size(0),
+_colorTime(0), _time(0), _period(0), _particlePerPeriod(0),
+_enabled(false), _startLoop(0), _gravity(0), _randomDir(false) {
+	indexedParticles()->push_back(this);
+}
+
+TeParticle::~TeParticle() {
+	Common::Array<TeParticle *> *parts = indexedParticles();
+	for (uint i = 0; i < parts->size(); i++) {
+		if ((*parts)[i] == this) {
+			parts->remove_at(i);
+			break;
+		}
+	}
+}
+
+bool TeParticle::loadTexture(const Common::String &name) {
+	warning("TODO: TeParticle::loadTexture %s", name.c_str());
+	return true;
+}
+
+/*static*/
+Common::Array<TeParticle *> *TeParticle::_indexedParticles = nullptr;
+
+/*static*/
+int TeParticle::getIndex(const Common::String &name) {
+	int retval = -1;
+	Common::Array<TeParticle *> *parts = indexedParticles();
+	for (uint i = 0; i < parts->size(); i++) {
+		if ((*parts)[i]->_name == name) {
+			retval = i;
+			break;
+		}
+	}
+	return retval;
+}
+
+/*static*/
+TeParticle *TeParticle::getIndexedParticle(int idx) {
+	Common::Array<TeParticle *> *parts = indexedParticles();
+	if (idx >= (int)(parts->size()))
+		error("Invalid particle %d requested (of %d)", idx, parts->size());
+	return (*parts)[idx];
+}
+
+/*static*/
+Common::Array<TeParticle *> *TeParticle::indexedParticles() {
+	if (_indexedParticles == nullptr)
+		_indexedParticles = new Common::Array<TeParticle *>();
+	return _indexedParticles;
+}
+
+/*static*/
+void TeParticle::cleanup() {
+	delete _indexedParticles;
+	_indexedParticles = nullptr;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_particle.h b/engines/tetraedge/te/te_particle.h
new file mode 100644
index 00000000000..3e76937a66c
--- /dev/null
+++ b/engines/tetraedge/te/te_particle.h
@@ -0,0 +1,91 @@
+/* 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/>.
+ *
+ */
+
+#ifndef TETRAEDGE_TE_TE_PARTICLE_H
+#define TETRAEDGE_TE_TE_PARTICLE_H
+
+#include "common/str.h"
+#include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_real_timer.h"
+#include "tetraedge/te/te_intrusive_ptr.h"
+#include "tetraedge/te/te_3d_texture.h"
+#include "tetraedge/te/te_scene.h"
+
+namespace Tetraedge {
+
+class TeParticle : public TeReferencesCounter {
+public:
+	class TeElement : public TeReferencesCounter {};
+
+	TeParticle(TeScene *scene);
+	~TeParticle();
+
+	void setName(const Common::String &name) { _name = name; }
+	bool loadTexture(const Common::String &name);
+	void setPosition(const TeVector3f32 &pos) { _position = pos; }
+	void setDirection(const TeVector3f32 &dir) { _direction = dir; }
+	void setSize(float size) { _size = size; }
+	void setVolumeSize(const TeVector3f32 &size) { _volumeSize = size; }
+	void setStartColor(const TeColor &col) { _startColor = col; }
+	void setEndColor(const TeColor &col) { _endColor = col; }
+	void setColorTime(int time) { _colorTime = time; }
+	void setTime(int time) { _time = time; }
+	void setPeriod(int period) { _period = period; }
+	void setParticlePerPeriod(int val) { _particlePerPeriod = val; }
+	void setEnabled(bool enabled) { _enabled = enabled; }
+	void setStartLoop(int startloop) { _startLoop = startloop; }
+	void setGravity(float gravity) { _gravity = gravity; }
+	void setRandomDir(bool val) { _randomDir = val; }
+	void setOrientation(const TeVector3f32 &orientation) { _orientation = orientation; }
+
+	static int getIndex(const Common::String &name);
+	static TeParticle *getIndexedParticle(int idx);
+	static void cleanup();
+
+private:
+	Common::Array<TeIntrusivePtr<TeElement>> _elements;
+	TeScene *_scene;
+	TeRealTimer _realTimer;
+	Common::String _name;
+	TeIntrusivePtr<Te3DTexture> _texture;
+	TeVector3f32 _position;
+	TeVector3f32 _direction;
+	float _size;
+	TeVector3f32 _volumeSize;
+	TeColor _startColor;
+	TeColor _endColor;
+	int	_colorTime;
+	int	_time;
+	int	_period;
+	int	_particlePerPeriod;
+	bool _enabled;
+	int _startLoop;
+	float _gravity;
+	bool _randomDir;
+	TeVector3f32 _orientation;
+	
+	static Common::Array<TeParticle *> *indexedParticles();
+	static Common::Array<TeParticle *> *_indexedParticles;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_TE_TE_PARTICLE_H
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.cpp b/engines/tetraedge/te/te_text_layout_xml_parser.cpp
index cc3d11cb591..25a0478e550 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.cpp
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.cpp
@@ -29,11 +29,7 @@ bool TeTextLayoutXmlParser::parserCallback_section(ParserNode *node) {
 }
 
 bool TeTextLayoutXmlParser::parserCallback_color(ParserNode *node) {
-	const Common::String &r = node->values["r"];
-	const Common::String &g = node->values["g"];
-	const Common::String &b = node->values["b"];
-	_color = TeColor(r.asUint64(), g.asUint64(), b.asUint64(), 255);
-	return true;
+	return parseCol(node, _color);
 }
 
 bool TeTextLayoutXmlParser::parserCallback_font(ParserNode *node) {
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.h b/engines/tetraedge/te/te_text_layout_xml_parser.h
index 627f3a3cc6d..0599b9337c6 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.h
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.h
@@ -24,13 +24,13 @@
 
 #include "common/array.h"
 #include "common/str.h"
-#include "common/formats/xmlparser.h"
 
+#include "tetraedge/te/te_xml_parser.h"
 #include "tetraedge/te/te_color.h"
 
 namespace Tetraedge {
 
-class TeTextLayoutXmlParser : public Common::XMLParser {
+class TeTextLayoutXmlParser : public TeXmlParser {
 public:
 	// Parser
 	CUSTOM_XML_PARSER(TeTextLayoutXmlParser) {
diff --git a/engines/tetraedge/te/te_xml_parser.cpp b/engines/tetraedge/te/te_xml_parser.cpp
new file mode 100644
index 00000000000..78063261ea6
--- /dev/null
+++ b/engines/tetraedge/te/te_xml_parser.cpp
@@ -0,0 +1,63 @@
+/* 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/>.
+ *
+ */
+
+#include "tetraedge/te/te_xml_parser.h"
+
+namespace Tetraedge {
+
+TeVector3f32 TeXmlParser::parsePoint(const ParserNode *node) const {
+	float x = atof(node->values["x"].c_str());
+	float y = atof(node->values["y"].c_str());
+	float z = atof(node->values["z"].c_str());
+	return TeVector3f32(x, y, z);
+}
+
+bool TeXmlParser::parseCol(const ParserNode *node, TeColor &colout) {
+	uint r = node->values["r"].asUint64();
+	uint g = node->values["g"].asUint64();
+	uint b = node->values["b"].asUint64();
+	uint a;
+	if (node->values.contains("a"))
+		a = node->values["a"].asUint64();
+	else
+		a = 0xff;
+
+	if (r > 255 || g > 255 || b > 255 || a > 255) {
+		parserError("Invalid color values");
+		return false;
+	}
+	colout = TeColor(r, g, b, a);
+	return true;
+}
+
+double TeXmlParser::parseDouble(const ParserNode *node, const char *attr) const {
+	if (!attr)
+		attr = "value";
+	return atof(node->values[attr].c_str());
+}
+
+int TeXmlParser::parseUint(const ParserNode *node, const char *attr) const {
+	if (!attr)
+		attr = "value";
+	return node->values[attr].asUint64();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_xml_parser.h b/engines/tetraedge/te/te_xml_parser.h
new file mode 100644
index 00000000000..2f55d739d36
--- /dev/null
+++ b/engines/tetraedge/te/te_xml_parser.h
@@ -0,0 +1,58 @@
+/* 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/>.
+ *
+ */
+
+#ifndef TETRAEDGE_TE_TE_XML_PARSER_H
+#define TETRAEDGE_TE_TE_XML_PARSER_H
+
+#include "common/hashmap.h"
+#include "common/str.h"
+#include "common/formats/xmlparser.h"
+
+#include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_color.h"
+
+namespace Tetraedge {
+
+/**
+ * A small extension to the common XML parser to
+ * add some convenience methods.
+ **/
+class TeXmlParser : public Common::XMLParser {
+protected:
+	/// Parse a point with x/y/z attributes
+	TeVector3f32 parsePoint(const ParserNode *node) const;
+
+	/// Parse a color with r/g/b and optionally a attributes
+	/// ('a' defaults to 255 if not in the attributes).  Returns true on success.
+	bool parseCol(const ParserNode *node, TeColor &colout);
+
+	/// Parse a double value from an attribute.
+	/// Default attribute name if left null is "value".
+	double parseDouble(const ParserNode *node, const char *attr = nullptr) const;
+
+	/// Parse an integer value from an attribute
+	/// Default attribute name if left null is "value".
+	int parseUint(const ParserNode *node, const char *attr = nullptr) const;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_TE_TE_XML_PARSER_H
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 626174d2393..5c9cfb9caad 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -40,6 +40,7 @@
 #include "tetraedge/te/te_lua_thread.h"
 #include "tetraedge/te/te_sound_manager.h"
 #include "tetraedge/te/te_input_mgr.h"
+#include "tetraedge/te/te_particle.h"
 
 namespace Tetraedge {
 
@@ -67,6 +68,7 @@ TetraedgeEngine::~TetraedgeEngine() {
 	TeLuaThread::cleanup();
 	TeTimer::cleanup();
 	TeObject::cleanup();
+	TeParticle::cleanup();
 }
 
 /*static*/


Commit: f5b648de205224d8aeedcd149dde6cfc0cbd18d7
    https://github.com/scummvm/scummvm/commit/f5b648de205224d8aeedcd149dde6cfc0cbd18d7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-15T21:46:19+09:00

Commit Message:
TETRAEDGE: Texture loading features for Syberia 2

Changed paths:
    engines/tetraedge/game/particle_xml_parser.h
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_3d_texture_opengl.cpp
    engines/tetraedge/te/te_3d_texture_tinygl.cpp
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_core.h
    engines/tetraedge/te/te_particle.cpp
    engines/tetraedge/te/te_particle.h


diff --git a/engines/tetraedge/game/particle_xml_parser.h b/engines/tetraedge/game/particle_xml_parser.h
index a39230c88e7..a9722945c04 100644
--- a/engines/tetraedge/game/particle_xml_parser.h
+++ b/engines/tetraedge/game/particle_xml_parser.h
@@ -122,33 +122,6 @@ public:
 	InGameScene *_scene;
 };
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 } // end namespace Tetraedge
 
 #endif // TETRAEDGE_GAME_PARTICLE_XML_PARSER_H
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 4a0285e90b4..9d9576649ba 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -31,7 +31,7 @@ namespace Tetraedge {
 Te3DTexture::Te3DTexture() : _createdTexture(false),
 _numFrames(1), _frameRate(0), _format(TeImage::INVALID), _loaded(false),
 _width(0), _height(0), _texHeight(0), _texWidth(0), _topBorder(0), _leftBorder(0),
-_rightBorder(0), _btmBorder(0), _flipY(false) {
+_rightBorder(0), _btmBorder(0), _flipY(false), _alphaOnly(false) {
 }
 
 Te3DTexture::~Te3DTexture() {
@@ -52,13 +52,13 @@ TeIntrusivePtr<Te3DTexture> Te3DTexture::load2(const Common::FSNode &node, bool
 		TeIntrusivePtr<Te3DTexture> retval(makeInstance());
 		if (!node.isReadable())
 			warning("Request to load unreadable texture %s", node.getPath().c_str());
+		if (alphaOnly)
+			retval->setLoadAlphaOnly();
+
 		bool result = retval->load(node);
 		if (!result)
 			warning("Failed loading texture %s", node.getPath().c_str());
 
-		if (alphaOnly)
-			warning("TODO: Handle alphaOnly in Te3DTexture::load2");
-
 		retval->setAccessName(fullPath);
 		resMgr->addResource(retval.get());
 		return retval;
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index df614cdda1c..a1ef6c31bcc 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -63,9 +63,11 @@ public:
 
 	uint width() const { return _width; }
 	uint height() const { return _height; }
+	void setLoadAlphaOnly() { _alphaOnly = true; }
 
 	static Te3DTexture *makeInstance();
 
+
 protected:
 	uint _width;
 	uint _height;
@@ -76,6 +78,8 @@ protected:
 	bool _loaded;
 	TeMatrix4x4 _matrix;
 
+	bool _alphaOnly;
+
 	uint _texWidth;
 	uint _texHeight;
 	uint _leftBorder;
diff --git a/engines/tetraedge/te/te_3d_texture_opengl.cpp b/engines/tetraedge/te/te_3d_texture_opengl.cpp
index 4436ffd1b49..9f48205b4a4 100644
--- a/engines/tetraedge/te/te_3d_texture_opengl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_opengl.cpp
@@ -139,10 +139,12 @@ bool Te3DTextureOpenGL::load(const TeImage &img) {
 
 	const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, _texWidth, _texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+		GLenum destfmt = _alphaOnly ? GL_ALPHA : GL_RGB8;
+		glTexImage2D(GL_TEXTURE_2D, 0, destfmt, _texWidth, _texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.pitch / 3, img.h, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
 	} else if (_format == TeImage::RGBA8) {
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _texWidth, _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+		GLenum destfmt = _alphaOnly ? GL_ALPHA : GL_RGBA8;
+		glTexImage2D(GL_TEXTURE_2D, 0, destfmt, _texWidth, _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.w, img.h, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
 		// FIXME: Slight hack.. sometimes artifacts appear because we draw
 		// a (half?)pixel outside the original texture. Clear one more row
diff --git a/engines/tetraedge/te/te_3d_texture_tinygl.cpp b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
index 2f14d170933..9443f9d8119 100644
--- a/engines/tetraedge/te/te_3d_texture_tinygl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
@@ -140,10 +140,11 @@ bool Te3DTextureTinyGL::load(const TeImage &img) {
 	tglPixelStorei(TGL_UNPACK_ALIGNMENT, 1);
 
 	const void *imgdata = img.getPixels();
+	TGLenum destfmt = _alphaOnly ? TGL_ALPHA : TGL_RGBA;
 	if (_format == TeImage::RGB8) {
-		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.pitch / 3, img.h, 0, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
+		tglTexImage2D(TGL_TEXTURE_2D, 0, destfmt, img.pitch / 3, img.h, 0, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
 	} else if (_format == TeImage::RGBA8) {
-		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.w, img.h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, imgdata);
+		tglTexImage2D(TGL_TEXTURE_2D, 0, destfmt, img.w, img.h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, imgdata);
 	} else {
 		warning("Te3DTexture::load can't send image format %d to GL.", _format);
 	}
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 012b8d3fb62..c67f7893543 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -128,7 +128,7 @@ static Common::FSNode _findSubPath(const Common::FSNode &parent, const Common::P
 }
 
 
-Common::FSNode TeCore::findFile(const Common::Path &path) {
+Common::FSNode TeCore::findFile(const Common::Path &path) const {
 	Common::FSNode node(path);
 	if (node.exists())
 		return node;
diff --git a/engines/tetraedge/te/te_core.h b/engines/tetraedge/te/te_core.h
index 9a055534df7..f9f3ed661de 100644
--- a/engines/tetraedge/te/te_core.h
+++ b/engines/tetraedge/te/te_core.h
@@ -61,7 +61,7 @@ public:
 	// Note: this is not in the original, but it's not clear how the original
 	// adds things like "PC-MacOSX" to the path, and there is not clear logic
 	// to them, so here we are.
-	Common::FSNode findFile(const Common::Path &path);
+	Common::FSNode findFile(const Common::Path &path) const;
 
 	bool _coreNotReady;
 
diff --git a/engines/tetraedge/te/te_particle.cpp b/engines/tetraedge/te/te_particle.cpp
index 75044845f4e..58b44ec4e7d 100644
--- a/engines/tetraedge/te/te_particle.cpp
+++ b/engines/tetraedge/te/te_particle.cpp
@@ -20,6 +20,8 @@
  */
 
 #include "tetraedge/te/te_particle.h"
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_core.h"
 
 namespace Tetraedge {
 
@@ -39,9 +41,13 @@ TeParticle::~TeParticle() {
 	}
 }
 
-bool TeParticle::loadTexture(const Common::String &name) {
-	warning("TODO: TeParticle::loadTexture %s", name.c_str());
-	return true;
+bool TeParticle::loadTexture(const Common::String &filename) {
+	// Path for these textures includes '/' so convert to Path object first.
+	const Common::Path path(filename);
+	_texture = Te3DTexture::makeInstance();
+	TeCore *core = g_engine->getCore();
+	Common::FSNode texnode = core->findFile(path);
+	return _texture->load(texnode);
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_particle.h b/engines/tetraedge/te/te_particle.h
index 3e76937a66c..512a3e64568 100644
--- a/engines/tetraedge/te/te_particle.h
+++ b/engines/tetraedge/te/te_particle.h
@@ -81,7 +81,7 @@ private:
 	float _gravity;
 	bool _randomDir;
 	TeVector3f32 _orientation;
-	
+
 	static Common::Array<TeParticle *> *indexedParticles();
 	static Common::Array<TeParticle *> *_indexedParticles;
 };




More information about the Scummvm-git-logs mailing list