Gamestudio Links
Zorro Links
Newest Posts
Blobsculptor tools and objects download here
by NeoDumont. 03/28/24 03:01
Issue with Multi-Core WFO Training
by aliswee. 03/24/24 20:20
Why Zorro supports up to 72 cores?
by Edgar_Herrera. 03/23/24 21:41
Zorro Trader GPT
by TipmyPip. 03/06/24 09:27
VSCode instead of SED
by 3run. 03/01/24 19:06
AUM Magazine
Latest Screens
The Bible Game
A psychological thriller game
SHADOW (2014)
DEAD TASTE
Who's Online Now
5 registered members (Quad, TipmyPip, degenerate_762, AndrewAMD, Nymphodora), 997 guests, and 5 spiders.
Key: Admin, Global Mod, Mod
Newest Members
sakolin, rajesh7827, juergen_wue, NITRO_FOREVER, jack0roses
19043 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
2d shadows #480005
05/12/20 22:27
05/12/20 22:27
Joined: May 2009
Posts: 5,370
Caucasus
3run Offline OP
Senior Expert
3run  Offline OP
Senior Expert

Joined: May 2009
Posts: 5,370
Caucasus
Hey! I've recently played around with shadertoy and converted some 2d shadows into HLSL grin
Maybe someone may find this useful! And if you guys have any ideas, how to convert any of those into 3d scene, share your ideas!

https://www.shadertoy.com/view/4dSXR1
Code
const float4 vecTime;
const float4 vecViewPort;
const float4 vecSkill1;

// Checks if spesific pixel is located within these bounds
float pixelInCube(float2 p, float4 t)
{
	return float(p.x > t.x-t.z*.5 && p.x < t.x+t.z*.5 && p.y > t.y-t.w*.5 && p.y < t.y+t.w*.5);
}

// Checks if spesific pixel is located within these bounds
float pixelInCircle(float2 p, float3 t)
{
	float hypT = pow(t.z*.5,2.)+pow(t.z*.5,2.);
	float hypP = pow(p.x-t.x,2.)+pow(p.y-t.y,2.);
	return float(hypP < hypT);
}

// Creates a cone for the light
float shadow_isInBounds(float2 p, float3 l, float4 t, float side){
	
	float ty = t.y - t.w*.5*(float(l.x > t.x)-.5)*2.*side;
	float tx = t.x - t.z*.5*(float(l.y < t.y)-.5)*2.*side;
	
	float rot = atan2(ty-l.y, tx-l.x)+45.;
	
	float lx = l.x + cos(rot) * l.z*side;
	float ly = l.y + sin(rot) * l.z*side;
	
	float angle = (ly-ty)/(lx-tx);
	float 	f = float(p.y > ly+((p.x-lx)*angle) && lx>tx);  
	f += float(p.y < ly+((p.x-lx)*angle) && lx<tx);
	
	return f;
}

// Culls the "front" of the cone so that only the "shadow" is visible
float shadow_cullLight(float f, float2 p, float3 l, float4 t){
	float ty = t.y - t.w*.5*(float(l.y > t.y)-.5)*2.;
	float tx = t.x - t.z*.5*(float(l.x > t.x)-.5)*2.;
	
	float c = 1.0;
	
	c*= float((p.y < ty && ty > l.y) || (p.y > ty && ty < l.y));
	c*= float((p.x < tx && tx > l.x) || (p.x > tx && tx < l.x));
	return clamp(f-c,0.0,1.0);
}

// Checks if spesific pixel is located within these bounds
float pixelInShadow(float2 p, float3 l, float4 t){
	return shadow_cullLight(shadow_isInBounds(p, l, t, 1.)-shadow_isInBounds(p, l, t, -1.),p,l,t);
}

float4 FP(float2 fragCoord: VPOS) : COLOR
{
	float4 color = float4(0.3, 0.3, 0.3, 0.3);
	float4 cube;
	float4 T = float4(fragCoord.xy, vecViewPort.xy);
	float3 light = float3(vecSkill1.x+float(vecSkill1.x == 0.0)*T.z*.4,vecSkill1.y+float(vecSkill1.y == 0.0)*T.w*.5,20.0);
	float time = vecTime.w * 0.01; // Controlls the rotationspeed of the circle
	
	color.rg += pixelInCircle(T.xy,light);
	float circleScale = 1.01;
	
	// Draw rotating circle
	for(int i = 0; i < 6; i+=1){
		cube = float4(cos(float(i)*circleScale+time)*T.w*.3+T.z*.7,sin(float(i)*circleScale+time)*T.w*.3+T.w*.5,40.0,40.0);
		color.r += pixelInCube(T.xy,cube);
		color -= pixelInShadow(T.xy,light,cube)*.95;
	}
	
	// Draw the corner objects
	for(int i = 0; i < 4; i+=1){
		cube = float4(fmod(floor(float(i)*.5),2.0)*-T.z*.8 + T.z*.9,fmod(float(i),2.0)*-T.w*.8 + T.w*.9,40.0,40.0);
		color.r += pixelInCube(T.xy,cube);
		color -= pixelInShadow(T.xy,light,cube)*.95;
	}
	
    // Draw the wall
    cube = float4(T.z*.2,0.0,10.0,T.w);
    color.r += pixelInCube(T.xy,cube);
    color -= pixelInShadow(T.xy,light,cube)*.95;
	
	float4 fragColor = color;
	return fragColor;
}

technique vhs
{
	pass one
	{
		PixelShader = compile ps_3_0 FP();
	}
}


https://www.shadertoy.com/view/4dfXDn

Code
/*
Hi all,

This is just my playground for a bunch of 2D stuff:

Some distance functions and blend functions
Cone marched 2D Soft shadows
Use the mouse to control the 3rd light
*/

const float4 vecTime;
const float4 vecViewPort;
const float4 vecSkill1;
const float TIME_FACTOR = 0.1;

//////////////////////////////////////
// Combine distance field functions //
//////////////////////////////////////

float smoothMerge(float d1, float d2, float k)
{
	float h = clamp(0.5 + 0.5*(d2 - d1)/k, 0.0, 1.0);
	return lerp(d2, d1, h) - k * h * (1.0-h);
}

float merge(float d1, float d2)
{
	return min(d1, d2);
}

float mergeExclude(float d1, float d2)
{
	return min(max(-d1, d2), max(-d2, d1));
}

float substract(float d1, float d2)
{
	return max(-d1, d2);
}

float intersect(float d1, float d2)
{
	return max(d1, d2);
}

//////////////////////////////
// Rotation and translation //
//////////////////////////////

float2 rotateCCW(float2 p, float a)
{
	float2x2 m = float2x2(cos(a), sin(a), -sin(a), cos(a));
	return mul(p, m);	
}

float2 rotateCW(float2 p, float a)
{
	float2x2 m = float2x2(cos(a), -sin(a), sin(a), cos(a));
	return mul(p, m);	
}

float2 translate(float2 p, float2 t)
{
	return p - t;
}

//////////////////////////////
// Distance field functions //
//////////////////////////////

float pie(float2 p, float angle)
{
	angle = radians(angle) / 2.0;
	float2 n = float2(cos(angle), sin(angle));
	return abs(p).x * n.x + p.y*n.y;
}

float circleDist(float2 p, float radius)
{
	return length(p) - radius;
}

float triangleDist(float2 p, float radius)
{
	return max(abs(p).x * 0.866025 + p.y * 0.5, -p.y) - radius * 0.5;
}

float triangleDist(float2 p, float width, float height)
{
	float2 n = normalize(float2(height, width / 2.0));
	return max(abs(p).x*n.x + p.y*n.y - (height*n.y), - p.y);
}

float semiCircleDist(float2 p, float radius, float angle, float width)
{
	width /= 2.0;
	radius -= width;
	return substract(pie(p, angle), abs(circleDist(p, radius)) - width);
}

float boxDist(float2 p, float2 size, float radius)
{
	size -= float2(radius, radius);
	float2 d = abs(p) - size;
	return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius;
}

float lineDist(float2 p, float2 start, float2 end, float width)
{
	float2 dir = start - end;
	float lngth = length(dir);
	dir /= lngth;
	float2 proj = max(0.0, min(lngth, dot((start - p), dir))) * dir;
	return length((start - p) - proj) - (width / 2.0);
}

///////////////////////
// Masks for drawing //
///////////////////////

float fillMask(float dist)
{
	return clamp(-dist, 0.0, 1.0);
}

float innerBorderMask(float dist, float width)
{
	//dist += 1.0;
	float alpha1 = clamp(dist + width, 0.0, 1.0);
	float alpha2 = clamp(dist, 0.0, 1.0);
	return alpha1 - alpha2;
}

float outerBorderMask(float dist, float width)
{
	//dist += 1.0;
	float alpha1 = clamp(dist, 0.0, 1.0);
	float alpha2 = clamp(dist - width, 0.0, 1.0);
	return alpha1 - alpha2;
}

///////////////
// The scene //
///////////////

float sceneDist(float2 p)
{
	float c = circleDist(translate(p, float2(100, 250)), 40.0);
	float b1 =  boxDist(translate(p, float2(200, 250)), float2(40, 40), 0.0);
	float b2 =  boxDist(translate(p, float2(300, 250)), float2(40, 40), 10.0);
	float l = lineDist(p, float2(370, 220), float2(430, 280), 10.0);
	float t1 = triangleDist(translate(p, float2(500, 210)), 80.0, 80.0);
	float t2 = triangleDist(rotateCW(translate(p, float2(600, 250)), vecTime.w * TIME_FACTOR), 40.0);
	
	float m = merge(c, b1);
	m = merge(m, b2);
	m = merge(m, l);
	m = merge(m, t1);
	m = merge(m, t2);
	
	float b3 = boxDist(translate(p, float2(100, sin((vecTime.w * TIME_FACTOR) * 3.0 + 1.0) * 40.0 + 100.0)), float2(40, 15), 0.0);
	float c2 = circleDist(translate(p, float2(100, 100)), 30.0);
	float s = substract(b3, c2);
	
	float b4 = boxDist(translate(p, float2(200, sin((vecTime.w * TIME_FACTOR) * 3.0 + 2.0) * 40.0 + 100.0)), float2(40, 15), 0.0);
	float c3 = circleDist(translate(p, float2(200, 100)), 30.0);
	float i = intersect(b4, c3);
	
	float b5 = boxDist(translate(p, float2(300, sin((vecTime.w * TIME_FACTOR) * 3.0 + 3.0) * 40.0 + 100.0)), float2(40, 15), 0.0);
	float c4 = circleDist(translate(p, float2(300, 100)), 30.0);
	float a = merge(b5, c4);
	
	float b6 = boxDist(translate(p, float2(400, 100)), float2(40, 15), 0.0);
	float c5 = circleDist(translate(p, float2(400, 100)), 30.0);
	float sm = smoothMerge(b6, c5, 10.0);
	
	float sc = semiCircleDist(translate(p, float2(500,100)), 40.0, 90.0, 10.0);
	
	float b7 = boxDist(translate(p, float2(600, sin((vecTime.w * TIME_FACTOR) * 3.0 + 3.0) * 40.0 + 100.0)), float2(40, 15), 0.0);
	float c6 = circleDist(translate(p, float2(600, 100)), 30.0);
	float e = mergeExclude(b7, c6);
	
	m = merge(m, s);
	m = merge(m, i);
	m = merge(m, a);
	m = merge(m, sm);
	m = merge(m, sc);
	m = merge(m, e);
	
	return m;
}

float sceneSmooth(float2 p, float r)
{
	float accum = sceneDist(p);
	accum += sceneDist(p + float2(0.0, r));
	accum += sceneDist(p + float2(0.0, -r));
	accum += sceneDist(p + float2(r, 0.0));
	accum += sceneDist(p + float2(-r, 0.0));
	return accum / 5.0;
}

//////////////////////
// Shadow and light //
//////////////////////

float shadow(float2 p, float2 pos, float radius)
{
	float2 dir = normalize(pos - p);
	float dl = length(p - pos);
	
	// fraction of light visible, starts at one radius (second half added in the end);
	float lf = radius * dl;
	
	// distance traveled
	float dt = 0.01;

	for(int i = 0; i < 64; ++i)
	{				
		// distance to scene at current position
		float sd = sceneDist(p + dir * dt);
		
		// early out when this ray is guaranteed to be full shadow
		if (sd < -radius)
		{
			return 0.0;
		}
		
		// width of cone-overlap at light
		// 0 in center, so 50% overlap: add one radius outside of loop to get total coverage
		// should be '(sd / dt) * dl', but '*dl' outside of loop
		lf = min(lf, sd / dt);
		
		// move ahead
		dt += max(1.0, abs(sd));
		if (dt > dl)
		{
			break;
		}
	}

	// multiply by dl to get the real projected overlap (moved out of loop)
	// add one radius, before between -radius and + radius
	// normalize to 1 ( / 2*radius)
	lf = clamp((lf * dl + radius) / (2.0 * radius), 0.0, 1.0);
	lf = smoothstep(0.0, 1.0, lf);
	return lf;
}

float4 drawLight(float2 p, float2 pos, float4 color, float dist, float range, float radius)
{
	// distance to light
	float ld = length(p - pos);
	
	// out of range
	if (ld > range)
	{
		return float4(0.0, 0.0, 0.0, 0.0);
	}
	
	// shadow and falloff
	float shad = shadow(p, pos, radius);
	float fall = (range - ld)/range;
	fall *= fall;
	float source = fillMask(circleDist(p - pos, radius));
	return (shad * fall + source) * color;
}

float luminance(float4 col)
{
	return 0.2126 * col.r + 0.7152 * col.g + 0.0722 * col.b;
}

void setLuminance(inout float4 col, float lum)
{
	lum /= luminance(col);
	col *= lum;
}

float AO(float2 p, float dist, float radius, float intensity)
{
	float a = clamp(dist / radius, 0.0, 1.0) - 1.0;
	return 1.0 - (pow(abs(a), 5.0) + 1.0) * intensity + (1.0 - intensity);
	return smoothstep(0.0, 1.0, dist / radius);
}

/////////////////
// The program //
/////////////////

float4 FP(float2 fragCoord: VPOS) : COLOR
{
	float2 p = fragCoord.xy + float2(0.5, 0.5);
	float2 c = vecViewPort.xy / 2.0;
	
	//float dist = sceneSmooth(p, 5.0);
	float dist = sceneDist(p);
	
	float2 light1Pos = vecSkill1.xy;
	float4 light1Col = float4(0.75, 1.0, 0.5, 1.0);
	setLuminance(light1Col, 0.4);
	
	float2 light2Pos = float2(vecViewPort.x * (sin((vecTime.w * TIME_FACTOR) + 3.1415) + 1.2) / 2.4, 175.0);
	float4 light2Col = float4(1.0, 0.75, 0.5, 1.0);
	setLuminance(light2Col, 0.5);
	
	float2 light3Pos = float2(vecViewPort.x * (sin((vecTime.w * TIME_FACTOR)) + 1.2) / 2.4, 340.0);
	float4 light3Col = float4(0.5, 0.75, 1.0, 1.0);
	setLuminance(light3Col, 0.6);
	
	// gradient
	float4 col = float4(0.5, 0.5, 0.5, 1.0) * (1.0 - length(c - p) / vecViewPort.x);
	// grid
	col *= clamp(min(fmod(p.y, 10.0), fmod(p.x, 10.0)), 0.9, 1.0);
	// ambient occlusion
	col *= AO(p, sceneSmooth(p, 10.0), 40.0, 0.4);
	//col *= 1.0-AO(p, sceneDist(p), 40.0, 1.0);
	// light
	col += drawLight(p, light1Pos, light1Col, dist, 150.0, 6.0);
	col += drawLight(p, light2Pos, light2Col, dist, 200.0, 8.0);
	col += drawLight(p, light3Pos, light3Col, dist, 300.0, 12.0);
	// shape fill
	col = lerp(col, float4(1.0, 0.4, 0.0, 1.0), fillMask(dist));
	// shape outline
	col = lerp(col, float4(0.1, 0.1, 0.1, 1.0), innerBorderMask(dist, 1.5));
	
	float4 fragColor = col;
	return fragColor;
}

technique vhs
{
	pass one
	{
		PixelShader = compile ps_3_0 FP();
	}
}



https://www.shadertoy.com/view/4sKBRD

Code
const float4 vecTime;
const float4 vecViewPort;
const float4 vecSkill1;
const float TIME_FACTOR = 0.1;

//spheres and coloured lights from an orthographic projection, with analytical antialiasing and shadows.
#define NUMBER_OF_SPHERES   20
#define NUMBER_OF_LIGHTS    7
#define SURFACE_BRIGHTNESS	0.01

struct light
{
	float3 pos;
	float3 col;
};

//hsl to rgb conversion adapted from https://gist.github.com/mjackson/5311256
float hue2rgb(float p, float q, float h)
{
	h = frac(h);
	if(h<0.1616) return p+(q-p)*6.0*h;
	else if(h<0.5) return q;
	else if(h<0.666) return p+(q-p)*(0.666-h)*6.0;
	else return p;
}

float3 hsltorgb(float3 hsl)
{
	float h = hsl.x;
	float s = hsl.y;
	float l = hsl.z;
	float q = l<0.5 ? l*(1.0+s) : l+s-l*s;
	float p = 2.0*l-q;
	float r = hue2rgb(p, q, h+0.333);
	float g = hue2rgb(p, q, h);
	float b = hue2rgb(p, q, h-0.333);
	return float3(r,g,b);
}

// https://www.shadertoy.com/view/4djSRW
float3 hash(float p)
{
	float3 p3 = frac(float3(p, p, p) * float3(0.1031, 0.1030, 0.0973));
	p3 += dot(p3, p3.yzx+19.19);
	return frac((p3.xxy+p3.yzz)*p3.zyx); 
}

float3 getnearestpointonlinesegment(float3 pa, float3 pb, float3 t)
{
	float3 l = pb-pa;
	float h = clamp(dot(t-pa,l)/dot(l,l), 0.0, 1.0 );	
	return pa+l*h;
}

float2 orbit(float r, float s, float p)
{
	r *= sin((vecTime.w * TIME_FACTOR) * p);
	s *= (vecTime.w * TIME_FACTOR);
	return float2( sin(s)*r, cos(s)*r );
}

float distancetocoverage(float d)
{
	return clamp(d*vecViewPort.y, 0.0, 1.0);
}


float circle(float2 uv, float2 p, float r)
{
	float d = r-distance(uv,p);
	return distancetocoverage(d);
}


float sphereshadow(float3 uv, float3 lp, float3 cp, float cr)
{
	float3 np = getnearestpointonlinesegment(uv, lp, cp);
	float d = cr-distance(np,cp);
	return distancetocoverage(d);
}

float4 FP(float2 fragCoord: VPOS) : COLOR
{
	float2 uv = fragCoord / vecViewPort.y;
	
	float2 center = float2(0.5*(vecViewPort.x / vecViewPort.y), 0.5);
	
	float3 spheres[NUMBER_OF_SPHERES];
	for (int i = 0; i<NUMBER_OF_SPHERES; i++ )
	{
		float3 h = hash(float(i+500));
		float px = h.x * vecViewPort.x/vecViewPort.y;
		float py = h.y;
		float r = lerp(0.025, 0.1, h.z);
		spheres[i] = float3( px, py, r );
	}
	
	light lights[NUMBER_OF_LIGHTS];
	for (int i = 0; i < NUMBER_OF_LIGHTS; i++)
	{
		float3 h = hash(float(i+200));
		float d = lerp(0.25, 0.8, h.x);								//orbital distance
		float s = lerp(0.12, 0.5, abs(h.y*2.0-1.0))*sign(h.y-0.5);	//orbital speed
		float p = lerp(0.1, 0.35, h.z);								//occilation speed
		lights[i].pos = float3(center+orbit(d,s,p), 0.101+sin((vecTime.w * TIME_FACTOR)+float(i))*0.1);
		float3 hsl = float3( (float(i)+(vecTime.w * TIME_FACTOR)*0.5)/float(NUMBER_OF_LIGHTS), 0.95, 0.2 );
		lights[i].col = hsltorgb( hsl );
	}
	
	if(vecSkill1.w > 0.0) lights[0].pos.xy = vecSkill1.xy / vecViewPort.y;
	
	float3 col = float3(0.0, 0.0, 0.0);
	
	int id = -1; // index of sphere under pixel (-1 if there is none)
	float pz = 0.0; // height of pixel above floor plane
	for(int i = 0; i<NUMBER_OF_SPHERES; i++)
	{
		float2 sr = spheres[i].xy-uv;
		float cz = spheres[i].z * spheres[i].z - dot(sr, sr); // (pixel height on spheres[i]) ^ 2
		if(cz > pz * pz)
		{
			pz = sqrt(cz);
			id = i;
		}
	}
	
	float coverage = 0.0; // sphere coverage of pixel
	float3 nor = float3(0.0, 0.0, 1.0); // surface normal
	if(id != -1)
	{
		coverage = circle(uv, spheres[id].xy, spheres[id].z);
		nor = normalize(float3(uv-spheres[id].xy, pz));
	}
	
	for(int i = 0; i<NUMBER_OF_LIGHTS; i++)
	{      
		// get shadow coverage
		float illumination = 1.0;
		for(int j = 0; j<NUMBER_OF_SPHERES && illumination>0.0; j++)
		{
			illumination *= 1.0 - sphereshadow(float3(uv,pz), lights[i].pos, float3(spheres[j].xy,0.0), spheres[j].z);
		}
		
		float3 rv = lights[i].pos-float3(uv,pz);
		float3 rd = normalize(rv); // light ray direction (from surface to light)
		float gr = max(rd.z,0.0) * (1.0-sphereshadow(float3(uv,0.0), lights[i].pos, float3(spheres[id].xy,0.0), spheres[id].z)); // Lambertian reflection from ground plane
		float sr = max(dot(rd, nor),0.0) * coverage; // Lambertian reflection from sphere surface
		
		float f = gr + sr;
		f *= illumination * SURFACE_BRIGHTNESS / dot(rv,rv);
		col += f * lights[i].col;
	}
	
	col *= 2.0;
	
	//filmic tone mapping -- http://filmicworlds.com/blog/filmic-tonemapping-operators/
	col = max(col-0.004, 0.0);
	col = (col*(6.2*col+0.5))/(col*(6.2*col+1.7)+0.06);
	
	col += (hash(uv.x*uv.y*(vecTime.w * TIME_FACTOR)*1000.0)-0.5) / 128.0;		//grain
	
	float4 fragColor = float4(col,1.0);
	return fragColor;
}

technique vhs
{
	pass one
	{
		PixelShader = compile ps_3_0 FP();
	}
}


Some of them take some time to get compiled, and all of them are pp shaders.

Best regards!


Looking for free stuff?? Take a look here: http://badcom.at.ua
Support me on: https://boosty.to/3rung
Re: 2d shadows [Re: 3run] #480147
05/22/20 03:51
05/22/20 03:51
Joined: May 2005
Posts: 868
Chicago, IL
Dooley Offline
User
Dooley  Offline
User

Joined: May 2005
Posts: 868
Chicago, IL
Wow, looks great!

Re: 2d shadows [Re: 3run] #480148
05/22/20 03:54
05/22/20 03:54
Joined: May 2005
Posts: 868
Chicago, IL
Dooley Offline
User
Dooley  Offline
User

Joined: May 2005
Posts: 868
Chicago, IL
Do these have anything to do with 3D Gamestudio in particular, or can they be used in any setting?

Re: 2d shadows [Re: 3run] #480156
05/22/20 09:02
05/22/20 09:02
Joined: May 2009
Posts: 5,370
Caucasus
3run Offline OP
Senior Expert
3run  Offline OP
Senior Expert

Joined: May 2009
Posts: 5,370
Caucasus
Originally Posted by 3run
And if you guys have any ideas, how to convert any of those into 3d scene, share your ideas!
It's just a shader, converted from GLSL to HLSL. Unfortunately I don't have enough knowledge, to change/adapt this code, in order to use it with real level environments, but I hope it's possible. These shaders reminded me of a project, made by Hummel (if I'm not mistaken), he made 2D shadows demo back in the times, but you had to place walls by yourself, in order to get shadows and it only could be used in 2D. I think it's the same thing here too, those shaders need some work to get them working in 3dgs.


Looking for free stuff?? Take a look here: http://badcom.at.ua
Support me on: https://boosty.to/3rung
Re: 2d shadows [Re: 3run] #480165
05/22/20 15:11
05/22/20 15:11
Joined: Feb 2011
Posts: 124
Germany Nrw Herford
HenWoll Offline
Member
HenWoll  Offline
Member

Joined: Feb 2011
Posts: 124
Germany Nrw Herford
HOLY SHIT ! THIS IS AWESOME !


Moderated by  Blink, Hummel, Superku 

Gamestudio download | chip programmers | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1