
/****************************************************************************
 *                                                                         
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Appearance.c
 *                                                                         
 * Purpose : Converts appearance nodes
 *
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "rpplugin.h"
#include "appearance.h"
#include "namegen.h"
#include "texturetransform.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "material.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: appearance.c,v 1.50 2001/02/05 11:50:43 johns Exp $";

/****************************************************************************
 Local (Static) Prototypes
 */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Appearance methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static RwImage     *
Appearance_SfImageToRwImage(sfimage * image)
{
    RWFUNCTION(RWSTRING("Appearance_SfImageToRwImage"));
    RWASSERT(image);

    if (image)
    {
        RwUInt8            *srcPixels = image->pixels;
        RwUInt8            *dstPixels;
        RwImage            *outImage;
        RwInt32             stride;
        RwInt32             width;
        RwInt32             height;

        width = (RwInt32) image->width;
        height = (RwInt32) image->height;

        outImage = RwImageCreate(width, height, 32);
        if (!outImage)
        {
            RWRETURN((RwImage *)NULL);
        }

        if (!RwImageAllocatePixels(outImage))
        {
            RwImageDestroy(outImage);

            RWRETURN((RwImage *)NULL);
        }

        stride = RwImageGetStride(outImage);
        dstPixels = RwImageGetPixels(outImage);
        /* filling from the bottom up */
        dstPixels = dstPixels + ((height - 1) * stride);
        if (!dstPixels)
        {
            RwImageDestroy(outImage);

            RWRETURN((RwImage *)NULL);
        }

        switch (image->components)
        {
            case 1:
                {
                    RwInt32             h, w;

                    for (h = 0; h < image->height; h++)
                    {
                        RwUInt8            *scanline = dstPixels;

                        for (w = 0; w < image->width; w++)
                        {
                            *dstPixels++ = *srcPixels;
                            *dstPixels++ = *srcPixels;
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = 0xFF;
                        }
                        dstPixels = scanline - stride;
                    }
                    break;
                }
            case 2:
                {
                    RwInt32             h, w;

                    for (h = 0; h < image->height; h++)
                    {
                        RwUInt8            *scanline = dstPixels;

                        for (w = 0; w < image->width; w++)
                        {
                            *dstPixels++ = *srcPixels;
                            *dstPixels++ = *srcPixels;
                            *dstPixels++ = *srcPixels;
                            *dstPixels++ = *srcPixels++;
                        }
                        dstPixels = scanline - stride;
                    }
                    break;
                }
            case 3:
                {
                    RwInt32             h, w;

                    for (h = 0; h < image->height; h++)
                    {
                        RwUInt8            *scanline = dstPixels;

                        for (w = 0; w < image->width; w++)
                        {
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = 0xFF;
                        }
                        dstPixels = scanline - stride;
                    }
                    break;
                }
            case 4:
                {
                    RwInt32             h, w;

                    for (h = 0; h < image->height; h++)
                    {
                        RwUInt8            *scanline = dstPixels;

                        for (w = 0; w < image->width; w++)
                        {
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = *srcPixels++;
                            *dstPixels++ = *srcPixels++;
                        }
                        dstPixels = scanline - stride;
                    }
                    break;
                }
            default:
                {
                    RWRETURN(FALSE);
                }
        }

        /* resample the image regardless */
        {
            RwImage            *reSampImage;

            reSampImage = RwImageCreateResample(outImage, 128, 128);
            RwImageDestroy(outImage);
            if (!reSampImage)
            {
                RWRETURN((RwImage *)NULL);
            }
            outImage = reSampImage;
        }

        RWRETURN(outImage);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RwTexture   *
Appearance_SfImageToRwTexture(sfimage * image)
{
    RWFUNCTION(RWSTRING("Appearance_SfImageToRwTexture"));
    RWASSERT(image);

    if (image)
    {
        RwImage            *outImage;
        RwRaster           *raster;
        RwTexture          *texture;

        outImage = Appearance_SfImageToRwImage(image);
        if (!outImage)
        {
            RWRETURN(FALSE);
        }

        /* create a suitably sized raster (use default depth) */
        raster = RwRasterCreate(RwImageGetWidth(outImage),
                                RwImageGetHeight(outImage),
                                0, rwRASTERTYPETEXTURE);
        if (!raster)
        {
            RwImageDestroy(outImage);

            RWRETURN((RwTexture *)NULL);
        }

        /* set the rasters image */
        RwRasterSetFromImage(raster, outImage);

        /* all has gone to plan so destroy original image */
        RwImageDestroy(outImage);

        /* create a texture */
        texture = RwTextureCreate(raster);
        if (!texture)
        {
            RwRasterDestroy(raster);

            RWRETURN((RwTexture *)NULL);
        }

        /* setup the filtering */
        RwTextureSetFilterMode(texture, rwFILTERLINEAR);

        /* set its name */
        {
            static char         _PT[] = "PT";
            char               *texName;

            texName = NameGen_CreateName(_PT);
            if (texName)
            {
                RwTextureSetName(texture, texName);
                RwFree(texName);
            }
            else
            {
                RwTextureSetName(texture, RWSTRING("PT_???"));
            }
            RwTextureSetMaskName(texture, RWSTRING(""));
        }

        /* put texture in the texture dictionary */
        RwTexDictionaryAddTexture(RwTexDictionaryGetCurrent(), texture);

        RWRETURN(texture);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
Appearance_PixelTexture(AbstractNode * an, RpMaterial * material)
{
    RWFUNCTION(RWSTRING("Appearance_PixelTexture"));
    RWASSERT(an);
    RWASSERT(material);

    if (an && material)
    {
        AbstractField      *af;

        if ((af = AbstractNode_GetAbstractField(an, "image")))
        {
            sfimage            *image;
            RwTexture          *texture;
            Field              *field;

            field = AbstractField_GetField(af);
            image = FieldSfimage_GetValue(field);
            texture = Appearance_SfImageToRwTexture(image);
            if (!texture)
            {
                RWRETURN(FALSE);
            }

            /* assign to material */
            RpMaterialSetTexture(material, texture);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static char        *
Appearance_GetTextureName(char *dstName, const char *srcName)
{
    RWFUNCTION(RWSTRING("Appearance_GetTextureName"));
    RWASSERT(dstName);
    RWASSERT(srcName);

    if (dstName && srcName)
    {
        RwInt32             strLen = 0;
        char               *start, *end;

        rwstrcpy(dstName, srcName);
        /* some use forward slash */
        if ((start = rwstrrchr(dstName, '/')))
        {
            start++;
        }
        /* while others use back slash */
        else if ((start = rwstrrchr(dstName, '\\')))
        {
            start++;
        }
        else
        {
            start = dstName;
        }

        if ((end = rwstrrchr(start, '.')))
        {
            *end = '\0';
        }
        else
        {
            end = start + rwstrlen(start);
        }
        strLen = rwstrlen(dstName);

        rwstrncpy(dstName, start, strLen + 1);
        dstName[strLen] = '\0';

        RWRETURN(dstName);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

static              RwBool
Appearance_ImageTexture(AbstractNode * an, RpMaterial * material)
{
    RWFUNCTION(RWSTRING("Appearance_ImageTexture"));
    RWASSERT(an);
    RWASSERT(material);

    if (an && material)
    {
        AbstractField      *af;

        if ((af = AbstractNode_GetAbstractField(an, "url")))
        {
            RwTexture          *texture;
            char                dstTextName[256], *srcTextName;
            sfstring           *sfs;
            Field              *field;

            field = AbstractField_GetField(af);
            sfs = FieldMfstring_GetValue(field, 0);
            if (!sfs)
            {
                RWRETURN(FALSE);
            }

            srcTextName = (char *) (*sfs);
            if (!Appearance_GetTextureName(dstTextName, srcTextName))
            {
                RWRETURN(FALSE);
            }

            texture = RwTextureRead(dstTextName, (RwChar *)NULL);
            if (texture)
            {
                RpMaterialSetTexture(material, texture);
            }
            else
            {
                /* couldn't read the texture, but we can still soldier on */
                RWERROR((E_RP_VRML_TEXREAD, srcTextName));
            }

            RWRETURN(TRUE);
        }
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
Appearance_Material(AbstractNode * an, RpMaterial * material)
{
    RWFUNCTION(RWSTRING("Appearance_Material"));
    RWASSERT(an);
    RWASSERT(material);

    if (an && material)
    {
        AbstractField      *af;

        /* vrml material properties:- */
        /* ambientIntensity */
        /* diffuseColor */
        /* emissiveColor */
        /* shininess */
        /* specularColor */
        /* transparancy */

        /* get diffuse color */
        if ((af = AbstractNode_GetAbstractField(an, "diffuseColor")))
        {
            RwRGBAReal          nColour;
            RwRGBA              cColour;
            sfcolor            *sfc;
            Field              *field;

            field = AbstractField_GetField(af);
            sfc = FieldSfcolor_GetValue(field);

            if (!sfc)
            {
                RWRETURN(FALSE);
            }

            /* Set materials parameters */
            nColour.red = (RwReal) (sfc->r);
            nColour.green = (RwReal) (sfc->g);
            nColour.blue = (RwReal) (sfc->b);
            nColour.alpha = (RwReal) (1);

            RwRGBAFromRwRGBAReal(&cColour, &nColour);

            RpMaterialSetColor(material, &cColour);
        }

        /* get transparancy */
        if ((af = AbstractNode_GetAbstractField(an, "transparency")))
        {
            Field              *field;
            RwRGBA              cColour;
            const RwRGBA       *source;
            RwReal              opacity;
            sffloat            *sff;

            field = AbstractField_GetField(af);
            sff = FieldSffloat_GetValue(field);

            if (!sff)
            {
                RWRETURN(FALSE);
            }

            opacity = (RwReal) (1 - *sff);

            source = RpMaterialGetColor(material);
            RwRGBAAssign(&cColour, source);

            /* use API function instead */
            cColour.alpha = (unsigned char) (RwInt32) (((opacity) *
                                                        ((RwReal)
                                                         (255.0))) +
                                                       (RwReal) (0.5 /
                                                                 256));
            RpMaterialSetColor(material, &cColour);
        }

        /* get emissive color */
        if ((af = AbstractNode_GetAbstractField(an, "emissiveColor")))
        {
            sfcolor            *sfc;
            Field              *field;

            field = AbstractField_GetField(af);
            sfc = FieldSfcolor_GetValue(field);

            if (!sfc)
            {
                RWRETURN(FALSE);
            }

            /* hack to try and retain the emissize color for proper pre-lighting */
            Material_SetCurrentEmissiveColor(sfc);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));

    RWRETURN(FALSE);
}

RpMaterial         *
Appearance_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Appearance_Convert"));
    RWASSERT(an);

    if (an)
    {
        RpMaterial         *material;
        AbstractNode       *anChild;

        material = RpMaterialCreate();
        if (!material)
        {
            RWRETURN((RpMaterial *)NULL);
        }

        if ((anChild = AbstractNode_GetChildNode(an, "material")))
        {
            if (!Appearance_Material(anChild, material))
            {
                RpMaterialDestroy(material);

                RWRETURN((RpMaterial *)NULL);
            }

            Material_SetMatNodeFlag(TRUE);
        }

        if ((anChild = AbstractNode_GetChildNode(an, "texture")))
        {
            RwChar             *childName =
                AbstractNode_GetBaseName(anChild);

            if (!childName)
            {
                RpMaterialDestroy(material);

                RWRETURN((RpMaterial *)NULL);
            }

            if (rwstrcmp(childName, "ImageTexture") == 0)
            {
                if (!Appearance_ImageTexture(anChild, material))
                {
                    RpMaterialDestroy(material);

                    RWRETURN((RpMaterial *)NULL);
                }
            }
            else if (rwstrcmp(childName, "PixelTexture") == 0)
            {
                if (!Appearance_PixelTexture(anChild, material))
                {
                    RpMaterialDestroy(material);

                    RWRETURN((RpMaterial *)NULL);
                }
            }
        }

        if ((anChild =
             AbstractNode_GetChildNode(an, "textureTransform")))
        {
            if (!TextureTransform_Convert(anChild))
            {
                RpMaterialDestroy(material);

                RWRETURN((RpMaterial *)NULL);
            }
        }

        RWRETURN(material);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpMaterial *)NULL);
}
