// license:BSD-3-Clause
// copyright-holders:Brad Oliver,Aaron Giles,Bernd Wiebelt,Allard van der Bas
/******************************************************************************
*
* vector.c
*
* anti-alias code by Andrew Caldwell
* (still more to add)
*
* 040227 Fixed miny clip scaling which was breaking in mhavoc. AREK
* 010903 added support for direct RGB modes MLR
* 980611 use translucent vectors. Thanks to Peter Hirschberg
* and Neil Bradley for the inspiration. BW
* 980307 added cleverer dirty handling. BW, ASG
* fixed antialias table .ac
* 980221 rewrote anti-alias line draw routine
* added inline assembly multiply fuction for 8086 based machines
* beam diameter added to draw routine
* beam diameter is accurate in anti-alias line draw (Tcosin)
* flicker added .ac
* 980203 moved LBO's routines for drawing into a buffer of vertices
* from avgdvg.c to this location. Scaling is now initialized
* by calling vector_init(...). BW
* 980202 moved out of msdos.c ASG
* 980124 added anti-alias line draw routine
* modified avgdvg.c and sega.c to support new line draw routine
* added two new tables Tinten and Tmerge (for 256 color support)
* added find_color routine to build above tables .ac
*
* Vector Team
*
* Brad Oliver
* Aaron Giles
* Bernd Wiebelt
* Allard van der Bas
* Al Kossow (VECSIM)
* Hedley Rainnie (VECSIM)
* Eric Smith (VECSIM)
* Neil Bradley (technical advice)
* Andrew Caldwell (anti-aliasing)
*
**************************************************************************** */
#include "emu.h"
#include "emuopts.h"
#include "rendutil.h"
#include "vector.h"
#define VECTOR_WIDTH_DENOM 512
#define MAX_POINTS 10000
float vector_options::s_flicker = 0.0f;
float vector_options::s_beam_width_min = 0.0f;
float vector_options::s_beam_width_max = 0.0f;
float vector_options::s_beam_intensity_weight = 0.0f;
void vector_options::init(emu_options& options)
{
s_beam_width_min = options.beam_width_min();
s_beam_width_max = options.beam_width_max();
s_beam_intensity_weight = options.beam_intensity_weight();
s_flicker = options.flicker();
}
// device type definition
DEFINE_DEVICE_TYPE(VECTOR, vector_device, "vector_device", "VECTOR")
vector_device::vector_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, VECTOR, tag, owner, clock),
device_video_interface(mconfig, *this),
m_vector_list(nullptr),
m_min_intensity(255),
m_max_intensity(0)
{
}
void vector_device::device_start()
{
vector_options::init(machine().options());
m_vector_index = 0;
/* allocate memory for tables */
m_vector_list = make_unique_clear<point[]>(MAX_POINTS);
}
/*
* www.dinodini.wordpress.com/2010/04/05/normalized-tunable-sigmoid-functions/
*/
float vector_device::normalized_sigmoid(float n, float k)
{
// valid for n and k in range of -1.0 and 1.0
return (n - n * k) / (k - fabs(n) * 2.0f * k + 1.0f);
}
/*
* Adds a line end point to the vertices list. The vector processor emulation
* needs to call this.
*/
void vector_device::add_point(int x, int y, rgb_t color, int intensity)
{
point *newpoint;
intensity = std::max(0, std::min(255, intensity));
m_min_intensity = intensity > 0 ? std::min(m_min_intensity, intensity) : m_min_intensity;
m_max_intensity = intensity > 0 ? std::max(m_max_intensity, intensity) : m_max_intensity;
if (vector_options::s_flicker && (intensity > 0))
{
float random = (float)(machine().rand() & 255) / 255.0f; // random value between 0.0 and 1.0
intensity -= (int)(intensity * random * vector_options::s_flicker);
intensity = std::max(0, std::min(255, intensity));
}
newpoint = &m_vector_list[m_vector_index];
newpoint->x = x;
newpoint->y = y;
newpoint->col = color;
newpoint->intensity = intensity;
m_vector_index++;
if (m_vector_index >= MAX_POINTS)
{
m_vector_index--;
logerror("*** Warning! Vector list overflow!\n");
}
}
/*
* The vector CPU creates a new display list. We save the old display list,
* but only once per refresh.
*/
void vector_device::clear_list(void)
{
m_vector_index = 0;
}
uint32_t vector_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
uint32_t flags = PRIMFLAG_ANTIALIAS(1) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD) | PRIMFLAG_VECTOR(1);
const rectangle &visarea = screen.visible_area();
float xscale = 1.0f / (65536 * visarea.width());
float yscale = 1.0f / (65536 * visarea.height());
float xoffs = (float)visarea.min_x;
float yoffs = (float)visarea.min_y;
point *curpoint;
int lastx = 0;
int lasty = 0;
curpoint = m_vector_list.get();
screen.container().empty();
screen.container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(0xff,0x00,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_VECTORBUF(1));
for (int i = 0; i < m_vector_index; i++)
{
render_bounds coords;
float intensity = (float)curpoint->intensity / 255.0f;
float intensity_weight = normalized_sigmoid(intensity, vector_options::s_beam_intensity_weight);
// check for static intensity
float beam_width = m_min_intensity == m_max_intensity
? vector_options::s_beam_width_min
: vector_options::s_beam_width_min + intensity_weight * (vector_options::s_beam_width_max - vector_options::s_beam_width_min);
// normalize width
beam_width *= 1.0f / (float)VECTOR_WIDTH_DENOM;
coords.x0 = ((float)lastx - xoffs) * xscale;
coords.y0 = ((float)lasty - yoffs) * yscale;
coords.x1 = ((float)curpoint->x - xoffs) * xscale;
coords.y1 = ((float)curpoint->y - yoffs) * yscale;
if (curpoint->intensity != 0)
{
screen.container().add_line(
coords.x0, coords.y0, coords.x1, coords.y1,
beam_width,
(curpoint->intensity << 24) | (curpoint->col & 0xffffff),
flags);
}
lastx = curpoint->x;
lasty = curpoint->y;
curpoint++;
}
return 0;
}