Index: cpp/frams/model/geometry/modelgeoclass.cpp
===================================================================
--- cpp/frams/model/geometry/modelgeoclass.cpp	(revision 1056)
+++ cpp/frams/model/geometry/modelgeoclass.cpp	(revision 1111)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2016  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -11,5 +11,5 @@
 static ParamEntry modelgeo_paramtab[] =
 {
-	{ "Creature: Geometry", 1, 5, "ModelGeometry",
+	{ "Creature: Geometry", 1, 6, "ModelGeometry",
 	"Approximately estimates sizes, volume, and area of a Model based on the geometry of its parts.\n"
 	"Example usage:\n"
@@ -22,4 +22,5 @@
 	{ "volume", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "volume", "p f()", PROCEDURE(p_volume), },
 	{ "area", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "area", "p f()", PROCEDURE(p_area), },
+	{ "voxels", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "voxels", "p oVector()", PROCEDURE(p_voxels), },
 	{ "sizesAndAxes", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "sizesAndAxes", "p oVector()", PROCEDURE(p_sizesandaxes), "The returned vector contains XYZ (sizes) and Orient (axes) objects." },
 	{ 0, 0, 0, },
@@ -33,5 +34,5 @@
 
 ModelGeometry::ModelGeometry(ModelObj *mo)
-:par(modelgeo_paramtab, this)
+	:par(modelgeo_paramtab, this)
 {
 	cached_for_density = -1; //invalid value, will be updated on first request
@@ -56,4 +57,5 @@
 	cached_area = -1;
 	cached_sizes.x = -1;
+	cached_voxels.setEmpty();
 }
 
@@ -74,12 +76,12 @@
 	if (m != NULL)
 	{
-	        if (m->getShapeType() == Model::SHAPETYPE_BALL_AND_STICK)
-			{
+		if (m->getShapeType() == Model::SHAPETYPE_BALL_AND_STICK)
+		{
 			Model *converted = new Model;
 			converted->open();
 			converted->buildUsingSolidShapeTypes(*m, Part::SHAPE_CYLINDER, 0.2);
 			converted->close();
-			m=converted;
-			}
+			m = converted;
+		}
 		ModelGeometry *mg = new ModelGeometry((ModelObj*)m);
 		mg->density = density;
@@ -106,4 +108,12 @@
 }
 
+void ModelGeometry::p_voxels(ExtValue *args, ExtValue *ret)
+{
+	onDensityChanged();
+	if (cached_voxels.isEmpty()) //calculate if invalid
+		cached_voxels = ModelGeometryInfo::getVoxels(*model, density);
+	ret->setObject(cached_voxels);
+}
+
 void ModelGeometry::p_sizesandaxes(ExtValue *args, ExtValue *ret)
 {
Index: cpp/frams/model/geometry/modelgeoclass.h
===================================================================
--- cpp/frams/model/geometry/modelgeoclass.h	(revision 1056)
+++ cpp/frams/model/geometry/modelgeoclass.h	(revision 1111)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -18,4 +18,5 @@
 	double cached_volume, cached_area;
 	Pt3D cached_sizes; Orient cached_axes;
+	ExtObject cached_voxels;
 
 	Param par;
@@ -31,4 +32,5 @@
 	PARAMPROCDEF(p_volume);
 	PARAMPROCDEF(p_area);
+	PARAMPROCDEF(p_voxels);
 	PARAMPROCDEF(p_sizesandaxes);
 #undef STATRICKCLASS
Index: cpp/frams/model/geometry/modelgeometryinfo.cpp
===================================================================
--- cpp/frams/model/geometry/modelgeometryinfo.cpp	(revision 1056)
+++ cpp/frams/model/geometry/modelgeometryinfo.cpp	(revision 1111)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -6,4 +6,6 @@
 #include <frams/model/geometry/geometryutils.h>
 #include <frams/model/geometry/meshbuilder.h>
+#include <frams/vm/classes/collectionobj.h>
+#include <frams/vm/classes/3dobject.h>
 
 void ModelGeometryInfo::findSizesAndAxes(Model &input_model, const double density,
@@ -33,12 +35,12 @@
 		return;
 	}
-	
+
 	boundingBox(model.getPart(0), lowerBoundary, upperBoundary);
-	
+
 	for (int i = 1; i < model.getPartCount(); i++)
 	{
 		Pt3D partLowerBoundary, partUpperBoundary;
 		boundingBox(model.getPart(i), partLowerBoundary, partUpperBoundary);
-		
+
 		lowerBoundary.getMin(partLowerBoundary);
 		upperBoundary.getMax(partUpperBoundary);
@@ -51,6 +53,6 @@
 	lowerBoundary.y = upperBoundary.y = part->p.y;
 	lowerBoundary.z = upperBoundary.z = part->p.z;
-	
-	for (Octants::Octant o = Octants::FIRST; o < Octants::NUMBER; o = Octants::Octant(o+1))
+
+	for (Octants::Octant o = Octants::FIRST; o < Octants::NUMBER; o = Octants::Octant(o + 1))
 	{
 		Pt3D vertex = part->scale;
@@ -58,7 +60,7 @@
 		vertex.y *= Octants::isPositiveY(o) ? +1 : -1;
 		vertex.z *= Octants::isPositiveZ(o) ? +1 : -1;
-		
+
 		vertex = part->o.transform(vertex) + part->p;
-		
+
 		lowerBoundary.getMin(vertex);
 		upperBoundary.getMax(vertex);
@@ -71,11 +73,11 @@
 	Pt3D lowerBoundary, upperBoundary;
 	boundingBox(model, lowerBoundary, upperBoundary);
-	
+
 	MeshBuilder::BoundingBoxVolume iterator(density);
 	iterator.initialize(lowerBoundary, upperBoundary);
-	
+
 	Pt3D point;
 	int allPoints = 0, pointsInsideModel = 0;
-	
+
 	while (iterator.tryGetNext(point))
 	{
@@ -83,23 +85,46 @@
 		pointsInsideModel += GeometryUtils::isPointInsideModel(point, model) ? 1 : 0;
 	}
-	
+
 	double boundingBoxVolume
 		= (upperBoundary.x - lowerBoundary.x)
 		* (upperBoundary.y - lowerBoundary.y)
 		* (upperBoundary.z - lowerBoundary.z);
-	
+
 	return boundingBoxVolume * (double)pointsInsideModel / (double)allPoints;
 }
 
+ExtObject ModelGeometryInfo::getVoxels(Model& input_model, const double density)
+{
+	SolidsShapeTypeModel model(input_model);
+	Pt3D lowerBoundary, upperBoundary;
+	boundingBox(model, lowerBoundary, upperBoundary);
+
+	MeshBuilder::BoundingBoxVolume iterator(density);
+	iterator.initialize(lowerBoundary, upperBoundary);
+
+	Pt3D point;
+	VectorObject *voxels = new VectorObject;
+
+	while (iterator.tryGetNext(point))
+	{
+		if (GeometryUtils::isPointInsideModel(point, model))
+		{
+			voxels->data += new ExtValue(Pt3D_Ext::makeDynamicObject(point));
+		}
+	}
+
+	return voxels->makeObject();
+}
+
 double ModelGeometryInfo::area(Model &input_model, const double density)
 {
 	SolidsShapeTypeModel model(input_model);
 	double area = 0.0;
-	
-	for (int partIndex = 0; partIndex < model.getModel().getPartCount(); partIndex+=1)
+
+	for (int partIndex = 0; partIndex < model.getModel().getPartCount(); partIndex += 1)
 	{
 		area += externalAreaOfPart(model, partIndex, density);
 	}
-	
+
 	return area;
 }
@@ -110,12 +135,12 @@
 	switch (part->shape)
 	{
-		case Part::SHAPE_ELLIPSOID:
-			return externalAreaOfEllipsoid(model, partIndex, density);
-			
-		case Part::SHAPE_CUBOID:
-			return externalAreaOfCuboid(model, partIndex, density);
-			
-		case Part::SHAPE_CYLINDER:
-			return externalAreaOfCylinder(model, partIndex, density);
+	case Part::SHAPE_ELLIPSOID:
+		return externalAreaOfEllipsoid(model, partIndex, density);
+
+	case Part::SHAPE_CUBOID:
+		return externalAreaOfCuboid(model, partIndex, density);
+
+	case Part::SHAPE_CYLINDER:
+		return externalAreaOfCylinder(model, partIndex, density);
 	}
 	logPrintf("ModelGeometryInfo", "externalAreaOfPart", LOG_ERROR, "Part shape=%d not supported", part->shape);
@@ -127,5 +152,5 @@
 	Pt3D point;
 	int all = 0, sur = 0;
-	
+
 	while (surface.tryGetNext(point))
 	{
@@ -133,5 +158,5 @@
 		sur += GeometryUtils::isPointInsideModelExcludingPart(point, &model, partIndex) ? 0 : 1;
 	}
-	
+
 	return sur * area / all;
 }
@@ -140,10 +165,10 @@
 {
 	Part *part = model.getPart(partIndex);
-	
+
 	MeshBuilder::EllipsoidSurface ellipsoid(density);
 	ellipsoid.initialize(part);
-	
+
 	double area = GeometryUtils::ellipsoidArea(part->scale);
-	
+
 	return externalAreaOfSurface(model, partIndex, ellipsoid, area);
 }
@@ -152,21 +177,21 @@
 {
 	Part *part = model.getPart(partIndex);
-	
+
 	double externalArea = 0.0;
-	
+
 	MeshBuilder::RectangleSurface rectangle(density);
-	
-	for (CuboidFaces::Face f = CuboidFaces::FIRST; f < CuboidFaces::NUMBER; f = CuboidFaces::Face(f+1))
+
+	for (CuboidFaces::Face f = CuboidFaces::FIRST; f < CuboidFaces::NUMBER; f = CuboidFaces::Face(f + 1))
 	{
 		rectangle.initialize(part, f);
-		
+
 		double area = 4.0;
 		area *= !CuboidFaces::isX(f) ? part->scale.x : 1.0;
 		area *= !CuboidFaces::isY(f) ? part->scale.y : 1.0;
 		area *= !CuboidFaces::isZ(f) ? part->scale.z : 1.0;
-		
+
 		externalArea += externalAreaOfSurface(model, partIndex, rectangle, area);
 	}
-	
+
 	return externalArea;
 }
@@ -175,25 +200,25 @@
 {
 	Part *part = model.getPart(partIndex);
-	
+
 	double externalArea = 0.0;
-	
+
 	MeshBuilder::EllipseSurface ellipse(density);
-	
+
 	double area = M_PI * part->scale.y * part->scale.z;
-	
-	for (CylinderBases::Base b = CylinderBases::FIRST; b < CylinderBases::NUMBER; b = CylinderBases::Base(b+1))
+
+	for (CylinderBases::Base b = CylinderBases::FIRST; b < CylinderBases::NUMBER; b = CylinderBases::Base(b + 1))
 	{
 		ellipse.initialize(part, b);
-		
+
 		externalArea += externalAreaOfSurface(model, partIndex, ellipse, area);
 	}
-	
+
 	MeshBuilder::CylinderWallSurface cylinderWall(density);
 	cylinderWall.initialize(part);
-	
-	area = 2.0*part->scale.x * GeometryUtils::ellipsePerimeter(part->scale.y, part->scale.z);
-	
+
+	area = 2.0 * part->scale.x * GeometryUtils::ellipsePerimeter(part->scale.y, part->scale.z);
+
 	externalArea += externalAreaOfSurface(model, partIndex, cylinderWall, area);
-	
+
 	return externalArea;
 }
Index: cpp/frams/model/geometry/modelgeometryinfo.h
===================================================================
--- cpp/frams/model/geometry/modelgeometryinfo.h	(revision 1056)
+++ cpp/frams/model/geometry/modelgeometryinfo.h	(revision 1111)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -27,4 +27,5 @@
 	double volume(Model &model, const double density);
 	void findSizesAndAxes(Model &model, const double density, Pt3D &sizes, Orient &axes);
+	ExtObject getVoxels(Model &model, const double density);
 
 	void boundingBox(const Model &model, Pt3D &lowerBoundary, Pt3D &upperBoundary);
