public static Clip ReadClip(ChunkReader reader)
        {
            // index
            var index			= reader.ReadUInt32();

            // first subchunk header
            var type			= reader.ReadID<ClipType>();
            var subchunk_size	= reader.ReadUInt16();
            subchunk_size += (ushort)(subchunk_size & 1);

            Clip clip = null;
            using (var subChunkReader = reader.GetSubChunk(subchunk_size))
            {
                switch (type)
                {
                    case ClipType.ID_STIL:
                    {
                        var tmp_clip	= new ClipStill(index);
                        tmp_clip.Name = subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_ISEQ:
                    {
                        var tmp_clip	= new ClipSequence(index);
                        tmp_clip.Digits = subChunkReader.ReadUInt8();
                        tmp_clip.Flags	= subChunkReader.ReadUInt8();
                        tmp_clip.Offset = subChunkReader.ReadSInt16();
                        subChunkReader.ReadUInt16();  // Legacy Cruft: Nothing to see here
                        tmp_clip.Start	= subChunkReader.ReadSInt16();
                        tmp_clip.End	= subChunkReader.ReadSInt16();
                        tmp_clip.Prefix = subChunkReader.ReadString();
                        tmp_clip.Suffix = subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_ANIM:
                    {
                        var tmp_clip	= new ClipAnim(index);
                        tmp_clip.Name	= subChunkReader.ReadString();
                        tmp_clip.Server = subChunkReader.ReadString();
                        tmp_clip.Data	= subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_XREF:
                    {
                        var tmp_clip			= new ClipCloned(index);
                        tmp_clip.clip_reference_index	= subChunkReader.ReadUInt32();
                        tmp_clip.Name			= subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_STCC:
                    {
                        var tmp_clip	= new ClipColorCycle(index);
                        tmp_clip.lo		= subChunkReader.ReadSInt16();
                        tmp_clip.hi		= subChunkReader.ReadSInt16();
                        tmp_clip.Name	= subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    default:
                        throw new Exception("Unknown Clip type"); // TODO: create proper exception class for this ...
                }
            }

            while (reader.BytesLeft > 0)
            {
                // process subchunks as they're encountered
                var id = reader.ReadID<ClipDataType>();
                subchunk_size = reader.ReadUInt16();
                subchunk_size += (ushort)(subchunk_size & 1);

                using (var subChunkReader = reader.GetSubChunk(subchunk_size))
                {
                    switch (id)
                    {
                        //case ClipDataType.ID_CLRS: // Color Space RGB   - CLRS { flags[U2], colorspace[U2], filename[FNAM0] }
                        //case ClipDataType.ID_CLRA: // Color Space Alpha - CLRA { flags[U2], colorspace[U2], filename[FNAM0] }
                        //case ClipDataType.ID_FILT: // Image Filtering   - FILT { flags[U2] }
                        //case ClipDataType.ID_DITH: // Image Dithering	  - DITH { flags[U2] }

                        // Contrast - CONT { contrast-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_CONT: { clip.Contrast.value = subChunkReader.ReadSingle(); clip.Contrast.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Brightness - BRIT { brightness-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_BRIT: { clip.Brightness.value = subChunkReader.ReadSingle(); clip.Brightness.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Saturation - SATR { saturation-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_SATR: { clip.Saturation.value = subChunkReader.ReadSingle(); clip.Saturation.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Hue - HUE { hue-rotation[FP4], envelope[VX] }
                        case ClipDataType.ID_HUE: { clip.Hue.value = subChunkReader.ReadSingle(); clip.Hue.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Gamma Correction - GAMM { gamma[F4], envelope[VX] }
                        case ClipDataType.ID_GAMM: { clip.Gamma.value = subChunkReader.ReadSingle(); clip.Gamma.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Negative - NEGA { enable[U2] }
                        case ClipDataType.ID_NEGA: { clip.Negative = subChunkReader.ReadUInt16() != 0; break; }

                        // Time - TIME { start-time[FP4], duration[FP4], frame-rate[FP4] }
                        case ClipDataType.ID_TIME:
                        {
                            clip.StartTime			= subChunkReader.ReadSingle();
                            clip.Duration			= subChunkReader.ReadSingle();
                            clip.FrameRate			= subChunkReader.ReadSingle();
                            break;
                        }

                        // Plug-in Image Filters - IFLT { server-name[S0], flags[U2], data[...] }
                        case ClipDataType.ID_IFLT:

                        // Plug-in Pixel Filters - PFLT { server-name[S0], flags[U2], data[...] }
                        case ClipDataType.ID_PFLT:
                        {
                            var filt = new LightwavePlugin();
                            filt.name	= subChunkReader.ReadString();
                            filt.flags	= subChunkReader.ReadUInt16();
                            filt.data	= subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);

                            if (id == ClipDataType.ID_IFLT)
                                clip.ImageFilters.Add(filt);
                            else
                                clip.PixelFilters.Add(filt);
                            break;
                        }

                        case ClipDataType.ID_FLAG: // not mentioned in documentation ...
                            var flags = subChunkReader.ReadUInt16(); // unknown what they mean ...
                            break;

                        default:
                            Console.WriteLine("Unknown clip type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return clip;
        }
        public static Envelope ReadEnvelope(ChunkReader reader)
        {
            var env = new Envelope(); // allocate the Envelope structure
            env.Index = reader.ReadVariableLengthIndex(); // index

            EnvelopeKey lastKey = null;
            while (reader.BytesLeft > 0)
            {
                // process subchunks as they're encountered
                var id = reader.ReadID<EnvelopeType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case EnvelopeType.ID_TYPE:
                        {
                            env.Type = subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_NAME:
                        {
                            env.Name = subChunkReader.ReadString();
                            break;
                        }

                        case EnvelopeType.ID_PRE:
                        {
                            env.PreBehavior = (EvalType)subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_POST:
                        {
                            env.PostBehavior = (EvalType)subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_KEY:
                        {
                            lastKey = new EnvelopeKey(
                                                subChunkReader.ReadSingle(),	// time
                                                subChunkReader.ReadSingle()		// value
                                                );
                            env.Keys.Add(lastKey);

                            //TODO: not sort all the time
                            env.Keys.Sort((Comparison<EnvelopeKey>)delegate(EnvelopeKey k1, EnvelopeKey k2)
                            {
                                return k1.Time > k2.Time ? 1 : k1.Time < k2.Time ? -1 : 0;
                            });
                            break;
                        }

                        case EnvelopeType.ID_SPAN:
                        {
                            if (lastKey == null) // We should've encountered an ID_KEY before an ID_SPAN
                                throw new Exception("Key not defined"); //TODO: make proper exception class

                            lastKey.Shape = subChunkReader.ReadID<KeyShape>();
                            switch (lastKey.Shape)
                            {
                                case KeyShape.ID_TCB:
                                {
                                    lastKey.Tension		= subChunkReader.ReadSingle();
                                    lastKey.Continuity	= subChunkReader.ReadSingle();
                                    lastKey.Bias		= subChunkReader.ReadSingle();
                                    break;
                                }

                                case KeyShape.ID_BEZI:
                                case KeyShape.ID_HERM:
                                case KeyShape.ID_BEZ2:
                                {
                                    Array.Clear(lastKey.param, 0, lastKey.param.Length);
                                    int i = 0;
                                    while (i < 4 && subChunkReader.BytesLeft > 0)
                                    {
                                        lastKey.param[i] = subChunkReader.ReadSingle();
                                        i++;
                                    }
                                    break;
                                }

                                case KeyShape.ID_LINE:
                                    break;

                                default:
                                    Console.WriteLine("Unknown envelope span shape type " + reader.GetIDString((uint)lastKey.Shape));
                                    break;
                            }
                            break;
                        }

                        case EnvelopeType.ID_CHAN:
                        {
                            var plug = new LightwavePlugin();
                            plug.name	= subChunkReader.ReadString();
                            plug.flags	= subChunkReader.ReadUInt16();
                            plug.data	= subChunkReader.ReadBytes((uint)reader.BytesLeft);
                            env.ChannelFilters.Add(plug);
                            break;
                        }

                        default:
                            Console.WriteLine("Unknown envelope type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return env;
        }
        public static TextureMap ReadTextureMap(ChunkReader reader)
        {
            var tmap = new TextureMap();
            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<TextureCoordinates>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case TextureCoordinates.ID_SIZE:
                            tmap.Size.values[0] = subChunkReader.ReadSingle();
                            tmap.Size.values[1] = subChunkReader.ReadSingle();
                            tmap.Size.values[2] = subChunkReader.ReadSingle();
                            tmap.Size.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case TextureCoordinates.ID_CNTR:
                            tmap.Size.values[0] = subChunkReader.ReadSingle();
                            tmap.Size.values[1] = subChunkReader.ReadSingle();
                            tmap.Size.values[2] = subChunkReader.ReadSingle();
                            tmap.Center.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case TextureCoordinates.ID_ROTA:
                            tmap.Size.values[0] = subChunkReader.ReadSingle();
                            tmap.Size.values[1] = subChunkReader.ReadSingle();
                            tmap.Size.values[2] = subChunkReader.ReadSingle();
                            tmap.Rotate.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case TextureCoordinates.ID_FALL:
                            tmap.FallType = subChunkReader.ReadUInt16();
                            tmap.Size.values[0] = subChunkReader.ReadSingle();
                            tmap.Size.values[1] = subChunkReader.ReadSingle();
                            tmap.Size.values[2] = subChunkReader.ReadSingle();
                            tmap.FallOff.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case TextureCoordinates.ID_OREF:
                            tmap.ReferenceObject = subChunkReader.ReadString();
                            break;

                        case TextureCoordinates.ID_CSYS:
                            tmap.CoordinateSystem = subChunkReader.ReadUInt16();
                            break;

                        default:
                            Console.WriteLine("Unknown texture map type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return tmap;
        }
        public static VertexMap ReadVertexMap(ChunkReader reader, bool perpoly)
        {
            var type		= reader.ReadID<VertexMapType>();
            var dimensions	= reader.ReadUInt16();
            var name		= reader.ReadString();

            var vertex_map	= new VertexMap(type, name, perpoly);

            var vertex_indices	= new List<uint>();
            var polygon_indices	= new List<uint>();
            var values			= new List<float[]>();

            // fill in the vertex-map values
            while (reader.BytesLeft > 0)
            {
                vertex_indices.Add(reader.ReadVariableLengthIndex());
                if (perpoly)
                    polygon_indices.Add(reader.ReadVariableLengthIndex());

                var pointValues = new float[dimensions];
                for (var j = 0; j < dimensions; j++)
                    pointValues[j] = reader.ReadSingle();
                values.Add(pointValues);
            }
            vertex_map.Values = values.ToArray();
            vertex_map.vertex_index = vertex_indices.ToArray();
            if (perpoly)
                vertex_map.polygon_index = polygon_indices.ToArray();
            return vertex_map;
        }
        bool ReadImageMap(ChunkReader reader)
        {
            while (reader.BytesLeft > 0)
            {
                // get the next subchunk header
                var id = reader.ReadID<ImageMapType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case ImageMapType.ID_TMAP:
                        {
                            tmap = TextureMap.ReadTextureMap(subChunkReader);
                            break;
                        }

                        case ImageMapType.ID_PROJ: imap.projection		= (ProjectionType)subChunkReader.ReadUInt16(); break;
                        case ImageMapType.ID_VMAP: imap.vertex_map_name = subChunkReader.ReadString(); break;
                        case ImageMapType.ID_AXIS: imap.axis			= subChunkReader.ReadUInt16(); break;
                        case ImageMapType.ID_IMAG: imap.clip_index		= subChunkReader.ReadVariableLengthIndex(); break;
                        case ImageMapType.ID_PIXB: imap.pblend			= subChunkReader.ReadUInt16(); break;

                        case ImageMapType.ID_WRAP:
                            imap.wrapw_type = (WrapType)subChunkReader.ReadUInt16();
                            imap.wraph_type = (WrapType)subChunkReader.ReadUInt16();
                            break;

                        case ImageMapType.ID_WRPW:
                            imap.wrapw.value = subChunkReader.ReadSingle();
                            imap.wrapw.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case ImageMapType.ID_WRPH:
                            imap.wraph.value = subChunkReader.ReadSingle();
                            imap.wraph.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case ImageMapType.ID_AAST:
                            imap.aas_flags = subChunkReader.ReadUInt16();
                            imap.aa_strength = subChunkReader.ReadSingle();
                            break;

                        case ImageMapType.ID_STCK:
                            imap.stck.value = subChunkReader.ReadSingle();
                            imap.stck.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        case ImageMapType.ID_TAMP:
                            imap.amplitude.value = subChunkReader.ReadSingle();
                            imap.amplitude.envelope_index = subChunkReader.ReadVariableLengthIndex();
                            break;

                        default:
                            Console.WriteLine("Unknown image map type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return true;
        }
        bool ReadProcedural(ChunkReader reader)
        {
            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<ProceduralType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);
                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case ProceduralType.ID_TMAP:
                        {
                            tmap = TextureMap.ReadTextureMap(subChunkReader);
                            break;
                        }

                        case ProceduralType.ID_AXIS:
                        {
                            proc.axis = subChunkReader.ReadUInt16();
                            break;
                        }

                        case ProceduralType.ID_VALU:
                        {
                            proc.value_0 = subChunkReader.ReadSingle();
                            if (sz >= 8) proc.value_1 = subChunkReader.ReadSingle();
                            if (sz >= 12) proc.value_2 = subChunkReader.ReadSingle();
                            break;
                        }

                        case ProceduralType.ID_FUNC:
                        {
                            proc.name	= subChunkReader.ReadString();
                            proc.data	= subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);
                            break;
                        }

                        default:
                            Console.WriteLine("Unknown procedural map type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return true;
        }
        public static Texture ReadTexture(ChunkReader reader, TextureType type)
        {
            var tex			= new Texture(type);
            var header_size = reader.ReadUInt16();
            using (var headerReader = reader.GetSubChunk(header_size))
            {
                // ordinal string
                tex.ord = headerReader.ReadString();

                // process subchunks as they're encountered
                while (headerReader.BytesLeft > 0)
                {
                    var subchunk_id		= headerReader.ReadID<TextureDataType>();
                    var subchunk_size	= headerReader.ReadUInt16();
                    subchunk_size += (ushort)(subchunk_size & 1);
                    using (var subChunkReader = headerReader.GetSubChunk(subchunk_size))
                    {
                        switch (subchunk_id)
                        {
                            case TextureDataType.ID_CHAN:
                                tex.Channel = subChunkReader.ReadID<TextureChannel>();
                                break;

                            case TextureDataType.ID_OPAC:
                                tex.opac_type = subChunkReader.ReadUInt16();
                                tex.opacity.value = subChunkReader.ReadSingle();
                                tex.opacity.envelope_index = subChunkReader.ReadVariableLengthIndex();
                                break;

                            case TextureDataType.ID_ENAB:
                                tex.enabled = subChunkReader.ReadUInt16();
                                break;

                            case TextureDataType.ID_NEGA:
                                tex.negative = subChunkReader.ReadUInt16();
                                break;

                            case TextureDataType.ID_AXIS:
                                tex.axis = subChunkReader.ReadUInt16();
                                break;

                            default:
                                Console.WriteLine("Unknown texture header type " + reader.GetIDString((uint)subchunk_id));
                                break;
                        }
                    }
                }
            }

            if (reader.BytesLeft > 0)
            {
                using (var blockReader = reader.GetSubChunk((uint)reader.BytesLeft))
                {
                    switch (type)
                    {
                        case TextureType.ID_IMAP: tex.ReadImageMap(blockReader); break;
                        case TextureType.ID_PROC: tex.ReadProcedural(blockReader); break;
                        case TextureType.ID_GRAD: tex.ReadGradient(blockReader); break;
                        default:
                            Console.WriteLine("Unknown texture type " + reader.GetIDString((uint)type));
                            break;
                    }
                }
            }
            return tex;
        }
        bool ReadGradient(ChunkReader reader)
        {
            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<GradientType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case GradientType.ID_TMAP:
                        {
                            tmap = TextureMap.ReadTextureMap(subChunkReader);
                            break;
                        }

                        case GradientType.ID_PNAM:
                        {
                            grad.paramname = subChunkReader.ReadString();
                            break;
                        }

                        case GradientType.ID_INAM:
                        {
                            grad.itemname = subChunkReader.ReadString();
                            break;
                        }

                        case GradientType.ID_GRST:
                        {
                            grad.start = subChunkReader.ReadSingle();
                            break;
                        }

                        case GradientType.ID_GREN:
                        {
                            grad.end = subChunkReader.ReadSingle();
                            break;
                        }

                        case GradientType.ID_GRPT:
                        {
                            grad.repeat = subChunkReader.ReadUInt16();
                            break;
                        }

                        case GradientType.ID_FKEY:
                        {
                            var keys = new List<GradientKey>();
                            while (subChunkReader.BytesLeft > 0)
                            {
                                var key = new GradientKey();
                                key.value = subChunkReader.ReadSingle();
                                key.rgba_0 = subChunkReader.ReadSingle();
                                key.rgba_1 = subChunkReader.ReadSingle();
                                key.rgba_2 = subChunkReader.ReadSingle();
                                key.rgba_3 = subChunkReader.ReadSingle();
                                keys.Add(key);
                            }
                            grad.Keys = keys.ToArray();
                            break;
                        }

                        case GradientType.ID_IKEY:
                        {
                            var ikeys = new List<ushort>();
                            while (subChunkReader.BytesLeft > 0)
                                ikeys.Add(subChunkReader.ReadUInt16());
                            grad.ikey = ikeys.ToArray();
                            break;
                        }

                        case GradientType.ID_GVER:	// unknown, not mentioned in lightwave sdk documentation
                            break;

                        default:
                            Console.WriteLine("Unknown gradient type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }
            return true;
        }
        // LWOB
        public static Surface ReadSurface5(ChunkReader reader, LightwaveObject obj)
        {
            var surf = new Surface();
            surf.name = reader.ReadString();

            Texture tex = null;
            LightwavePlugin shdr = null;
            float[] v = new float[3];
            uint flags = 0;

            // process subchunks as they're encountered
            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<SurfaceParameter>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
             				{
                    switch (id)
                    {
                        case SurfaceParameter.ID_COLR:
                        {
                            surf.color.Red = subChunkReader.ReadUInt8() / 255.0f;
                            surf.color.Green = subChunkReader.ReadUInt8() / 255.0f;
                            surf.color.Blue = subChunkReader.ReadUInt8() / 255.0f;
                            break;
                        }

                        case SurfaceParameter.ID_FLAG:
                        {
                            flags = subChunkReader.ReadUInt16();
                            if ((flags & 4) != 0) surf.smooth = 1.56207f;
                            if ((flags & 8) != 0) surf.color_hilite.value = 1.0f;
                            if ((flags & 16) != 0) surf.color_filter.value = 1.0f;
                            if ((flags & 128) != 0) surf.dif_sharp.value = 0.5f;
                            if ((flags & 256) != 0) surf.sideflags = 3;
                            if ((flags & 512) != 0) surf.add_trans.value = 1.0f;
                            break;
                        }

                        case SurfaceParameter.ID_LUMI:
                        {
                            surf.luminosity.Value = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_VLUM:
                        {
                            surf.luminosity.Value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_DIFF:
                        {
                            surf.diffuse.Value = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_VDIF:
                        {
                            surf.diffuse.Value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_SPEC:
                        {
                            surf.specularity.Value = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_VSPC:
                        {
                            surf.specularity.Value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_GLOS:
                        {
                            surf.glossiness.Value = (float)Math.Log(subChunkReader.ReadUInt16()) / 20.7944f;
                            break;
                        }

                        case SurfaceParameter.ID_SMAN:
                        {
                            surf.smooth = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_REFL:
                        {
                            surf.reflection.values.Value = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_RFLT:
                        {
                            surf.reflection.options = subChunkReader.ReadUInt16();
                            break;
                        }

                        case SurfaceParameter.ID_RIMG:
                        {
                            var s = subChunkReader.ReadString();
                            surf.reflection.clip_index = AddClip(s, obj.Clips);
                            surf.reflection.options = 3;
                            break;
                        }

                        case SurfaceParameter.ID_RSAN:
                        {
                            surf.reflection.seam_angle = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TRAN:
                        {
                            surf.transparency.values.Value = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_RIND:
                        {
                            surf.eta.Value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_BTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.bump.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_CTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.color.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_DTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.diffuse.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_LTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.luminosity.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_RTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.reflection.values.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_STEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.specularity.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_TTEX:
                        {
                            var s = subChunkReader.ReadString(sz);//.getbytes((uint)sz);
                            tex = ParseTexture(s);
                            surf.transparency.values.textures.Add(tex);
                            break;
                        }

                        case SurfaceParameter.ID_TFLG:
                        {
                            if (tex == null)
                                return null;
                            flags = subChunkReader.ReadUInt16();

                            int i = 0;
                            if ((flags & 1) != 0) i = 0;
                            if ((flags & 2) != 0) i = 1;
                            if ((flags & 4) != 0) i = 2;
                            tex.axis = (ushort)i;
                            if (tex.Type == TextureType.ID_IMAP)
                                tex.imap.axis = i;
                            else
                                tex.proc.axis = i;

                            if ((flags & 8) != 0) tex.tmap.CoordinateSystem = 1;
                            if ((flags & 16) != 0) tex.negative = 1;
                            if ((flags & 32) != 0) tex.imap.pblend = 1;
                            if ((flags & 64) != 0)
                            {
                                tex.imap.aa_strength = 1.0f;
                                tex.imap.aas_flags = 1;
                            }
                            break;
                        }

                        case SurfaceParameter.ID_TSIZ:
                        {
                            tex.tmap.Size.values[0] = subChunkReader.ReadSingle();
                            tex.tmap.Size.values[1] = subChunkReader.ReadSingle();
                            tex.tmap.Size.values[2] = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TCTR:
                        {
                            tex.tmap.Center.values[0] = subChunkReader.ReadSingle();
                            tex.tmap.Center.values[1] = subChunkReader.ReadSingle();
                            tex.tmap.Center.values[2] = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TFAL:
                        {
                            tex.tmap.FallOff.values[0] = subChunkReader.ReadSingle();
                            tex.tmap.FallOff.values[1] = subChunkReader.ReadSingle();
                            tex.tmap.FallOff.values[2] = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TVEL:
                        {
                            for (var i = 0; i < 3; i++)
                                v[i] = subChunkReader.ReadSingle();
                            tex.tmap.Center.envelope_index = AddTextureVelocity(tex.tmap.Center.values, v, obj.Envelopes);
                            break;
                        }

                        case SurfaceParameter.ID_TCLR:
                        {
                            if (tex.Type == TextureType.ID_PROC)
                            {
                                tex.proc.value_0 = subChunkReader.ReadUInt8() / 255.0f;
                                tex.proc.value_1 = subChunkReader.ReadUInt8() / 255.0f;
                                tex.proc.value_2 = subChunkReader.ReadUInt8() / 255.0f;
                            }
                            break;
                        }

                        case SurfaceParameter.ID_TVAL:
                        {
                            tex.proc.value_0 = subChunkReader.ReadSInt16() / 256.0f;
                            break;
                        }

                        case SurfaceParameter.ID_TAMP:
                        {
                            if (tex.Type == TextureType.ID_IMAP)
                                tex.imap.amplitude.value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TIMG:
                        {
                            var s = subChunkReader.ReadString();
                            tex.imap.clip_index = AddClip(s, obj.Clips);
                            break;
                        }

                        case SurfaceParameter.ID_TAAS:
                        {
                            tex.imap.aa_strength = subChunkReader.ReadSingle();
                            tex.imap.aas_flags = 1;
                            break;
                        }

                        case SurfaceParameter.ID_TREF:
                        {
                            tex.tmap.ReferenceObject = subChunkReader.ReadString((uint)sz);//.getbytes((uint)sz);
                            break;
                        }

                        case SurfaceParameter.ID_TOPC:
                        {
                            tex.opacity.value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TFP0:
                        {
                            if (tex.Type == TextureType.ID_IMAP)
                                tex.imap.wrapw.value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_TFP1:
                        {
                            if (tex.Type == TextureType.ID_IMAP)
                                tex.imap.wraph.value = subChunkReader.ReadSingle();
                            break;
                        }

                        case SurfaceParameter.ID_SHDR:
                        {
                            shdr = new LightwavePlugin();
                            if (shdr == null)
                                return null;
                            shdr.name = subChunkReader.ReadString((uint)sz);
                            surf.shader.Add(shdr);
                            break;
                        }

                        case SurfaceParameter.ID_SDAT:
                        {
                            if (shdr == null)
                                return null;
                            shdr.data = subChunkReader.ReadBytes(sz);
                            break;
                        }

                        default:
                            Console.WriteLine("Unknown surface parameter type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }
            return surf;
        }
示例#10
0
        public static Surface ReadSurface(ChunkReader reader)
        {
            // allocate the Surface structure
            var surf = new Surface();

            // names
            surf.name		= reader.ReadString();
            surf.srcname	= reader.ReadString();

            // process subchunks as they're encountered
            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<SurfaceParameter>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        // Vertex Color Map - VCOL { intensity[FP4], envelope[VX], vmap-type[ID4], name[S0] }
                        //		The vertex color map subchunk identifies an RGB or RGBA VMAP that will be used to color the surface
                        case SurfaceParameter.ID_VCOL:
                        {
                            var intensity		= subChunkReader.ReadSingle();
                            var envelope_index	= subChunkReader.ReadVariableLengthIndex();
                            var vmap_type		= subChunkReader.ReadUInt32();
                            var name			= subChunkReader.ReadString();
                            break;
                        }

                        case SurfaceParameter.ID_CMNT: // Not mentioned in LWO documentation, maybe means 'comment'?
                        {
                            break;
                        }

                        case SurfaceParameter.ID_VERS: // Not mentioned in LWO documentation, maybe means 'version'?
                        {
                            break;
                        }

                        case SurfaceParameter.ID_NODS: // Not mentioned in LWO documentation, maybe means 'nodes'?
                        {
                            break;
                        }

                        case SurfaceParameter.ID_COLR:
                        {
                            surf.color.Red							= subChunkReader.ReadSingle();
                            surf.color.Green						= subChunkReader.ReadSingle();
                            surf.color.Blue							= subChunkReader.ReadSingle();
                            surf.color.envelope_index				= subChunkReader.ReadVariableLengthIndex();
                            break;
                        }

                        case SurfaceParameter.ID_LUMI:
                            surf.luminosity.Value					= subChunkReader.ReadSingle();
                            surf.luminosity.envelope_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_DIFF:
                            surf.diffuse.Value						= subChunkReader.ReadSingle();
                            surf.diffuse.envelope_index				= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_SPEC:
                            surf.specularity.Value					= subChunkReader.ReadSingle();
                            surf.specularity.envelope_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_GLOS:
                            surf.glossiness.Value					= subChunkReader.ReadSingle();
                            surf.glossiness.envelope_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_REFL:
                            surf.reflection.values.Value			= subChunkReader.ReadSingle();
                            surf.reflection.values.envelope_index	= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_RFOP:
                            surf.reflection.options					= subChunkReader.ReadUInt16();
                            break;

                        case SurfaceParameter.ID_RIMG:
                            surf.reflection.clip_index				= subChunkReader.ReadVariableLengthIndex();
                            break;

                        // Reflection Blurring - RBLR { blur-percentage[FP4], envelope[VX] }
                        //		The amount of blurring of reflections. The default is zero.
                        case SurfaceParameter.ID_RBLR:
                            break;

                        case SurfaceParameter.ID_RSAN:
                            surf.reflection.seam_angle				= subChunkReader.ReadSingle();
                            break;

                        case SurfaceParameter.ID_TRAN:
                            surf.transparency.values.Value			= subChunkReader.ReadSingle();
                            surf.transparency.values.envelope_index	= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_TROP:
                            surf.transparency.options				= subChunkReader.ReadUInt16();
                            break;

                        case SurfaceParameter.ID_TIMG:
                            surf.transparency.clip_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_RIND:
                            surf.eta.Value							= subChunkReader.ReadSingle();
                            surf.eta.envelope_index					= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_TRNL:
                            surf.translucency.Value					= subChunkReader.ReadSingle();
                            surf.translucency.envelope_index		= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_BUMP:
                            surf.bump.Value							= subChunkReader.ReadSingle();
                            surf.bump.envelope_index				= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_SMAN:
                            surf.smooth								= subChunkReader.ReadSingle();
                            break;

                        case SurfaceParameter.ID_SIDE:
                            surf.sideflags							= subChunkReader.ReadUInt16();
                            break;

                        case SurfaceParameter.ID_CLRH:
                            surf.color_hilite.value					= subChunkReader.ReadSingle();
                            surf.color_hilite.envelope_index		= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_CLRF:
                            surf.color_filter.value					= subChunkReader.ReadSingle();
                            surf.color_filter.envelope_index		= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_ADTR:
                            surf.add_trans.value					= subChunkReader.ReadSingle();
                            surf.add_trans.envelope_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_SHRP:
                            surf.dif_sharp.value					= subChunkReader.ReadSingle();
                            surf.dif_sharp.envelope_index			= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_GVAL:
                            surf.glow.value							= subChunkReader.ReadSingle();
                            surf.glow.envelope_index				= subChunkReader.ReadVariableLengthIndex();
                            break;

                        // Render Outlines - LINE { flags[U2], ( size[F4], size-envelope[VX], ( color[COL12], color-envelope[VX] )? )? }
                        case SurfaceParameter.ID_LINE:
                            surf.line.enabled = 1;
                            if (sz >= 2) surf.line.flags				= subChunkReader.ReadUInt16();
                            if (sz >= 6) surf.line.size.value			= subChunkReader.ReadSingle();
                            if (sz >= 8) surf.line.size.envelope_index	= subChunkReader.ReadVariableLengthIndex();
                            break;

                        case SurfaceParameter.ID_ALPH:
                            surf.alpha_mode = subChunkReader.ReadUInt16();
                            surf.alpha		= subChunkReader.ReadSingle();
                            break;

                        case SurfaceParameter.ID_AVAL:
                            surf.alpha		= subChunkReader.ReadSingle();
                            break;

                        case SurfaceParameter.ID_BLOK:
                        {
                            var type = subChunkReader.ReadID<TextureType>();
                            switch (type)
                            {
                                case TextureType.ID_IMAP:
                                case TextureType.ID_PROC:
                                case TextureType.ID_GRAD:
                                {
                                    using (var blockReader = subChunkReader.GetSubChunk((uint)subChunkReader.BytesLeft))
                                    {
                                        var tex = Texture.ReadTexture(blockReader, type);
                                        switch ( tex.Channel )
                                        {
                                            case TextureChannel.ID_COLR: surf.color					.textures.Add(tex); break;
                                            case TextureChannel.ID_LUMI: surf.luminosity			.textures.Add(tex); break;
                                            case TextureChannel.ID_DIFF: surf.diffuse				.textures.Add(tex); break;
                                            case TextureChannel.ID_SPEC: surf.specularity			.textures.Add(tex); break;
                                            case TextureChannel.ID_GLOS: surf.glossiness			.textures.Add(tex); break;
                                            case TextureChannel.ID_REFL: surf.reflection   .values	.textures.Add(tex); break;
                                            case TextureChannel.ID_TRAN: surf.transparency .values	.textures.Add(tex); break;
                                            case TextureChannel.ID_RIND: surf.eta					.textures.Add(tex); break;
                                            case TextureChannel.ID_TRNL: surf.translucency			.textures.Add(tex); break;
                                            case TextureChannel.ID_BUMP: surf.bump					.textures.Add(tex); break;
                                            default:
                                                throw new Exception("Unknown texture channel"); // TODO: create proper exception calass for this
                                        }
                                    }
                                    break;
                                }
                                case TextureType.ID_SHDR:
                                {
                                    using (var blockReader = subChunkReader.GetSubChunk((uint)subChunkReader.BytesLeft))
                                    {
                                        surf.shader.Add(
                                                LightwavePlugin.ReadShader(blockReader)
                                            );
                                    }
                                    break;
                                }
                                default:
                                    Console.WriteLine("Unknown blok type " + reader.GetIDString((uint)type));
                                    break;
                            }
                            break;
                        }

                        default:
                            Console.WriteLine("Unknown surface parameter type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return surf;
        }
示例#11
0
        public static LightwavePlugin ReadShader(ChunkReader reader)
        {
            var shader = new LightwavePlugin();

            var hsz		= reader.ReadUInt16(); // Q: example code does this, but can't find this in documentation?
            shader.ord	= reader.ReadString();

            using (var headerReader = reader.GetSubChunk(hsz))
            {
                while (headerReader.BytesLeft > 0)
                {
                    var id = headerReader.ReadID<PluginType>();
                    var sz = headerReader.ReadUInt16();
                    sz += (ushort)(sz & 1);
                    hsz -= sz;
                    if (id == PluginType.ID_ENAB)
                    {
                        shader.flags = headerReader.ReadUInt16();
                        break;
                    }
                }
            }

            while (reader.BytesLeft > 0)
            {
                var id = reader.ReadID<PluginType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case PluginType.ID_FUNC:
                        {
                            shader.name = subChunkReader.ReadString();
                            shader.data = subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);
                            break;
                        }

                        default:
                            //Console.WriteLine("Unknown shader type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return shader;
        }
示例#12
0
        // LWOB
        static IEnumerable<Polygon> ReadPolygons5(ChunkReader reader, uint vertex_offset)
        {
            // fill in the new polygons
            while (reader.BytesLeft > 0)
            {
                var vertex_count = reader.ReadUInt16();
                var newPolygon = new Polygon(PolygonType.ID_FACE);
                for (var j = 0; j < vertex_count; j++)
                    newPolygon.Vertices.Add(
                            new PolygonVertex(reader.ReadUInt16() + vertex_offset)
                        );

                var index = (int)reader.ReadSInt16();
                if (index < 0)
                {
                    index = (int)-index;
                    reader.ReadSInt16(); // skip 2 bytes - Q: why??
                }
                newPolygon.surf_index = (uint)(index - 1);
                yield return newPolygon;
            }
        }
示例#13
0
        static IEnumerable<Polygon> ReadPolygons(ChunkReader reader, uint vertex_offset)
        {
            var type = reader.ReadID<PolygonType>();

            // fill in the new polygons
            while (reader.BytesLeft > 0)
            {
                var vertex_count = reader.ReadUInt16();
                var flags = vertex_count & 0xFC00;
                vertex_count &= 0x03FF;

                var newPolygon = new Polygon(type, flags);
                for (var j = 0; j < vertex_count; j++)
                    newPolygon.Vertices.Add(
                            new PolygonVertex(reader.ReadVariableLengthIndex() + vertex_offset)
                        );

                yield return newPolygon;
            }
        }