Code:
flt GetDist(C Box &box)
{
return Max(box.w()/(2*Tan(D.viewFovX()/2)), box.h()/(2*Tan(D.viewFovY()/2))) + box.d()/2;
}
static void Render() {IconEdit.render();}
void render()
{
switch(Renderer())
{
case RM_PREPARE:
{
if(obj)if(obj->mesh())obj->mesh()->draw(matrix());
LightDir(Matrix3().setRotateXY(settings.light0_angle.y, settings.light0_angle.x+PI).z, settings.light0_col).add(settings.shadows);
LightDir(Matrix3().setRotateXY(settings.light1_angle.y, settings.light1_angle.x+PI).z, settings.light1_col).add(settings.shadows);
}break;
case RM_SHADOW:
{
if(obj)if(obj->mesh())obj->mesh()->drawShadow(matrix());
}break;
}
}
Matrix matrix() {return Matrix().setScale(settings.scale);}
Box box () {return (obj && obj->mesh()) ? obj->mesh()->box : 0;}
flt getDist() // !! assumes that viewport and obj are already set !!
{
if(!settings.auto_center)return 1; // we need constant camera distance when auto centering is disabled
return GetDist(box());
}
static void Draw(Viewport &viewport) {IconEdit.draw();}
void draw(bool final=false)
{
MOTION_MODE motion =D.motionMode (); D. motionMode (MOTION_NONE);
AMBIENT_MODE ambient =D.ambientMode (); D.ambientMode (AMBIENT_ULTRA);
BUMP_MODE bump =D.bumpMode (); if(final && bump<BUMP_PARALLAX)D.bumpMode(BUMP_PARALLAX);
EDGE_SOFTEN_MODE edge =D.edgeSoftenMode (); D.edgeSoftenMode (EDGE_SOFTEN_MLAA);
Vec ambient_col =D.ambientColor (); D.ambientColor (settings.ambient_col);
flt ambient_contr=D.ambientContrast(); D.ambientContrast(settings.ambient_occl);
flt ambient_range=D.ambientRange (); D.ambientRange (settings.ambient_range);
bool ambient_half =D.ambientHalfRes (); D.ambientHalfRes (false);
flt lod_fac =D.lodFactor (); D.lodFactor (0);
DOF_MODE dof =D.dofMode (); D.dofMode (DOF_NONE);
bool bloom_over =D.bloomOverbright();
bool bloom_half =D.bloomHalf (), shadow_jitter=D.shadowJitter();
byte bloom_blurs =D.bloomBlurs (), shadow_soft =D.shadowSoft (), shd_map_num=D.shadowMapNum();
bool hdr =Renderer.use_hdr; Renderer.use_hdr=false;
bool astros =AstrosDraw ; AstrosDraw =false;
bool ocean =Water.draw ; Water.draw =false;
flt view_from=D.viewFrom(), view_range=D.viewRange(), view_fov=D.viewFov();
VecI2 view_size;
Sky.clear();
D.bloomOriginal(settings.bloom_original).bloomScale(settings.bloom_scale).bloomCut(settings.bloom_cut).bloomContrast(settings.bloom_contrast);
D.bloomBlurs(0).bloomHalf(true).bloomOverbright(false);
D.shadowSoft(1).shadowJitter(true).shadowMapNum(6);
// set object first
UID obj_id=UIDZero; if(ElmIcon *data=T.data())obj_id=data.obj_id; T.obj=(obj_id.valid() ? Proj.gamePath(obj_id) : S);
// set viewport
VecI2 size(settings.width, settings.height);
Rect rect=D.viewRect();
Vec2 rect_size=rect.size();
if(final)
{
D.viewForceSquarePixel(true);
view_size=size; VecI2 screen_size(D.x(), D.y());
if(view_size.x>screen_size.x)view_size=Round(view_size*(flt(screen_size.x)/view_size.x)); // fit X
if(view_size.y>screen_size.y)view_size=Round(view_size*(flt(screen_size.y)/view_size.y)); // fit Y
REP(8) // super sample
{
VecI2 super_size=view_size*2;
if( super_size.x<=screen_size.x && super_size.y<=screen_size.y)view_size=super_size;else break;
}
D.viewRect(&RectI(VecI2(0), view_size));
}else
if(no_scale)
{
Vec2 view_size=D.pixelToScreenSize(size);
if(view_size.x>rect_size.x)view_size*=rect_size.x/view_size.x; // fit X
if(view_size.y>rect_size.y)view_size*=rect_size.y/view_size.y; // fit Y
D.viewRect(Rect_C(rect.center(), view_size));
}else
{
Vec2 view_size=size*rect_size.y/size.y; // scale to fit Y
if(view_size.x>rect_size.x)view_size*=rect_size.x/view_size.x; // fit X
D.viewRect(Rect_C(rect.center(), view_size));
}
D.viewFov (settings.fov); // set fov first
D.viewRange(Max(0.1, getDist()*5)); // 5 is needed because of 64x320 icons with low FOV
D.viewFrom (D.viewRange()*0.01);
// now after obj and viewport
Cam.at=settings.cam_focus;
if(settings.auto_center)Cam.at+=(box()*matrix()).center();
Cam.setSpherical(Cam.at, settings.cam_angle.x+PI, settings.cam_angle.y, settings.cam_angle.z, getDist()).set();
if(!final)Renderer(IconEditor.Render);else
if(ElmIcon *data=T.data())
{
Image col, soft;
D.edgeSoftenMode(EDGE_SOFTEN_NONE); Renderer(IconEditor.Render); Renderer.capture(col , -1, -1, IMAGE_B8G8R8A8, IMAGE_SOFT, 1, true );
D.edgeSoftenMode(EDGE_SOFTEN_MLAA); Renderer(IconEditor.Render); Renderer.capture(soft, -1, -1, IMAGE_B8G8R8A8, IMAGE_SOFT, 1, false); // skip alpha for soft to improve performance
col .crop(col , 0, 0, 0, view_size.x, view_size.y, 1);
soft.crop(soft, 0, 0, 0, view_size.x, view_size.y, 1);
// copy alpha from 'col' to 'soft
REPD(y, col.y())
REPD(x, col.x())
{
Color c=soft.color(x, y); c.a=col.color(x, y).a; soft.color(x, y, c);
}
// !! following code assumes that background is black !!
// setup anti-aliasing according to soft (soft will be darker on edges because it will be softened with black background)
REPD(y, col.y())
REPD(x, col.x())
{
Color c=col.color(x, y), s=soft.color(x, y);
if(s.a) // fully opaque
{
if(c.r!=s.r || c.g!=s.g || c.b!=s.b) // if different (was affected by softening)
{
for(int sy=y-1; sy<=y+1; sy++)if(InRange(sy, col.y()))
for(int sx=x-1; sx<=x+1; sx++)if(InRange(sx, col.x()))if(sx || sy)if(!soft.color(sx, sy).a) // if at least one neighbor is transparent (so we won't adjust opacity of softened pixels which are "inside" the object)
{
int c_lum=c.r+c.g+c.b, s_lum=s.r+s.g+s.b;
if( c_lum)
{
c.a=Mid(c.a*s_lum/c_lum, 0, 255);
col.color(x, y, c);
}
goto found_transparent;
}
col.color(x, y, s); // copy color from soft (because this is softened surrounded by object)
found_transparent:;
}
}else // transparent
{
int c_lum=Max(c.r, c.g, c.b), // generated from bloom blur or blend material
s_lum=Max(s.r, s.g, s.b); // generated from softening
if(s_lum>c_lum+3) // softening
{
// find max luminance of nearby opaque colors
c_lum=0;
for(int sy=y-1; sy<=y+1; sy++)if(InRange(sy, col.y()))
for(int sx=x-1; sx<=x+1; sx++)if(InRange(sx, col.x()))if(sx || sy)
{
Color n=soft.color(sx, sy); if(n.a) // fully opaque neighbor
{
MAX(c_lum, Max(n.r, n.g, n.b));
}
}
if(c_lum)
{
s=ColorBrightness(s, Min(8, flt(c_lum)/s_lum));
s.a=255*s_lum/c_lum;
col.color(x, y, s);
}
}else
if(c_lum) // bloom blur / blend material
{
c.a=c_lum;
MAX(c_lum, 32); // limit brightness adjustment because without this artifacts could occur (especially visible when drawing stretched and with compression enabled)
c=ColorBrightness(c, 255.0/c_lum); // increase brightness
col.color(x, y, c);
}
}
}
REP(8)if(col.x()/2>=settings.width && col.y()/2>=settings.height)col.downSample(true);
col.resize(settings.width, settings.height, FILTER_BICUBIC); // resize in case source is smaller than target
// here save/copy 'col'
}
D.viewForceSquarePixel(false).viewFrom(view_from).viewRange(view_range).viewFov(view_fov);
D.bloomBlurs(bloom_blurs).bloomHalf(bloom_half).bloomOverbright(bloom_over);
D.shadowSoft(shadow_soft).shadowJitter(shadow_jitter).shadowMapNum(shd_map_num);
D. lodFactor(lod_fac);
D. dofMode(dof );
D.edgeSoftenMode(edge);
D. bumpMode(bump );
D.ambientMode(ambient).ambientColor(ambient_col).ambientContrast(ambient_contr).ambientRange(ambient_range).ambientHalfRes(ambient_half);
D. motionMode(motion );
Renderer.use_hdr=hdr;
AstrosDraw =astros;
Water.draw =ocean;